Deprecate <ControllerName>:stateChange in favor of :stateChanged#8187
Deprecate <ControllerName>:stateChange in favor of :stateChanged#8187
Conversation
dbd3929 to
d8d71e5
Compare
Since the `:stateChange` event was added to `BaseController`, we have added a guideline asking engineers to use past tense for all event names. This commit updates BaseController to align with that guideline. To achieve backward compatibility, we add a new event, `:stateChanged`, rather than replacing the existing event. We deprecate `:stateChange` by adding some custom ESLint rules.
d8d71e5 to
41daada
Compare
eslint.config.mjs
Outdated
| // replaces the rule entirely rather than merging arrays. | ||
| 'no-restricted-syntax': [ | ||
| 'error', | ||
| ...getNoRestrictedSyntaxEntries(typescript), |
There was a problem hiding this comment.
Feels like there ought to be a better way to do this...
| expect(messengerSpy).toHaveBeenNthCalledWith( | ||
| // 1. AccountsController:stateChange | ||
| 2, | ||
| expect(messengerSpy).toHaveBeenCalledWith( |
There was a problem hiding this comment.
The name of this test indicates that all we care about is that the accountAdded event is published, so I not only fixed the test but changed it to reflect that.
| expect(messengerSpy).toHaveBeenNthCalledWith( | ||
| // 1. AccountsController:stateChange | ||
| 2, | ||
| expect(messengerSpy).toHaveBeenCalledWith( |
| accountsController.state.internalAccounts.selectedAccount, | ||
| ).toStrictEqual(mockNonEvmAccount.id); | ||
|
|
||
| expect(messengerSpy.mock.calls).toHaveLength(2); // state change and then selectedAccountChange |
There was a problem hiding this comment.
Similar story; based on the test name it seems all we care about is that the selectedEvmAccountChange event is not published.
|
|
||
| // check the 3rd call since the 2nd one is for the | ||
| // event stateChange | ||
| // Some of these calls are for state changes |
There was a problem hiding this comment.
Since there is a new state change event it changes the number of expected events.
| >['type'] extends MessengerEvents<ControllerMessenger>['type'] | ||
| ? ControllerMessenger | ||
| : never | ||
| : ControllerStateChangedEvent< |
There was a problem hiding this comment.
Controllers must at least support :stateChange or :stateChanged. (We don't want to require that controllers support both :stateChange and :stateChanged, so we can't use ControllerEvents here anymore.)
…leOptions
The getNoRestrictedSyntaxEntries function was redundant with the
pre-existing collectExistingRuleOptions helper. It was also less
robust: it used '?? []' instead of an Array.isArray guard, meaning it
would throw a TypeError if an upstream config ever set the rule to a
non-array value (e.g. 'off' or 0).
Replace the call site with collectExistingRuleOptions('no-restricted-syntax',
[typescript]) and remove the now-unused helper.
Co-authored-by: Elliot Winkler <elliot.winkler@gmail.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| accountsController.state.internalAccounts.selectedAccount, | ||
| ).toStrictEqual(mockNonEvmAccount.id); | ||
|
|
||
| expect(messengerSpy.mock.calls).toHaveLength(2); // state change and then selectedAccountChange |
There was a problem hiding this comment.
Weakened test no longer verifies event is never published
Low Severity
The test "does not emit setSelectedEvmAccountChange if the account is non-EVM" previously had three assertions that together proved selectedEvmAccountChange was never published: a call-count check, a toHaveBeenLastCalledWith for the expected event, and not.toHaveBeenLastCalledWith for the unexpected event. The first two were removed, leaving only not.toHaveBeenLastCalledWith, which only checks the last call. With the new stateChanged event adding more published events, this assertion could pass even if selectedEvmAccountChange were erroneously published as a non-last event. Changing to not.toHaveBeenCalledWith would match the stated intent.


Explanation
Since the
:stateChangeevent was added to BaseController, we have added a guideline which suggests that engineers use past tense for all event names. This commit updatesBaseControllerto align with that guideline. To maintain backward compatibility, we add a new event,:stateChanged, rather than replacing the existing event. We also deprecate subscribing to or delegating:stateChangeby adding some custom ESLint rules.References
We are about to add the
:cacheUpdatetoBaseDataServicehere, so I thought I'd deprecateBaseController:stateChangeso we can addBaseDataService:cacheUpdatedinstead.Checklist
Note
Medium Risk
Touches
BaseControllerevent publishing/typing and subscription cleanup, so regressions could impact any controller state listener. Backward compatibility is preserved by emitting both events, but consumers may see extra publishes.Overview
Introduces a new controller state event name.
BaseControllernow definesControllerStateChangedEventand treats controller events as a union of:stateChangeand:stateChanged, registering initial payloads and publishing both events onupdate/applyPatches.Deprecation enforcement and adoption guidance. ESLint adds
no-restricted-syntaxselectors to flag subscribing to or delegating*:*:stateChange, with accompanying updates to docs/guidelines andeslint-suppressions.jsonto account for existing usages.Tests/changelog updated for dual emission.
base-controllertests are refactored to validate behavior for both event names, some downstream tests loosen expectations around publish call ordering/counts, andpackages/base-controller/CHANGELOG.mddocuments the addition and deprecation.Written by Cursor Bugbot for commit 07cce7c. This will update automatically on new commits. Configure here.