Skip to content

Conversation

Gudahtt
Copy link
Member

@Gudahtt Gudahtt commented Jul 17, 2025

Explanation

Add the parent constructor parameter to the Messenger class for automatic delegation of all capabilities under that messenger's namespace. This significantly reduces boilerplate, making the messenger easier to use in a similar manner to how the old RestrictedMessenger class wored.

See this ADR PR for details: MetaMask/decisions#53

References

Depends upon #6132
Relates to #5626

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed, highlighting breaking changes as necessary
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

@Gudahtt Gudahtt force-pushed the add-messenger-parent-param branch 3 times, most recently from 4ff719e to f3e18ba Compare July 17, 2025 16:31
@Gudahtt Gudahtt changed the title feat: Add Messenger parant parameter feat: Add Messenger parent parameter Jul 17, 2025
@Gudahtt Gudahtt force-pushed the add-messenger-delegation branch from 2dd241a to 6729773 Compare July 23, 2025 20:11
@Gudahtt Gudahtt force-pushed the add-messenger-delegation branch 2 times, most recently from 1e6b6ab to d6f1dc4 Compare August 7, 2025 12:40
@Gudahtt Gudahtt force-pushed the add-messenger-parent-param branch from f3e18ba to 04b2069 Compare August 7, 2025 12:40
@Gudahtt Gudahtt force-pushed the add-messenger-delegation branch 7 times, most recently from b9bef02 to f980adf Compare August 12, 2025 20:36
@Gudahtt Gudahtt force-pushed the add-messenger-parent-param branch from 0a293cd to 11eece3 Compare August 12, 2025 21:03
@Gudahtt Gudahtt force-pushed the add-messenger-delegation branch from f980adf to 61f5053 Compare August 13, 2025 15:21
@Gudahtt Gudahtt force-pushed the add-messenger-parent-param branch from 11eece3 to f057a12 Compare August 13, 2025 15:22
Base automatically changed from add-messenger-delegation to main August 13, 2025 15:26
@Gudahtt Gudahtt force-pushed the add-messenger-parent-param branch from eb8e095 to 764f8e9 Compare August 13, 2025 16:20
@Gudahtt
Copy link
Member Author

Gudahtt commented Aug 13, 2025

Type tests can be found on this branch: https://github.com/MetaMask/core/tree/add-messenger-type-tests-parent

@Gudahtt Gudahtt marked this pull request as ready for review August 13, 2025 16:31
@Gudahtt Gudahtt requested a review from a team as a code owner August 13, 2025 16:31
cursor[bot]

This comment was marked as outdated.

Namespace extends string,
Action extends ActionConstraint,
Event extends EventConstraint,
Parent extends Messenger<
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new addition from what was described in the ADR. As you can see in the tests, it does make using this a bit more verbose, but it's not too bad IMO. This was the only way I could find to validate that the parent had all necessary actions/events (this was validated in the constructor params, but the method of validation relied on there being a generic type param to validate against).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense given how we've designed Messenger. I can't think of another way to do this. We can use the typeof trick you used in the tests, though, to make providing this type easier.

Copy link
Contributor

@mcmire mcmire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

string,
ActionConstraint,
EventConstraint,
// Use `any` to avoid preventing a parent from having a parent. `any` is harmless in a type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does work, but wouldn't leaving this type parameter out also work? Then the default would be never which seems more accurate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hesitant to do this because there is nothing really stopping someone from writing a messenger that is both a parent and has a parent. It might even be a reasonably good design. For example, if the TransactionController wanted to have internal messengers for each helper class it uses, they might delegate to the main transaction controller messenger so their actions/events can be used internally in the transaction controller.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I missed the double negative. "Avoid preventing". Got it :)

Yes I agree we should make it possible to essentially create a delegation chain.

}
this.#registerActionHandler(actionType, handler);
if (this.#parent) {
// @ts-expect-error The parent type isn't constructed in a way that proves it supports this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like there ought to be a way to align the types so this isn't necessary, but I don't know how. I tried changing the type of #parent from DelegatedMessenger to Parent but then the if (messenger === this.#parent) { statement you added at the beginning of revoke failed because TypeScript thought there was no overlap between the two values. So it seems that we need @ts-expect-error somewhere...

I do agree this seems type-safe though given the check we have in the constructor.

Namespace extends string,
Action extends ActionConstraint,
Event extends EventConstraint,
Parent extends Messenger<
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense given how we've designed Messenger. I can't think of another way to do this. We can use the typeof trick you used in the tests, though, to make providing this type easier.

*/
export class Messenger<
Namespace extends string,
Action extends ActionConstraint,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note, but should Action and Event default to never so that if you don't supply any type parameters to Messenger, allowing TypeScript to infer them, you don't end up with a messenger that recognizes all actions and events?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I will try this separately

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done here: #6311

@Gudahtt Gudahtt force-pushed the add-messenger-parent-param branch from ad2eab7 to 29d3a01 Compare August 13, 2025 19:43
Add the `parent` constructor parameter to the `Messenger` class for
automatic delegation of all capabilities under that messenger's
namespace. This significantly reduces boilerplate, making the messenger
easier to use in a similar manner to how the old `RestrictedMessenger`
class wored.

See this ADR PR for details: MetaMask/decisions#53
@Gudahtt Gudahtt force-pushed the add-messenger-parent-param branch from 29d3a01 to dfa154b Compare August 13, 2025 19:49
@Gudahtt Gudahtt enabled auto-merge (squash) August 13, 2025 19:49
@Gudahtt Gudahtt merged commit c0a2946 into main Aug 13, 2025
223 checks passed
@Gudahtt Gudahtt deleted the add-messenger-parent-param branch August 13, 2025 19:54
Gudahtt added a commit that referenced this pull request Aug 19, 2025
## Explanation

The action and event type parameters for the new `@metamask/messenger`
package's Messenger class now default to `never`. This should make it
harder to accidentally create a messenger with overly broad inferred
types, making it less type safe than the author might expect.

The `Messenger` classes in `@metamask/base-controller` were not changed
to avoid breaking changes. While this change itself wouldn't really be
breaking by most definitions, it would require changes due to our use of
the `@typescript-eslint/no-unnecessary-type-arguments` lint rule.

## References

This was a suggestion from here:
#6142 (comment)

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've communicated my changes to consumers by [updating changelogs
for packages I've
changed](https://github.com/MetaMask/core/tree/main/docs/contributing.md#updating-changelogs),
highlighting breaking changes as necessary
- [x] I've prepared draft pull requests for clients and consumer
packages to resolve any breaking changes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants