From 2358bbe5fa354429a32cde519f2f6f7d5773dac9 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Wed, 13 Aug 2025 15:35:25 -0230 Subject: [PATCH 01/16] feat: Migrate controller guidelines and examples to new Messenger Migrate the controller guidelines and the `sample-controllers` package to the `next` export of the `@metamask/base-controller` package and the new `Messenger` class from `@metamask/messenger`. --- docs/controller-guidelines.md | 306 ++++++++---------- docs/data-services.md | 2 +- packages/sample-controllers/CHANGELOG.md | 1 + packages/sample-controllers/package.json | 1 + .../src/sample-gas-prices-controller.test.ts | 32 +- .../src/sample-gas-prices-controller.ts | 57 ++-- .../sample-gas-prices-service.test.ts | 26 +- .../sample-gas-prices-service.ts | 31 +- .../src/sample-petnames-controller.test.ts | 29 +- .../src/sample-petnames-controller.ts | 39 +-- .../sample-controllers/tsconfig.build.json | 1 + packages/sample-controllers/tsconfig.json | 1 + yarn.lock | 25 +- 13 files changed, 284 insertions(+), 267 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index fdf1a32cac6..60e99f89ab6 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -231,12 +231,10 @@ If the recipient controller supports the messaging system, however, the callback const name = 'FooController'; -type FooControllerMessenger = RestrictedMessenger< +type FooControllerMessenger = Messenger< typeof name, - never, - never, - never, - never + FooControllerActions, + FooControllerEvents >; class FooController extends BaseController< @@ -255,15 +253,33 @@ class FooController extends BaseController< // === Client repo === -const rootMessenger = new Messenger<'BarController:stateChange', never>(); -const barControllerMessenger = rootMessenger.getRestricted({ - name: 'BarController', +const rootMessenger = new Messenger<'Root', RootActions, RootEvents>({ + namespace: 'Root', +}); +const barControllerMessenger = new Messenger< + 'BarController', + BarControllerActions, + BarControllerEvents, + typeof rootMessenger +>({ + namespace: 'BarController', + parent: rootMessenger, }); const barController = new BarController({ messenger: barControllerMessenger, }); -const fooControllerMessenger = rootMessenger.getRestricted({ - name: 'FooController', +const fooControllerMessenger = new Messenger< + 'FooController', + FooControllerActions, + FooControllerEvents | BarControllerStateChange, + typeof rootMessenger +>({ + namespace: 'FooController', + parent: rootMessenger, +}); +rootMessenger.delegate({ + events: ['BarController:stateChange' as const], + messenger: fooControllerMessenger, }); const fooController = new FooController({ messenger: fooControllerMessenger, @@ -312,12 +328,10 @@ However, this pattern can be replaced with the use of the messenger: const name = 'FooController'; -type FooControllerMessenger = RestrictedMessenger< +type FooControllerMessenger = Messenger< typeof name, - never, - never, - never, - never + FooControllerActions, + FooControllerEvents >; class FooController extends BaseController< @@ -336,9 +350,17 @@ class FooController extends BaseController< // === Client repo === -const rootMessenger = new Messenger<'FooController:someEvent', never>(); -const fooControllerMessenger = rootMessenger.getRestricted({ - name: 'FooController', +const rootMessenger = new Messenger<'Root', RootActions, RootEvents>({ + namespace: 'Root', +}); +const fooControllerMessenger = new Messenger< + 'FooController', + FooControllerActions, + FooControllerEvents, + typeof rootMessenger +>({ + namespace: 'FooController', + parent: rootMessenger, }); const fooController = new FooController({ messenger: fooControllerMessenger, @@ -507,37 +529,19 @@ A controller should define and export a type union that holds all of its actions The name of this type should be `${ControllerName}Actions`. -This type should be only passed to `RestrictedMessenger` as the 2nd type parameter. It should _not_ be included in its 4th type parameter, as that is is used for external actions. - -🚫 **`FooController['type']` is passed as the 4th type parameter** - -```typescript -export type FooControllerActions = - | FooControllerUpdateCurrencyAction - | FooControllerUpdateRatesAction; - -export type FooControllerMessenger = RestrictedMessenger< - 'FooController', - FooControllerActions, - never, - FooControllerActions['type'], - never ->; -``` +This type should be passed to `Messenger` as the 2nd type parameter. It should _not_ include external actions. -✅ **`never` is passed as the 4th type parameter (assuming no external actions)** +✅ **`FooControllerActions` is passed as the 2nd type parameter (assuming no external actions)** ```typescript export type FooControllerActions = | FooControllerUpdateCurrencyAction | FooControllerUpdateRatesAction; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', FooControllerActions, - never, - never, - never + FooControllerEvents >; ``` @@ -547,189 +551,178 @@ A controller should define and export a type union that holds all of its events. The name of this type should be `${ControllerName}Events`. -This type should be only passed to `RestrictedMessenger` as the 3rd type parameter. It should _not_ be included in its 5th type parameter, as that is is used for external events. +This type should be passed to `Messenger` as the 3rd type parameter. It should _not_ include external events. -🚫 **`FooControllerEvents['type']` is passed as the 5th type parameter** +✅ **`FooControllerEvents` is passed as the 2nd type parameter (assuming no external events)** ```typescript export type FooControllerEvents = | FooControllerMessageReceivedEvent | FooControllerNotificationAddedEvent; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - never, - FooControllerEvents, - never, - FooControllerEvents['type'] ->; -``` - -✅ **`never` is passed as the 5th type parameter (assuming no external events)** - -```typescript -export type FooControllerEvents = - | FooControllerMessageReceivedEvent - | FooControllerNotificationAddedEvent; - -export type FooControllerMessenger = RestrictedMessenger< - 'FooController', - never, - FooControllerEvents, - never, - never + FooControllerActions, + FooControllerEvents >; ``` ## Define, but do not export, a type union for external action types -A controller may wish to call actions defined by other controllers, and therefore will need to define them in the messenger's allowlist. +A controller may wish to call actions defined by other controllers, and therefore will need to include them in the controller messenger's type definition. -In this case, the controller should group these types into a type union so that they can be easily passed to the `RestrictedMessenger` type. However, it should not export this type, as it would then be re-exporting types that another package has already exported. +In this case, the controller should group these types into a type union so that they can be easily passed to the `Messenger` type. However, it should not export this type, as it would then be re-exporting types that another package has already exported. The name of this type should be `AllowedActions`. -This type should not only be passed to `RestrictedMessenger` as the 2nd type parameter, but should also be included in its 4th type parameter. +This type should be passed to `Messenger` as part of the 2nd type parameter, in a type union with internal actions. -🚫 **`never` is passed as the 4th type parameter** +🚫 **`AllowedActions` is exported** ```typescript +/* === packages/foo-controller/src/FooController.ts === */ + +export type FooControllerActions = + | FooControllerUpdateCurrencyAction + | FooControllerUpdateRatesAction; + export type AllowedActions = | BarControllerDoSomethingAction | BarControllerDoSomethingElseAction; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - AllowedActions, - never, - never, - never + FooControllerActions | AllowedActions, + FooControllerEvents >; + +/* === packages/foo-controller/src/index.ts === */ + +export type { AllowedActions } from '@metamask/foo-controller'; ``` -🚫 **`AllowedActions['type']` is passed as the 4th type parameter, but `AllowedActions` is exported** +🚫 **External actions are included in controller action type** ```typescript -/* === packages/foo-controller/src/FooController.ts === */ - -export type AllowedActions = +export type FooControllerActions = + | FooControllerUpdateCurrencyAction + | FooControllerUpdateRatesAction | BarControllerDoSomethingAction | BarControllerDoSomethingElseAction; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - AllowedActions, - never, - AllowedActions['type'], - never + FooControllerActions, + FooControllerEvents >; - -/* === packages/foo-controller/src/index.ts === */ - -export type { AllowedActions } from '@metamask/foo-controller'; ``` -✅ **`AllowedActions['type']` is passed as the 4th type parameter, and `AllowedActions` is _not_ exported** +✅ **`AllowedActions` is included in the 2nd type parameter but is _not_ exported** ```typescript +export type FooControllerActions = + | FooControllerUpdateCurrencyAction + | FooControllerUpdateRatesAction; + type AllowedActions = | BarControllerDoSomethingAction | BarControllerDoSomethingElseAction; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - AllowedActions, - never, - AllowedActions['type'], - never + FooControllerActions | AllowedActions, + FooControllerEvents >; ``` -If, in a test, you need to access all of the actions included in a controller's messenger allowlist, use the [`ExtractAvailableAction` utility type](../packages/base-controller/tests/helpers.ts): +If, in a test, you need to access all of the actions supported by a messenger, use the [`MessengerActions` utility type](../packages/messenger/src/Messenger.ts): ```typescript -// NOTE: You may need to adjust the path depending on where you are -import { ExtractAvailableAction } from '../../base-controller/tests/helpers'; +import type { MessengerActions, MessengerEvents } from '@metamask/messenger'; const messenger = new Messenger< - ExtractAvailableAction, - never + controllerName, + MessengerActions, + MessengerEvents >(); ``` ## Define, but do not export, a type union for external event types -A controller may wish to subscribe to events defined by other controllers, and therefore will need to define them in the messenger's allowlist. +A controller may wish to subscribe to events defined by other controllers, and therefore will need to include them in the controller messenger's type definition. -In this case, the controller should group these types into a type union so that they can be easily passed to the `RestrictedMessenger` type. However, it should not export this type, as it would then be re-exporting types that another package has already exported. +In this case, the controller should group these types into a type union so that they can be easily passed to the `Messenger` type. However, it should not export this type, as it would then be re-exporting types that another package has already exported. The name of this type should be `AllowedEvents`. -This type should not only be passed to `RestrictedMessenger` as the 3rd type parameter, but should also be included in its 5th type parameter. +This type should be passed to `Messenger` as part of the 3rd type parameter, in a type union with internal events. -🚫 **`never` is passed as the 5th type parameter** +🚫 **`AllowedEvents` is exported** ```typescript +/* === packages/foo-controller/src/FooController.ts === */ +export type FooControllerEvents = + | FooControllerMessageReceivedEvent + | FooControllerNotificationAddedEvent; + export type AllowedEvents = | BarControllerSomethingHappenedEvent | BarControllerSomethingElseHappenedEvent; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - never, - AllowedEvents, - never, - never + FooControllerActions, + FooControllerEvents | AllowedEvents >; + +/* === packages/foo-controller/src/index.ts === */ + +export type { AllowedEvents } from '@metamask/foo-controller'; ``` -🚫 **`AllowedEvents['type']` is passed as the 5th type parameter, but `AllowedEvents` is exported** +🚫 **External events are included in controller actieventn type** ```typescript -/* === packages/foo-controller/src/FooController.ts === */ - -export type AllowedEvents = +export type FooControllerEvents = + | FooControllerMessageReceivedEvent + | FooControllerNotificationAddedEvent | BarControllerSomethingHappenedEvent | BarControllerSomethingElseHappenedEvent; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - never, - AllowedEvents, - never, - AllowedEvents['type'] + FooControllerActions, + FooControllerEvents >; - -/* === packages/foo-controller/src/index.ts === */ - -export type { AllowedEvents } from '@metamask/foo-controller'; ``` -✅ **`AllowedEvents['type']` is passed as the 5th type parameter, and `AllowedEvents` is _not_ exported** +✅ **`AllowedEvents` is included in the 3nd type parameter but is _not_ exported** ```typescript +export type FooControllerEvents = + | FooControllerMessageReceivedEvent + | FooControllerNotificationAddedEvent; + type AllowedEvents = | BarControllerSomethingHappenedEvent | BarControllerSomethingElseHappenedEvent; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - never, - AllowedEvents, - never, - AllowedEvents['type'] + FooControllerActions, + FooControllerEvents | AllowedEvents >; ``` -If, in a test, you need to access all of the events included in a controller's messenger allowlist, use the [`ExtractAvailableEvent` utility type](../packages/base-controller/tests/helpers.ts): +If, in a test, you need to access all of the events supported by a messenger, use the [`MessengerEvents` utility type](../packages/messenger/src/Messenger.ts): ```typescript -// NOTE: You may need to adjust the path depending on where you are -import { ExtractAvailableEvent } from '../../base-controller/tests/helpers'; +import type { MessengerActions, MessengerEvents } from '@metamask/messenger'; const messenger = new Messenger< - never, - ExtractAvailableEvent + controllerName, + MessengerActions, + MessengerEvents >(); ``` @@ -792,25 +785,17 @@ export type AllowedEvents = | ApprovalControllerApprovalRequestApprovedEvent | ApprovalControllerApprovalRequestRejectedEvent; -export type SwapsControllerMessenger = RestrictedMessenger< +export type SwapsControllerMessenger = Messenger< 'SwapsController', SwapsControllerActions | AllowedActions, - SwapsControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + SwapsControllerEvents | AllowedEvents >; ``` A messenger that allows no actions or events (whether internal or external) looks like this: ```typescript -export type SwapsControllerMessenger = RestrictedMessenger< - 'SwapsController', - never, - never, - never, - never ->; +export type FooServiceMessenger = Messenger<'FooService', never, never>; ``` ## Define and export a type for the controller's state @@ -1175,14 +1160,14 @@ class AccountsController extends BaseController< import { AccountsControllerGetStateAction } from '@metamask/accounts-controller'; +// Other type definitions + type AllowedActions = AccountsControllerGetStateAction; -type PreferencesControllerMessenger = RestrictedMessenger< +type PreferencesControllerMessenger = Messenger< 'PreferencesController', - AllowedActions, - never, - AllowedActions['type'], - never + PreferencesControllerActions | AllowedActions, + PreferencesControllerEvents >; class PreferencesController extends BaseController< @@ -1271,16 +1256,15 @@ export type AccountsControllerGetInactiveAccountsAction = { handler: AccountsController['getInactiveAccounts']; }; -type AccountsControllerActions = +export type AccountsControllerActions = + /// Other actions | AccountsControllerGetActiveAccountAction | AccountsControllerGetInactiveAccountsAction; -export type AccountsControllerMessenger = RestrictedMessenger< +export type AccountsControllerMessenger = Messenger< 'AccountsController', AccountsControllerActions, - never, - never, - never + AccountsControllerEvents >; class AccountsController extends BaseController { @@ -1304,12 +1288,10 @@ type AllowedActions = | AccountsControllerGetActiveAccountsAction | AccountsControllerGetInactiveAccountsAction; -export type TokensControllerMessenger = RestrictedMessenger< +export type TokensControllerMessenger = Messenger< 'TokensController', - AllowedActions, - never, - AllowedActions['type'], - never + TokensControllerActions | AllowedActions, + TokensControllerEvents >; class TokensController extends BaseController { @@ -1385,12 +1367,10 @@ export type AccountsControllerGetStateAction = ControllerGetStateAction< type AccountsControllerActions = AccountsControllerGetStateAccountAction; -export type AccountsControllerMessenger = RestrictedMessenger< +export type AccountsControllerMessenger = Messenger< 'AccountsController', AccountsControllerActions, - never, - never, - never + AccountsControllerEvents, >; /* === This repo: packages/tokens-controller/src/TokensController.ts === */ @@ -1402,12 +1382,10 @@ import { accountsControllerSelectors } from '@metamask/accounts-controller'; type AllowedActions = AccountsControllerGetStateAction; -export type TokensControllerMessenger = RestrictedMessenger< +export type TokensControllerMessenger = Messenger< 'TokensController', - AllowedActions, - never, - AllowedActions['type'], - never + TokensControllerActions | AllowedActions, + TokensControllerEvents >; class TokensController extends BaseController { diff --git a/docs/data-services.md b/docs/data-services.md index 57d79671c0b..b00920b77a0 100644 --- a/docs/data-services.md +++ b/docs/data-services.md @@ -78,7 +78,7 @@ Next we'll define the messenger. We give the messenger a namespace, and we expos ```typescript // (top of file) -import type { RestrictedMessenger } from '@metamask/base-controller'; +import type { Messenger } from '@metamask/base-controller'; const SERVICE_NAME = 'GasPricesService'; diff --git a/packages/sample-controllers/CHANGELOG.md b/packages/sample-controllers/CHANGELOG.md index 221507b9201..a486e0e44e8 100644 --- a/packages/sample-controllers/CHANGELOG.md +++ b/packages/sample-controllers/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Migrate to new `Messenger` class ([#6335](https://github.com/MetaMask/core/pull/6335)) - Bump `@metamask/network-controller` from `^24.2.2` to `^24.3.0` ([#6883](https://github.com/MetaMask/core/pull/6883)) ## [2.0.1] diff --git a/packages/sample-controllers/package.json b/packages/sample-controllers/package.json index 59203040d7a..2e6cf450715 100644 --- a/packages/sample-controllers/package.json +++ b/packages/sample-controllers/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.1", + "@metamask/messenger": "^0.1.0", "@metamask/utils": "^11.8.1" }, "devDependencies": { diff --git a/packages/sample-controllers/src/sample-gas-prices-controller.test.ts b/packages/sample-controllers/src/sample-gas-prices-controller.test.ts index 6159414c133..fd7744a49b5 100644 --- a/packages/sample-controllers/src/sample-gas-prices-controller.test.ts +++ b/packages/sample-controllers/src/sample-gas-prices-controller.test.ts @@ -1,12 +1,13 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import { SampleGasPricesController } from '@metamask/sample-controllers'; import type { SampleGasPricesControllerMessenger } from '@metamask/sample-controllers'; import { flushPromises } from '../../../tests/helpers'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; import { buildMockGetNetworkClientById } from '../../network-controller/tests/helpers'; describe('SampleGasPricesController', () => { @@ -301,7 +302,7 @@ describe('SampleGasPricesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); @@ -362,8 +363,11 @@ describe('SampleGasPricesController', () => { * required by the controller under test. */ type RootMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent + // Use `string` rather than 'Root' here to allow registering actions and publishing events from + // any namespace in tests. + string, + MessengerActions, + MessengerEvents >; /** @@ -389,7 +393,7 @@ type WithControllerOptions = { * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger(); + return new Messenger({ namespace: 'Root' }); } /** @@ -402,13 +406,9 @@ function getRootMessenger(): RootMessenger { function getMessenger( rootMessenger: RootMessenger, ): SampleGasPricesControllerMessenger { - return rootMessenger.getRestricted({ - name: 'SampleGasPricesController', - allowedActions: [ - 'SampleGasPricesService:fetchGasPrices', - 'NetworkController:getNetworkClientById', - ], - allowedEvents: ['NetworkController:stateChange'], + return new Messenger({ + namespace: 'SampleGasPricesController', + parent: rootMessenger, }); } diff --git a/packages/sample-controllers/src/sample-gas-prices-controller.ts b/packages/sample-controllers/src/sample-gas-prices-controller.ts index c6db87b3f32..8bcec969f01 100644 --- a/packages/sample-controllers/src/sample-gas-prices-controller.ts +++ b/packages/sample-controllers/src/sample-gas-prices-controller.ts @@ -1,10 +1,10 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, StateMetadata, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkClientId, NetworkControllerGetNetworkClientByIdAction, @@ -65,9 +65,9 @@ export type SampleGasPricesControllerState = { */ const gasPricesControllerMetadata = { gasPricesByChainId: { + includeInDebugSnapshot: false, includeInStateLogs: true, persist: true, - anonymous: false, usedInUi: true, }, } satisfies StateMetadata; @@ -137,12 +137,10 @@ type AllowedEvents = NetworkControllerStateChangeEvent; * The messenger restricted to actions and events accessed by * {@link SampleGasPricesController}. */ -export type SampleGasPricesControllerMessenger = RestrictedMessenger< +export type SampleGasPricesControllerMessenger = Messenger< typeof controllerName, SampleGasPricesControllerActions | AllowedActions, - SampleGasPricesControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + SampleGasPricesControllerEvents | AllowedEvents >; // === CONTROLLER DEFINITION === @@ -153,7 +151,7 @@ export type SampleGasPricesControllerMessenger = RestrictedMessenger< * @example * * ``` ts - * import { Messenger } from '@metamask/base-controller'; + * import { Messenger } from '@metamask/messenger'; * import type { * NetworkControllerActions, * NetworkControllerEvents, @@ -170,18 +168,23 @@ export type SampleGasPricesControllerMessenger = RestrictedMessenger< * selectGasPrices, * } from '@metamask/sample-controllers'; * - * const globalMessenger = new Messenger< + * const rootMessenger = new Messenger< + * 'Root', * SampleGasPricesServiceActions * | SampleGasPricesControllerActions * | NetworkControllerActions, * SampleGasPricesServiceEvents * | SampleGasPricesControllerEvents * | NetworkControllerEvents - * >(); - * const gasPricesServiceMessenger = globalMessenger.getRestricted({ - * name: 'SampleGasPricesService', - * allowedActions: [], - * allowedEvents: [], + * >({ namespace: 'Root' }); + * const gasPricesServiceMessenger = new Messenger< + * 'SampleGasPricesService', + * SampleGasPricesServiceActions, + * SampleGasPricesServiceEvents, + * typeof rootMessenger, + * >({ + * namespace: 'SampleGasPricesService', + * parent: rootMessenger, * }); * // Instantiate the service to register its actions on the messenger * new SampleGasPricesService({ @@ -189,10 +192,14 @@ export type SampleGasPricesControllerMessenger = RestrictedMessenger< * // We assume you're using this in the browser. * fetch, * }); - * const gasPricesControllerMessenger = globalMessenger.getRestricted({ - * name: 'SampleGasPricesController', - * allowedActions: ['NetworkController:getNetworkClientById'], - * allowedEvents: ['NetworkController:stateChange'], + * const gasPricesControllerMessenger = new Messenger< + * 'SampleGasPricesController', + * SampleGasPricesControllerActions | NetworkControllerGetNetworkClientByIdAction, + * SampleGasPricesControllerEvents | NetworkControllerStateChangeEvent, + * typeof rootMessenger, + * >({ + * namespace: 'SampleGasPricesController', + * parent: rootMessenger, * }); * // Instantiate the controller to register its actions on the messenger * new SampleGasPricesController({ @@ -200,11 +207,11 @@ export type SampleGasPricesControllerMessenger = RestrictedMessenger< * }); * * // Later... - * await globalMessenger.call( + * await rootMessenger.call( * 'SampleGasPricesController:updateGasPrices', * { chainId: '0x42' }, * ); - * const gasPricesControllerState = await globalMessenger.call( + * const gasPricesControllerState = await rootMessenger.call( * 'SampleGasPricesController:getState', * ); * gasPricesControllerState.gasPricesByChainId @@ -246,12 +253,12 @@ export class SampleGasPricesController extends BaseController< }, }); - this.messagingSystem.registerMethodActionHandlers( + this.messenger.registerMethodActionHandlers( this, MESSENGER_EXPOSED_METHODS, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'NetworkController:stateChange', this.#onSelectedNetworkClientIdChange.bind(this), (networkControllerState) => @@ -267,7 +274,7 @@ export class SampleGasPricesController extends BaseController< * @param args.chainId - The chain ID for which to fetch gas prices. */ async updateGasPrices({ chainId }: { chainId: Hex }) { - const gasPricesResponse = await this.messagingSystem.call( + const gasPricesResponse = await this.messenger.call( 'SampleGasPricesService:fetchGasPrices', chainId, ); @@ -291,7 +298,7 @@ export class SampleGasPricesController extends BaseController< ) { const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', selectedNetworkClientId, ); diff --git a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts index d7628dc190c..159ba031095 100644 --- a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts +++ b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts @@ -1,15 +1,15 @@ -import { Messenger } from '@metamask/base-controller'; import { HttpError } from '@metamask/controller-utils'; +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import nock from 'nock'; import { useFakeTimers } from 'sinon'; import type { SinonFakeTimers } from 'sinon'; import type { SampleGasPricesServiceMessenger } from './sample-gas-prices-service'; import { SampleGasPricesService } from './sample-gas-prices-service'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../../base-controller/tests/helpers'; describe('SampleGasPricesService', () => { let clock: SinonFakeTimers; @@ -293,8 +293,11 @@ describe('SampleGasPricesService', () => { * required by the service under test. */ type RootMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent + // Use `string` rather than 'Root' here to allow registering actions and publishing events from + // any namespace in tests. + string, + MessengerActions, + MessengerEvents >; /** @@ -304,7 +307,7 @@ type RootMessenger = Messenger< * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger(); + return new Messenger({ namespace: 'Root' }); } /** @@ -317,10 +320,9 @@ function getRootMessenger(): RootMessenger { function getMessenger( rootMessenger: RootMessenger, ): SampleGasPricesServiceMessenger { - return rootMessenger.getRestricted({ - name: 'SampleGasPricesService', - allowedActions: [], - allowedEvents: [], + return new Messenger({ + namespace: 'SampleGasPricesService', + parent: rootMessenger, }); } diff --git a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts index 5117114bc7c..1639cce3cd3 100644 --- a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts +++ b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts @@ -1,4 +1,3 @@ -import type { RestrictedMessenger } from '@metamask/base-controller'; import type { CreateServicePolicyOptions, ServicePolicy, @@ -8,6 +7,7 @@ import { fromHex, HttpError, } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import { hasProperty, isPlainObject, type Hex } from '@metamask/utils'; import type { SampleGasPricesServiceMethodActions } from './sample-gas-prices-service-method-action-types'; @@ -49,12 +49,10 @@ type AllowedEvents = never; * The messenger which is restricted to actions and events accessed by * {@link SampleGasPricesService}. */ -export type SampleGasPricesServiceMessenger = RestrictedMessenger< +export type SampleGasPricesServiceMessenger = Messenger< typeof serviceName, SampleGasPricesServiceActions | AllowedActions, - SampleGasPricesServiceEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + SampleGasPricesServiceEvents | AllowedEvents >; // === SERVICE DEFINITION === @@ -76,20 +74,25 @@ type GasPricesResponse = { * @example * * ``` ts - * import { Messenger } from '@metamask/base-controller'; + * import { Messenger } from '@metamask/messenger'; * import type { * SampleGasPricesServiceActions, * SampleGasPricesServiceEvents, * } from '@metamask/sample-controllers'; * - * const globalMessenger = new Messenger< - * SampleGasPricesServiceActions, + * const rootMessenger = new Messenger< + * 'Root', + * SampleGasPricesServiceActions * SampleGasPricesServiceEvents - * >(); - * const gasPricesServiceMessenger = globalMessenger.getRestricted({ - * name: 'SampleGasPricesService', - * allowedActions: [], - * allowedEvents: [], + * >({ namespace: 'Root' }); + * const gasPricesServiceMessenger = new Messenger< + * 'SampleGasPricesService', + * SampleGasPricesServiceActions, + * SampleGasPricesServiceEvents, + * typeof rootMessenger, + * >({ + * namespace: 'SampleGasPricesService', + * parent: rootMessenger, * }); * // Instantiate the service to register its actions on the messenger * new SampleGasPricesService({ @@ -99,7 +102,7 @@ type GasPricesResponse = { * * // Later... * // Fetch gas prices for Mainnet - * const gasPrices = await globalMessenger.call( + * const gasPrices = await rootMessenger.call( * 'SampleGasPricesService:fetchGasPrices', * '0x1', * ); diff --git a/packages/sample-controllers/src/sample-petnames-controller.test.ts b/packages/sample-controllers/src/sample-petnames-controller.test.ts index 40163b21703..9cc34615060 100644 --- a/packages/sample-controllers/src/sample-petnames-controller.test.ts +++ b/packages/sample-controllers/src/sample-petnames-controller.test.ts @@ -1,11 +1,12 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import type { SamplePetnamesControllerMessenger } from './sample-petnames-controller'; import { SamplePetnamesController } from './sample-petnames-controller'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; import { PROTOTYPE_POLLUTION_BLOCKLIST } from '../../controller-utils/src/util'; describe('SamplePetnamesController', () => { @@ -197,7 +198,7 @@ describe('SamplePetnamesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); @@ -258,8 +259,11 @@ describe('SamplePetnamesController', () => { * required by the controller under test. */ type RootMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent + // Use `string` rather than 'Root' here to allow registering actions and publishing events from + // any namespace in tests. + string, + MessengerActions, + MessengerEvents >; /** @@ -285,7 +289,7 @@ type WithControllerOptions = { * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger(); + return new Messenger({ namespace: 'Root' }); } /** @@ -298,10 +302,9 @@ function getRootMessenger(): RootMessenger { function getMessenger( rootMessenger: RootMessenger, ): SamplePetnamesControllerMessenger { - return rootMessenger.getRestricted({ - name: 'SamplePetnamesController', - allowedActions: [], - allowedEvents: [], + return new Messenger({ + namespace: 'SamplePetnamesController', + parent: rootMessenger, }); } diff --git a/packages/sample-controllers/src/sample-petnames-controller.ts b/packages/sample-controllers/src/sample-petnames-controller.ts index cf7a4a70784..bf0e7499441 100644 --- a/packages/sample-controllers/src/sample-petnames-controller.ts +++ b/packages/sample-controllers/src/sample-petnames-controller.ts @@ -1,11 +1,11 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, StateMetadata, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import { isSafeDynamicKey } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; import type { SamplePetnamesControllerMethodActions } from './sample-petnames-controller-method-action-types'; @@ -41,9 +41,9 @@ export type SamplePetnamesControllerState = { */ const samplePetnamesControllerMetadata = { namesByChainIdAndAddress: { + includeInDebugSnapshot: false, includeInStateLogs: true, persist: true, - anonymous: false, usedInUi: true, }, } satisfies StateMetadata; @@ -111,12 +111,10 @@ type AllowedEvents = never; * The messenger restricted to actions and events accessed by * {@link SamplePetnamesController}. */ -export type SamplePetnamesControllerMessenger = RestrictedMessenger< +export type SamplePetnamesControllerMessenger = Messenger< typeof controllerName, SamplePetnamesControllerActions | AllowedActions, - SamplePetnamesControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + SamplePetnamesControllerEvents | AllowedEvents >; // === CONTROLLER DEFINITION === @@ -128,27 +126,32 @@ export type SamplePetnamesControllerMessenger = RestrictedMessenger< * @example * * ``` ts - * import { Messenger } from '@metamask/base-controller'; + * import { Messenger } from '@metamask/messenger'; * import type { * SamplePetnamesControllerActions, * SamplePetnamesControllerEvents, * } from '@metamask/sample-controllers'; * - * const globalMessenger = new Messenger< + * const rootMessenger = new Messenger< + * 'Root', * SamplePetnamesControllerActions, * SamplePetnamesControllerEvents - * >(); - * const samplePetnamesMessenger = globalMessenger.getRestricted({ - * name: 'SamplePetnamesController', - * allowedActions: [], - * allowedEvents: [], + * >({ namespace: 'Root' }); + * const samplePetnamesMessenger = new Messenger< + * 'SamplePetnamesController', + * SamplePetnamesControllerActions, + * SamplePetnamesControllerEvents, + * typeof rootMessenger, + * >({ + * namespace: 'SamplePetnamesController', + * parent: rootMessenger, * }); * // Instantiate the controller to register its actions on the messenger * new SamplePetnamesController({ * messenger: samplePetnamesMessenger, * }); * - * globalMessenger.call( + * rootMessenger.call( * 'SamplePetnamesController:assignPetname', * [ * '0x1', @@ -156,7 +159,7 @@ export type SamplePetnamesControllerMessenger = RestrictedMessenger< * 'Primary Account', * ], * ); - * const samplePetnamesControllerState = await globalMessenger.call( + * const samplePetnamesControllerState = await rootMessenger.call( * 'SamplePetnamesController:getState', * ); * samplePetnamesControllerState.namesByChainIdAndAddress @@ -193,7 +196,7 @@ export class SamplePetnamesController extends BaseController< }, }); - this.messagingSystem.registerMethodActionHandlers( + this.messenger.registerMethodActionHandlers( this, MESSENGER_EXPOSED_METHODS, ); diff --git a/packages/sample-controllers/tsconfig.build.json b/packages/sample-controllers/tsconfig.build.json index 37e83ff4f7f..d71ced03932 100644 --- a/packages/sample-controllers/tsconfig.build.json +++ b/packages/sample-controllers/tsconfig.build.json @@ -7,6 +7,7 @@ }, "references": [ { "path": "../../packages/base-controller/tsconfig.build.json" }, + { "path": "../../packages/messenger/tsconfig.build.json" }, { "path": "../../packages/network-controller/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/sample-controllers/tsconfig.json b/packages/sample-controllers/tsconfig.json index 42ff3e1c18a..65b458897f2 100644 --- a/packages/sample-controllers/tsconfig.json +++ b/packages/sample-controllers/tsconfig.json @@ -6,6 +6,7 @@ "references": [ { "path": "../../packages/base-controller" }, { "path": "../../packages/controller-utils" }, + { "path": "../../packages/messenger" }, { "path": "../../packages/network-controller" } ], "include": ["../../types", "./src"], diff --git a/yarn.lock b/yarn.lock index 5d09b848de7..58d7bf44ea5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4015,6 +4015,13 @@ __metadata: languageName: unknown linkType: soft +"@metamask/messenger@npm:^0.1.0": + version: 0.1.0 + resolution: "@metamask/messenger@npm:0.1.0" + checksum: 10/5d6105865255e72571df143c648ebfb42b04ead24cd6bade758f0340eafba3790d9ff0818bd06fbda17799e3253ac05df51d9e13ebfd0c710e68fd1c0d1007a9 + languageName: node + linkType: hard + "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" @@ -4610,6 +4617,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.1" "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/messenger": "npm:^0.1.0" "@metamask/network-controller": "npm:^24.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" @@ -13946,12 +13954,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.x, semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": - version: 7.7.3 - resolution: "semver@npm:7.7.3" +"semver@npm:7.x, semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": + version: 7.6.3 + resolution: "semver@npm:7.6.3" bin: semver: bin/semver.js - checksum: 10/8dbc3168e057a38fc322af909c7f5617483c50caddba135439ff09a754b20bdd6482a5123ff543dad4affa488ecf46ec5fb56d61312ad20bb140199b88dfaea9 + checksum: 10/36b1fbe1a2b6f873559cd57b238f1094a053dbfd997ceeb8757d79d1d2089c56d1321b9f1069ce263dc64cfa922fa1d2ad566b39426fe1ac6c723c1487589e10 languageName: node linkType: hard @@ -13964,6 +13972,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.3.4": + version: 7.7.3 + resolution: "semver@npm:7.7.3" + bin: + semver: bin/semver.js + checksum: 10/8dbc3168e057a38fc322af909c7f5617483c50caddba135439ff09a754b20bdd6482a5123ff543dad4affa488ecf46ec5fb56d61312ad20bb140199b88dfaea9 + languageName: node + linkType: hard + "send@npm:0.19.0": version: 0.19.0 resolution: "send@npm:0.19.0" From bcd4e747cef42d1cf812ceda88913dea6228a7a5 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Wed, 20 Aug 2025 15:26:24 -0230 Subject: [PATCH 02/16] Update docs and sample-controllers to handle messagingSystem rename --- docs/controller-guidelines.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index 60e99f89ab6..43109ed5ab0 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -344,7 +344,7 @@ class FooController extends BaseController< } doSomething() { - this.messagingSystem.publish('FooController:someEvent'); + this.messenger.publish('FooController:someEvent'); } } @@ -1309,7 +1309,7 @@ class TokensController extends BaseController { // Now TokensController no longer needs an instance of AccountsController to // access the list of active accounts, which is good... const tokens = getTokens( - this.messagingSystem.call('AccountsController:getActiveAccounts'), + this.messenger.call('AccountsController:getActiveAccounts'), ); // ... do something with tokens ... } @@ -1401,7 +1401,7 @@ class TokensController extends BaseController { fetchTokens() { // Now TokensController can use the selector in combination with the state - const tokensControllerState = this.messagingSystem.call( + const tokensControllerState = this.messenger.call( 'AccountsController:getState', ); const accounts = accountsControllerSelectors.selectActiveAccounts( From 2723744802ae25a31ac18512dc384dc670234fdb Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Wed, 20 Aug 2025 15:35:35 -0230 Subject: [PATCH 03/16] Update data services docs --- docs/data-services.md | 75 ++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/docs/data-services.md b/docs/data-services.md index b00920b77a0..1dba0ff8725 100644 --- a/docs/data-services.md +++ b/docs/data-services.md @@ -78,7 +78,7 @@ Next we'll define the messenger. We give the messenger a namespace, and we expos ```typescript // (top of file) -import type { Messenger } from '@metamask/base-controller'; +import type { Messenger } from '@metamask/messenger'; const SERVICE_NAME = 'GasPricesService'; @@ -95,21 +95,19 @@ export type GasPricesServiceEvents = never; type AllowedEvents = never; -export type GasPricesServiceMessenger = RestrictedMessenger< +export type GasPricesServiceMessenger = Messenger< typeof SERVICE_NAME, GasPricesServiceActions | AllowedActions, - GasPricesServiceEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + GasPricesServiceEvents | AllowedEvents >; // ... ``` -Note that we need to add `@metamask/base-controller` as a direct dependency of the package to bring in the `RestrictedMessenger` type (here we assume that our package is called `@metamask/gas-prices-controller`): +Note that we need to add `@metamask/messenger` as a direct dependency of the package to bring in the `Messenger` type (here we assume that our package is called `@metamask/gas-prices-controller`): ```shell -yarn workspace @metamask/gas-prices-controller add @metamask/base-controller +yarn workspace @metamask/gas-prices-controller add @metamask/messenger ``` Finally we will register the method as an action handler on the messenger: @@ -145,7 +143,7 @@ export class GasPricesService {
View whole file
```typescript -import type { RestrictedMessenger } from '@metamask/base-controller'; +import type { Messenger } from '@metamask/messenger'; const SERVICE_NAME = 'GasPricesService'; @@ -162,12 +160,10 @@ export type GasPricesServiceEvents = never; type AllowedEvents = never; -export type GasPricesServiceMessenger = RestrictedMessenger< +export type GasPricesServiceMessenger = Messenger< typeof SERVICE_NAME, GasPricesServiceActions | AllowedActions, - GasPricesServiceEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + GasPricesServiceEvents | AllowedEvents >; type GasPricesResponse = { @@ -272,10 +268,12 @@ import { Messenger } from '@metamask/base-controller'; // ... function buildMessenger(): GasPricesServiceMessenger { - return new Messenger().getRestricted({ - name: 'GasPricesService', - allowedActions: [], - allowedEvents: [], + return new Messenger< + 'GasPricesService', + GasPricesServiceActions, + GasPricesServiceEvents + >({ + namespace: 'GasPricesService', }); } ``` @@ -321,7 +319,11 @@ describe('GasPricesService', () => { ```typescript import nock from 'nock'; -import type { GasPricesServiceMessenger } from './gas-prices-service'; +import type { + GasPricesServiceMessenger, + GasPricesServiceActions, + GasPricesServiceEvents, +} from './gas-prices-service'; import { GasPricesService } from './gas-prices-service'; describe('GasPricesService', () => { @@ -375,10 +377,12 @@ describe('GasPricesService', () => { }); function buildMessenger(): GasPricesServiceMessenger { - return new Messenger().getRestricted({ - name: 'GasPricesService', - allowedActions: [], - allowedEvents: [], + return new Messenger< + 'GasPricesService', + GasPricesServiceActions, + GasPricesServiceEvents + >({ + namespace: 'GasPricesService', }); } ``` @@ -387,7 +391,7 @@ function buildMessenger(): GasPricesServiceMessenger { ## How to use a data service -Let's say that we wanted to use our data service that we built above. To do this, we will instantiate the messenger for the data service — which itself relies on a global messenger — and then the data service itself. +Let's say that we wanted to use our data service that we built above. To do this, we will instantiate the messenger for the data service — which itself relies on a root messenger — and then the data service itself. First we need to import the data service: @@ -395,22 +399,29 @@ First we need to import the data service: import { GasPricesService } from '@metamask/gas-prices-service'; ``` -Then we create a global messenger: +Then we create a root messenger: ```typescript -const globalMessenger = new Messenger(); +const rootMessenger = new Messenger<'Root', AllActions, AllEvents>({ + namespace: 'Root', +}); ``` Then we create a messenger for the GasPricesService: ```typescript -const gasPricesServiceMessenger = globalMessenger.getRestricted({ - allowedActions: [], - allowedEvents: [], +const gasPricesServiceMessenger = new Messenger< + 'GasPricesService', + GasPricesServiceActions, + GasPricesServiceEvents, + typeof rootMessenger +>({ + namespace: 'GasPricesService', + parent: rootMessenger, }); ``` -Now we instantiate the data service to register the action handler on the global messenger. We assume we have a global `fetch` function available: +Now we instantiate the data service to register the action handler on the root messenger. We assume we have a global `fetch` function available: ```typescript const gasPricesService = new GasPricesService({ @@ -421,7 +432,7 @@ const gasPricesService = new GasPricesService({ Great! Now that we've set up the data service and its messenger action, we can use it somewhere else. -Let's say we wanted to use it in a controller. We'd just need to allow that controller's messenger access to `GasPricesService:fetchGasPrices` by passing it via the `allowedActions` option. +Let's say we wanted to use it in a controller. We'd just need to allow that controller's messenger access to `GasPricesService:fetchGasPrices` by delegating it from the root messenger. This code would probably be in the controller package itself. For instance, if we had a file `packages/send-controller/send-controller.ts`, we might have: @@ -436,12 +447,10 @@ type SendControllerEvents = ...; type AllowedEvents = ...; -type SendControllerMessenger = RestrictedMessenger< +type SendControllerMessenger = Messenger< 'SendController', SendControllerActions | AllowedActions, SendControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] >; ``` @@ -452,7 +461,7 @@ class SendController extends BaseController { // ... await someMethodThatUsesGasPrices() { - const gasPrices = await this.#messagingSystem.call( + const gasPrices = await this.messenger.call( 'GasPricesService:fetchGasPrices', ); // ... use gasPrices somehow ... From 1692a732e48db9b306a36634dbd7683d7d02a19a Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 21 Aug 2025 15:09:01 -0230 Subject: [PATCH 04/16] Avoid using the term messaging system --- docs/controller-guidelines.md | 14 +++++++------- docs/data-services.md | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index 43109ed5ab0..2b766cc2130 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -14,7 +14,7 @@ Controllers are foundational pieces within MetaMask's architecture: All controllers should inherit from `BaseController` from the `@metamask/base-controller` package. This provides a few benefits: - It defines a standard interface for all controllers. -- It introduces the messenger system, which is useful for interacting with other controllers without requiring direct access to them. +- It introduces the messenger, which is useful for interacting with other parts of the application without requiring a direct reference. - It enforces that `update` is the only way to modify the state of the controller and provides a way to listen for state updates via the messenger. - It simplifies initialization by consolidating constructor arguments into one options object. @@ -22,7 +22,7 @@ All controllers should inherit from `BaseController` from the `@metamask/base-co One of the uniquely identifying features of a controller is the ability to manage state. -If you have a class that does not capture any data in state, then your class does not need to inherit from `BaseController` (even if it uses the messaging system). +If you have a class that does not capture any data in state, then your class does not need to inherit from `BaseController` (even if it uses a messenger). ## Maintain a clear and concise API @@ -193,7 +193,7 @@ class FooController extends BaseController { } ``` -## Use the messaging system instead of callbacks +## Use the messenger instead of callbacks Prior to BaseController v2, it was common for a controller to respond to an event occurring within another controller (such a state change) by receiving an event listener callback which the client would bind ahead of time: @@ -222,7 +222,7 @@ const fooController = new FooController({ }); ``` -If the recipient controller supports the messaging system, however, the callback pattern is unnecessary. Using the messenger not only aligns the controller with `BaseController`, but also reduces the number of options that consumers need to remember in order to use the controller: +If the recipient controller uses a messenger, however, the callback pattern is unnecessary. Using the messenger not only aligns the controller with `BaseController`, but also reduces the number of options that consumers need to remember in order to use the controller: ✅ **The constructor subscribes to the `BarController:stateChange` event** @@ -286,7 +286,7 @@ const fooController = new FooController({ }); ``` -## Use the messaging system instead of event emitters +## Use the messenger instead of event emitters Some controllers expose an EventEmitter object so that other parts of the system can listen to them: @@ -1201,7 +1201,7 @@ class PreferencesController extends BaseController< ## Expose derived state using selectors instead of getters -Sometimes, for convenience, consumers want access to a higher-level representation of a controller's state. It is tempting to add a method to the controller which provides this representation, but this means that a consumer would need an entire instance of the controller on hand to use this method. Using the messaging system mitigates this problem, but then the consumer would need access to a messenger as well, which may be impossible in places like Redux selector functions. +Sometimes, for convenience, consumers want access to a higher-level representation of a controller's state. It is tempting to add a method to the controller which provides this representation, but this means that a consumer would need an entire instance of the controller on hand to use this method. Using the messenger mitigates this problem, but then the consumer would need access to a messenger as well, which may be impossible in places like Redux selector functions. To make it easier to share such representations across disparate parts of the codebase in a flexible fashion, you can define and export selector functions from your controller file instead. They should be placed under a `${controllerName}Selectors` object and then exported. @@ -1241,7 +1241,7 @@ class TokensController extends BaseController { } ``` -🚫 **Methods exposed via the messaging system** +🚫 **Methods exposed via the messenger** ```typescript /* === This repo: packages/accounts-controller/src/AccountsController.ts === */ diff --git a/docs/data-services.md b/docs/data-services.md index 1dba0ff8725..951f8e79021 100644 --- a/docs/data-services.md +++ b/docs/data-services.md @@ -2,14 +2,14 @@ ## What is a data service? -A **data service** is a pattern for making interactions with an external API (fetching token prices, storing accounts, etc.). It is implemented as a plain TypeScript class with methods that are exposed through the messaging system. +A **data service** is a pattern for making interactions with an external API (fetching token prices, storing accounts, etc.). It is implemented as a plain TypeScript class with methods that are exposed through a messenger. ## Why use this pattern? If you want to talk to an API, it might be tempting to define a method in the controller or a function in a separate file. However, implementing the data service pattern is advantageous for the following reasons: 1. The pattern provides an abstraction that allows for implementing and reusing strategies that are common when working with external APIs, such as batching, automatic retries with exponential backoff, etc. -2. By integrating with the messaging system, other parts of the application can make use of the data service without needing to go through the controller, or in fact, without needing a reference to the data service at all. +2. By integrating with a messenger, other parts of the application can make use of the data service without needing to go through the controller, or in fact, without needing a reference to the data service at all. ## How to create a data service From 7215dd07edce1f31ab5e2dc5eac1605879cd0a77 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 21 Aug 2025 15:13:46 -0230 Subject: [PATCH 05/16] Expand on data service delegation example --- docs/data-services.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/data-services.md b/docs/data-services.md index 951f8e79021..cfd3967d3dc 100644 --- a/docs/data-services.md +++ b/docs/data-services.md @@ -432,7 +432,7 @@ const gasPricesService = new GasPricesService({ Great! Now that we've set up the data service and its messenger action, we can use it somewhere else. -Let's say we wanted to use it in a controller. We'd just need to allow that controller's messenger access to `GasPricesService:fetchGasPrices` by delegating it from the root messenger. +Let's say we wanted to use `GasPricesService:fetchGasPrices` in a controller. First, that controller's messenger would need to include `GasPricesService:fetchGasPrices` in its type defintion. This code would probably be in the controller package itself. For instance, if we had a file `packages/send-controller/send-controller.ts`, we might have: @@ -454,6 +454,15 @@ type SendControllerMessenger = Messenger< >; ``` +Then we'll need to allow that controller's messenger access to `GasPricesService:fetchGasPrices` by delegating it from the root messenger: + +```typescript +rootMessenger.delegate({ + actions: ['GasPricesService:fetchGasPrices'], + messenger: sendControllerMessenger, +}); +``` + Then, later on in our controller, we could say: ```typescript From f784d4849d8933a1dd8f58876e6302b0bdbd44bb Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 28 Aug 2025 14:28:15 -0230 Subject: [PATCH 06/16] Fix typo Co-authored-by: Elliot Winkler --- docs/controller-guidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index 2b766cc2130..e6cd3d8ac7f 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -680,7 +680,7 @@ export type FooControllerMessenger = Messenger< export type { AllowedEvents } from '@metamask/foo-controller'; ``` -🚫 **External events are included in controller actieventn type** +🚫 **External events are included in controller event type** ```typescript export type FooControllerEvents = From 53f2c02ad684b0a71efa3d649d3c52ace4981998 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 28 Aug 2025 14:29:28 -0230 Subject: [PATCH 07/16] Remove 'as const' that doesn't seem necessary --- docs/controller-guidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index e6cd3d8ac7f..a8413a325f6 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -278,7 +278,7 @@ const fooControllerMessenger = new Messenger< parent: rootMessenger, }); rootMessenger.delegate({ - events: ['BarController:stateChange' as const], + events: ['BarController:stateChange'], messenger: fooControllerMessenger, }); const fooController = new FooController({ From 0e497c2a2ee420c38b611b78855856298499bfd9 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 28 Aug 2025 17:26:37 -0230 Subject: [PATCH 08/16] Improve example title by dropping reference to type parameter order --- docs/controller-guidelines.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index a8413a325f6..e143b729001 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -617,7 +617,7 @@ export type FooControllerMessenger = Messenger< >; ``` -✅ **`AllowedActions` is included in the 2nd type parameter but is _not_ exported** +✅ **`AllowedActions` is included in the actions type union but is _not_ exported** ```typescript export type FooControllerActions = @@ -696,7 +696,7 @@ export type FooControllerMessenger = Messenger< >; ``` -✅ **`AllowedEvents` is included in the 3nd type parameter but is _not_ exported** +✅ **`AllowedEvents` is included in the events type union but is _not_ exported** ```typescript export type FooControllerEvents = From 22a761eb9ecd757c8a5b69258367a2ff2abe354c Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 28 Aug 2025 17:26:59 -0230 Subject: [PATCH 09/16] Fix mistake in type parameter number --- docs/controller-guidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index e143b729001..bd954e9f5df 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -553,7 +553,7 @@ The name of this type should be `${ControllerName}Events`. This type should be passed to `Messenger` as the 3rd type parameter. It should _not_ include external events. -✅ **`FooControllerEvents` is passed as the 2nd type parameter (assuming no external events)** +✅ **`FooControllerEvents` is passed as the 3rd type parameter (assuming no external events)** ```typescript export type FooControllerEvents = From 2ddbd1ad1b8385bfdda168fbdda55255c84580e5 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 28 Aug 2025 17:29:44 -0230 Subject: [PATCH 10/16] Improve example title consistency --- docs/controller-guidelines.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index bd954e9f5df..25ac0375b7f 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -577,7 +577,7 @@ The name of this type should be `AllowedActions`. This type should be passed to `Messenger` as part of the 2nd type parameter, in a type union with internal actions. -🚫 **`AllowedActions` is exported** +🚫 **`AllowedActions` is included in the actions type union and _is_ exported** ```typescript /* === packages/foo-controller/src/FooController.ts === */ @@ -657,7 +657,7 @@ The name of this type should be `AllowedEvents`. This type should be passed to `Messenger` as part of the 3rd type parameter, in a type union with internal events. -🚫 **`AllowedEvents` is exported** +🚫 **`AllowedEvents` is included in the actions type union and _is_ exported** ```typescript /* === packages/foo-controller/src/FooController.ts === */ From 7ebddfd102b8a8301a138c83a7a2ce87bdaf708a Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Tue, 2 Sep 2025 13:15:49 -0230 Subject: [PATCH 11/16] Use new mock namespace bypass --- .../src/sample-gas-prices-controller.test.ts | 8 ++++---- .../sample-gas-prices-service.test.ts | 8 ++++---- .../src/sample-petnames-controller.test.ts | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/sample-controllers/src/sample-gas-prices-controller.test.ts b/packages/sample-controllers/src/sample-gas-prices-controller.test.ts index fd7744a49b5..29569e825f1 100644 --- a/packages/sample-controllers/src/sample-gas-prices-controller.test.ts +++ b/packages/sample-controllers/src/sample-gas-prices-controller.test.ts @@ -1,6 +1,8 @@ import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, type MessengerActions, type MessengerEvents, } from '@metamask/messenger'; @@ -363,9 +365,7 @@ describe('SampleGasPricesController', () => { * required by the controller under test. */ type RootMessenger = Messenger< - // Use `string` rather than 'Root' here to allow registering actions and publishing events from - // any namespace in tests. - string, + MockAnyNamespace, MessengerActions, MessengerEvents >; @@ -393,7 +393,7 @@ type WithControllerOptions = { * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger({ namespace: 'Root' }); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** diff --git a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts index 159ba031095..1796ea2bd6e 100644 --- a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts +++ b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts @@ -1,6 +1,8 @@ import { HttpError } from '@metamask/controller-utils'; import { Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, type MessengerActions, type MessengerEvents, } from '@metamask/messenger'; @@ -293,9 +295,7 @@ describe('SampleGasPricesService', () => { * required by the service under test. */ type RootMessenger = Messenger< - // Use `string` rather than 'Root' here to allow registering actions and publishing events from - // any namespace in tests. - string, + MockAnyNamespace, MessengerActions, MessengerEvents >; @@ -307,7 +307,7 @@ type RootMessenger = Messenger< * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger({ namespace: 'Root' }); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** diff --git a/packages/sample-controllers/src/sample-petnames-controller.test.ts b/packages/sample-controllers/src/sample-petnames-controller.test.ts index 9cc34615060..0d7d67159f7 100644 --- a/packages/sample-controllers/src/sample-petnames-controller.test.ts +++ b/packages/sample-controllers/src/sample-petnames-controller.test.ts @@ -1,6 +1,8 @@ import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, type MessengerActions, type MessengerEvents, } from '@metamask/messenger'; @@ -259,9 +261,7 @@ describe('SamplePetnamesController', () => { * required by the controller under test. */ type RootMessenger = Messenger< - // Use `string` rather than 'Root' here to allow registering actions and publishing events from - // any namespace in tests. - string, + MockAnyNamespace, MessengerActions, MessengerEvents >; @@ -289,7 +289,7 @@ type WithControllerOptions = { * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger({ namespace: 'Root' }); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** From 692060ddd8828547c0ecb475547f35a9a3681389 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Tue, 2 Sep 2025 13:25:15 -0230 Subject: [PATCH 12/16] Delegate actions/events to test gas prices controller messenger --- .../src/sample-gas-prices-controller.test.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/sample-controllers/src/sample-gas-prices-controller.test.ts b/packages/sample-controllers/src/sample-gas-prices-controller.test.ts index 29569e825f1..b61edf5c6c4 100644 --- a/packages/sample-controllers/src/sample-gas-prices-controller.test.ts +++ b/packages/sample-controllers/src/sample-gas-prices-controller.test.ts @@ -406,10 +406,19 @@ function getRootMessenger(): RootMessenger { function getMessenger( rootMessenger: RootMessenger, ): SampleGasPricesControllerMessenger { - return new Messenger({ + const messenger: SampleGasPricesControllerMessenger = new Messenger({ namespace: 'SampleGasPricesController', parent: rootMessenger, }); + rootMessenger.delegate({ + actions: [ + 'NetworkController:getNetworkClientById', + 'SampleGasPricesService:fetchGasPrices', + ], + events: ['NetworkController:stateChange'], + messenger, + }); + return messenger; } /** From 2e2705b87a1e2f28be13b6cf01aae3686343b196 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 3 Oct 2025 16:16:36 -0230 Subject: [PATCH 13/16] Fix lint errors --- packages/sample-controllers/package.json | 2 +- .../sample-gas-prices-service.test.ts | 2 ++ .../sample-gas-prices-service.ts | 2 ++ yarn.lock | 9 +-------- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/sample-controllers/package.json b/packages/sample-controllers/package.json index 2e6cf450715..955ff91a2c3 100644 --- a/packages/sample-controllers/package.json +++ b/packages/sample-controllers/package.json @@ -48,7 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.1", - "@metamask/messenger": "^0.1.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1" }, "devDependencies": { diff --git a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts index 1796ea2bd6e..2c82b73f7c6 100644 --- a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts +++ b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts @@ -297,6 +297,8 @@ describe('SampleGasPricesService', () => { type RootMessenger = Messenger< MockAnyNamespace, MessengerActions, + // TODO: Disable this lint rule + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments MessengerEvents >; diff --git a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts index 1639cce3cd3..5eacf5d7a79 100644 --- a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts +++ b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts @@ -52,6 +52,8 @@ type AllowedEvents = never; export type SampleGasPricesServiceMessenger = Messenger< typeof serviceName, SampleGasPricesServiceActions | AllowedActions, + // TODO: Disable this lint rule + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments SampleGasPricesServiceEvents | AllowedEvents >; diff --git a/yarn.lock b/yarn.lock index 58d7bf44ea5..034d2b30cc1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4015,13 +4015,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/messenger@npm:^0.1.0": - version: 0.1.0 - resolution: "@metamask/messenger@npm:0.1.0" - checksum: 10/5d6105865255e72571df143c648ebfb42b04ead24cd6bade758f0340eafba3790d9ff0818bd06fbda17799e3253ac05df51d9e13ebfd0c710e68fd1c0d1007a9 - languageName: node - linkType: hard - "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" @@ -4617,7 +4610,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.1" "@metamask/controller-utils": "npm:^11.14.1" - "@metamask/messenger": "npm:^0.1.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" From 80d77308319b6675b3f0e3cb1a814f510e9f3c30 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Mon, 20 Oct 2025 10:48:58 -0230 Subject: [PATCH 14/16] Update controller metadata docs with new properties` --- docs/controller-guidelines.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index 25ac0375b7f..7a4c613304f 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -108,22 +108,24 @@ export { FooController, getDefaultFooControllerState } from './FooController'; Each property in state has two pieces of metadata that must be specified. This instructs the client how to treat that property: +- `includeInDebugLogs` - Informs the client whether to include the property in debug state logs attached to Sentry events (`true`) or not (`false`). We must exclude any data that could potentially be personally identifying here, and we often also exclude data that is large and/or unhelpful for debugging. +- `includeInStateLogs` - Informs the client whether to include the property in state logs downloaded by users (`true`) or not (`false`). We must exclude any sensitive data that we don't want our support team to have access to (such as private keys). We include personally-identifiable data related to on-chain state here (we never collect this data, and we have a disclaimer about this in the UI when users download state logs), but other types of personally identifiable information must still be excluded. - `persist` — Informs the client whether the property should be placed in persistent storage (`true`) or not (`false`). Opting out is useful if you want to have a property in state for convenience reasons but you know that property is ephemeral and can be easily reconstructed. -- `anonymous` — Informs the client whether the property is free of personally identifiable information (`true`) or not (`false`) and can therefore safely be included and sent to error reporting services such as Sentry. When in doubt, use `false`. +- `usedInUi` - Informs the client whether the property is used in the UI (`true`) or not (`false`). This is used to filter the state we send to the UI to improve performance. A variable named `${controllerName}Metadata` should be defined (there is no need to export it) and passed as the `metadata` argument in the constructor to `BaseController`. ```typescript const keyringControllerMetadata = { vault: { + // This property can be used to identify a user, so we want to make sure we + // do not include it in Sentry. + includeInDebugLogs: false, // We don't want to include this in state logs because it contains sensitive key material. includeInStateLogs: false, // We want to persist this property so it's restored automatically, as we // cannot reconstruct it otherwise. persist: true, - // This property can be used to identify a user, so we want to make sure we - // do not include it in Sentry. - anonymous: false, // This property is only used in the controller, not in the UI. usedInUi: false, }, From d4bea7d76e9ae1ef608f55510b3d1c0c6838a8eb Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Tue, 21 Oct 2025 18:34:15 -0230 Subject: [PATCH 15/16] Dedupe dependencies --- yarn.lock | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index 034d2b30cc1..f7f9842e83c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13947,12 +13947,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.x, semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": - version: 7.6.3 - resolution: "semver@npm:7.6.3" +"semver@npm:7.x, semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": + version: 7.7.3 + resolution: "semver@npm:7.7.3" bin: semver: bin/semver.js - checksum: 10/36b1fbe1a2b6f873559cd57b238f1094a053dbfd997ceeb8757d79d1d2089c56d1321b9f1069ce263dc64cfa922fa1d2ad566b39426fe1ac6c723c1487589e10 + checksum: 10/8dbc3168e057a38fc322af909c7f5617483c50caddba135439ff09a754b20bdd6482a5123ff543dad4affa488ecf46ec5fb56d61312ad20bb140199b88dfaea9 languageName: node linkType: hard @@ -13965,15 +13965,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.4": - version: 7.7.3 - resolution: "semver@npm:7.7.3" - bin: - semver: bin/semver.js - checksum: 10/8dbc3168e057a38fc322af909c7f5617483c50caddba135439ff09a754b20bdd6482a5123ff543dad4affa488ecf46ec5fb56d61312ad20bb140199b88dfaea9 - languageName: node - linkType: hard - "send@npm:0.19.0": version: 0.19.0 resolution: "send@npm:0.19.0" From c1ad7a45e9261bc046a24303711f52c00b3a231a Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 24 Oct 2025 17:32:14 -0230 Subject: [PATCH 16/16] Fix mistake in docs --- docs/controller-guidelines.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index 7a4c613304f..f4db78d4580 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -108,7 +108,7 @@ export { FooController, getDefaultFooControllerState } from './FooController'; Each property in state has two pieces of metadata that must be specified. This instructs the client how to treat that property: -- `includeInDebugLogs` - Informs the client whether to include the property in debug state logs attached to Sentry events (`true`) or not (`false`). We must exclude any data that could potentially be personally identifying here, and we often also exclude data that is large and/or unhelpful for debugging. +- `includeInDebugSnapshot` - Informs the client whether to include the property in debug state logs attached to Sentry events (`true`) or not (`false`). We must exclude any data that could potentially be personally identifying here, and we often also exclude data that is large and/or unhelpful for debugging. - `includeInStateLogs` - Informs the client whether to include the property in state logs downloaded by users (`true`) or not (`false`). We must exclude any sensitive data that we don't want our support team to have access to (such as private keys). We include personally-identifiable data related to on-chain state here (we never collect this data, and we have a disclaimer about this in the UI when users download state logs), but other types of personally identifiable information must still be excluded. - `persist` — Informs the client whether the property should be placed in persistent storage (`true`) or not (`false`). Opting out is useful if you want to have a property in state for convenience reasons but you know that property is ephemeral and can be easily reconstructed. - `usedInUi` - Informs the client whether the property is used in the UI (`true`) or not (`false`). This is used to filter the state we send to the UI to improve performance. @@ -120,7 +120,7 @@ const keyringControllerMetadata = { vault: { // This property can be used to identify a user, so we want to make sure we // do not include it in Sentry. - includeInDebugLogs: false, + includeInDebugSnapshot: false, // We don't want to include this in state logs because it contains sensitive key material. includeInStateLogs: false, // We want to persist this property so it's restored automatically, as we