@metamask-previews/network-connection-banner-controller 0.1.0-preview-3c0e8a808 → 0.1.0-preview-33855f889

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,18 +5,26 @@ connection banner based on RPC endpoint health. It encapsulates the shared
5
5
  rule, the 5s/30s timer state machine, and the eTLD+1 grouping previously
6
6
  duplicated across MetaMask clients.
7
7
 
8
- ## Initialization
8
+ ## Lifecycle
9
9
 
10
- After constructing the controller, call `init()` only after the
11
- `NetworkController`, `NetworkEnablementController`, and
12
- `ConnectivityController` have initialized. Until then, upstream state changes
13
- are ignored and no banner timers run.
10
+ The controller stays dormant after construction so the 5s / 30s escalation
11
+ timers do not run before a user is actually looking at the wallet (e.g. while
12
+ the app is still on the lock screen). The UI that renders the banner is
13
+ responsible for driving the lifecycle:
14
14
 
15
15
  ```typescript
16
- networkConnectionBannerController.init();
16
+ // When the wallet UI that shows the banner becomes active
17
+ // (e.g. the home surface mounts after unlock):
18
+ networkConnectionBannerController.start();
19
+
20
+ // When it goes away:
21
+ networkConnectionBannerController.stop();
17
22
  ```
18
23
 
19
- `init()` is idempotent and immediately evaluates the latest upstream state.
24
+ Both methods are idempotent. `start` runs the initial evaluation immediately
25
+ and enables reactions to upstream state changes. `stop` cancels any pending
26
+ timers and resets the banner state to `available`. Upstream state changes are
27
+ ignored while stopped.
20
28
 
21
29
  ## Installation
22
30
 
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController-method-action-types.cjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { NetworkConnectionBannerController } from './NetworkConnectionBannerController';\n\n/**\n * Starts evaluating network connection state.\n *\n * This method should be called after the upstream network, network\n * enablement, and connectivity controllers have been initialized.\n */\nexport type NetworkConnectionBannerControllerInitAction = {\n type: `NetworkConnectionBannerController:init`;\n handler: NetworkConnectionBannerController['init'];\n};\n\n/**\n * Clears the banner state such that the banner will be hidden.\n */\nexport type NetworkConnectionBannerControllerDismissBannerAction = {\n type: `NetworkConnectionBannerController:dismissBanner`;\n handler: NetworkConnectionBannerController['dismissBanner'];\n};\n\n/**\n * Switches the chain's default RPC endpoint to its first Infura endpoint,\n * causing the banner to clear once the network becomes available again.\n *\n * @param args - The arguments to this action.\n * @param args.chainId - The chain whose default RPC should be switched.\n * @throws If the chain configuration cannot be found, or if it has no\n * Infura endpoint to switch to, or if the default is already Infura.\n */\nexport type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction = {\n type: `NetworkConnectionBannerController:switchToDefaultInfuraRpc`;\n handler: NetworkConnectionBannerController['switchToDefaultInfuraRpc'];\n};\n\n/**\n * Union of all NetworkConnectionBannerController action types.\n */\nexport type NetworkConnectionBannerControllerMethodActions =\n | NetworkConnectionBannerControllerInitAction\n | NetworkConnectionBannerControllerDismissBannerAction\n | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction;\n"]}
1
+ {"version":3,"file":"NetworkConnectionBannerController-method-action-types.cjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { NetworkConnectionBannerController } from './NetworkConnectionBannerController';\n\n/**\n * Look for a failed network, if any, and populate the initial state of the\n * banner. Reacts to upstream state changes from this point on.\n *\n * Call this when the wallet UI that consumes the banner becomes active\n * (typically when the wallet is unlocked and the home surface mounts) so\n * timers do not run while the user is not looking at the wallet. Should\n * be called after `NetworkController`, `NetworkEnablementController`, and\n * `ConnectivityController` have been initialized. Idempotent.\n */\nexport type NetworkConnectionBannerControllerStartAction = {\n type: `NetworkConnectionBannerController:start`;\n handler: NetworkConnectionBannerController['start'];\n};\n\n/**\n * Stops evaluating network connection state. Clears any pending banner\n * timers and resets state to `available`. Call this when the UI\n * consuming the banner is no longer active. Idempotent.\n */\nexport type NetworkConnectionBannerControllerStopAction = {\n type: `NetworkConnectionBannerController:stop`;\n handler: NetworkConnectionBannerController['stop'];\n};\n\n/**\n * Clears the banner state such that the banner will be hidden.\n */\nexport type NetworkConnectionBannerControllerDismissBannerAction = {\n type: `NetworkConnectionBannerController:dismissBanner`;\n handler: NetworkConnectionBannerController['dismissBanner'];\n};\n\n/**\n * Switches the chain's default RPC endpoint to its Infura endpoint,\n * causing the banner to clear once the network becomes available again.\n *\n * @param chainId - The chain whose default RPC endpoint should be switched.\n * @throws If the chain configuration cannot be found, or if it has no\n * Infura endpoint to switch to, or if the default is already Infura.\n */\nexport type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction =\n {\n type: `NetworkConnectionBannerController:switchToDefaultInfuraRpcEndpoint`;\n handler: NetworkConnectionBannerController['switchToDefaultInfuraRpcEndpoint'];\n };\n\n/**\n * Union of all NetworkConnectionBannerController action types.\n */\nexport type NetworkConnectionBannerControllerMethodActions =\n | NetworkConnectionBannerControllerStartAction\n | NetworkConnectionBannerControllerStopAction\n | NetworkConnectionBannerControllerDismissBannerAction\n | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;\n"]}
@@ -4,14 +4,27 @@
4
4
  */
5
5
  import type { NetworkConnectionBannerController } from "./NetworkConnectionBannerController.cjs";
6
6
  /**
7
- * Starts evaluating network connection state.
7
+ * Look for a failed network, if any, and populate the initial state of the
8
+ * banner. Reacts to upstream state changes from this point on.
8
9
  *
9
- * This method should be called after the upstream network, network
10
- * enablement, and connectivity controllers have been initialized.
10
+ * Call this when the wallet UI that consumes the banner becomes active
11
+ * (typically when the wallet is unlocked and the home surface mounts) so
12
+ * timers do not run while the user is not looking at the wallet. Should
13
+ * be called after `NetworkController`, `NetworkEnablementController`, and
14
+ * `ConnectivityController` have been initialized. Idempotent.
11
15
  */
12
- export type NetworkConnectionBannerControllerInitAction = {
13
- type: `NetworkConnectionBannerController:init`;
14
- handler: NetworkConnectionBannerController['init'];
16
+ export type NetworkConnectionBannerControllerStartAction = {
17
+ type: `NetworkConnectionBannerController:start`;
18
+ handler: NetworkConnectionBannerController['start'];
19
+ };
20
+ /**
21
+ * Stops evaluating network connection state. Clears any pending banner
22
+ * timers and resets state to `available`. Call this when the UI
23
+ * consuming the banner is no longer active. Idempotent.
24
+ */
25
+ export type NetworkConnectionBannerControllerStopAction = {
26
+ type: `NetworkConnectionBannerController:stop`;
27
+ handler: NetworkConnectionBannerController['stop'];
15
28
  };
16
29
  /**
17
30
  * Clears the banner state such that the banner will be hidden.
@@ -21,20 +34,19 @@ export type NetworkConnectionBannerControllerDismissBannerAction = {
21
34
  handler: NetworkConnectionBannerController['dismissBanner'];
22
35
  };
23
36
  /**
24
- * Switches the chain's default RPC endpoint to its first Infura endpoint,
37
+ * Switches the chain's default RPC endpoint to its Infura endpoint,
25
38
  * causing the banner to clear once the network becomes available again.
26
39
  *
27
- * @param args - The arguments to this action.
28
- * @param args.chainId - The chain whose default RPC should be switched.
40
+ * @param chainId - The chain whose default RPC endpoint should be switched.
29
41
  * @throws If the chain configuration cannot be found, or if it has no
30
42
  * Infura endpoint to switch to, or if the default is already Infura.
31
43
  */
32
- export type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction = {
33
- type: `NetworkConnectionBannerController:switchToDefaultInfuraRpc`;
34
- handler: NetworkConnectionBannerController['switchToDefaultInfuraRpc'];
44
+ export type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction = {
45
+ type: `NetworkConnectionBannerController:switchToDefaultInfuraRpcEndpoint`;
46
+ handler: NetworkConnectionBannerController['switchToDefaultInfuraRpcEndpoint'];
35
47
  };
36
48
  /**
37
49
  * Union of all NetworkConnectionBannerController action types.
38
50
  */
39
- export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerInitAction | NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction;
51
+ export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerStartAction | NetworkConnectionBannerControllerStopAction | NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;
40
52
  //# sourceMappingURL=NetworkConnectionBannerController-method-action-types.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController-method-action-types.d.cts","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iCAAiC,EAAE,gDAA4C;AAE7F;;;;;GAKG;AACH,MAAM,MAAM,2CAA2C,GAAG;IACxD,IAAI,EAAE,wCAAwC,CAAC;IAC/C,OAAO,EAAE,iCAAiC,CAAC,MAAM,CAAC,CAAC;CACpD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oDAAoD,GAAG;IACjE,IAAI,EAAE,iDAAiD,CAAC;IACxD,OAAO,EAAE,iCAAiC,CAAC,eAAe,CAAC,CAAC;CAC7D,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,+DAA+D,GAAG;IAC5E,IAAI,EAAE,4DAA4D,CAAC;IACnE,OAAO,EAAE,iCAAiC,CAAC,0BAA0B,CAAC,CAAC;CACxE,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,8CAA8C,GACtD,2CAA2C,GAC3C,oDAAoD,GACpD,+DAA+D,CAAC"}
1
+ {"version":3,"file":"NetworkConnectionBannerController-method-action-types.d.cts","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iCAAiC,EAAE,gDAA4C;AAE7F;;;;;;;;;GASG;AACH,MAAM,MAAM,4CAA4C,GAAG;IACzD,IAAI,EAAE,yCAAyC,CAAC;IAChD,OAAO,EAAE,iCAAiC,CAAC,OAAO,CAAC,CAAC;CACrD,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,2CAA2C,GAAG;IACxD,IAAI,EAAE,wCAAwC,CAAC;IAC/C,OAAO,EAAE,iCAAiC,CAAC,MAAM,CAAC,CAAC;CACpD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oDAAoD,GAAG;IACjE,IAAI,EAAE,iDAAiD,CAAC;IACxD,OAAO,EAAE,iCAAiC,CAAC,eAAe,CAAC,CAAC;CAC7D,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,uEAAuE,GACjF;IACE,IAAI,EAAE,oEAAoE,CAAC;IAC3E,OAAO,EAAE,iCAAiC,CAAC,kCAAkC,CAAC,CAAC;CAChF,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,8CAA8C,GACtD,4CAA4C,GAC5C,2CAA2C,GAC3C,oDAAoD,GACpD,uEAAuE,CAAC"}
@@ -4,14 +4,27 @@
4
4
  */
5
5
  import type { NetworkConnectionBannerController } from "./NetworkConnectionBannerController.mjs";
6
6
  /**
7
- * Starts evaluating network connection state.
7
+ * Look for a failed network, if any, and populate the initial state of the
8
+ * banner. Reacts to upstream state changes from this point on.
8
9
  *
9
- * This method should be called after the upstream network, network
10
- * enablement, and connectivity controllers have been initialized.
10
+ * Call this when the wallet UI that consumes the banner becomes active
11
+ * (typically when the wallet is unlocked and the home surface mounts) so
12
+ * timers do not run while the user is not looking at the wallet. Should
13
+ * be called after `NetworkController`, `NetworkEnablementController`, and
14
+ * `ConnectivityController` have been initialized. Idempotent.
11
15
  */
12
- export type NetworkConnectionBannerControllerInitAction = {
13
- type: `NetworkConnectionBannerController:init`;
14
- handler: NetworkConnectionBannerController['init'];
16
+ export type NetworkConnectionBannerControllerStartAction = {
17
+ type: `NetworkConnectionBannerController:start`;
18
+ handler: NetworkConnectionBannerController['start'];
19
+ };
20
+ /**
21
+ * Stops evaluating network connection state. Clears any pending banner
22
+ * timers and resets state to `available`. Call this when the UI
23
+ * consuming the banner is no longer active. Idempotent.
24
+ */
25
+ export type NetworkConnectionBannerControllerStopAction = {
26
+ type: `NetworkConnectionBannerController:stop`;
27
+ handler: NetworkConnectionBannerController['stop'];
15
28
  };
16
29
  /**
17
30
  * Clears the banner state such that the banner will be hidden.
@@ -21,20 +34,19 @@ export type NetworkConnectionBannerControllerDismissBannerAction = {
21
34
  handler: NetworkConnectionBannerController['dismissBanner'];
22
35
  };
23
36
  /**
24
- * Switches the chain's default RPC endpoint to its first Infura endpoint,
37
+ * Switches the chain's default RPC endpoint to its Infura endpoint,
25
38
  * causing the banner to clear once the network becomes available again.
26
39
  *
27
- * @param args - The arguments to this action.
28
- * @param args.chainId - The chain whose default RPC should be switched.
40
+ * @param chainId - The chain whose default RPC endpoint should be switched.
29
41
  * @throws If the chain configuration cannot be found, or if it has no
30
42
  * Infura endpoint to switch to, or if the default is already Infura.
31
43
  */
32
- export type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction = {
33
- type: `NetworkConnectionBannerController:switchToDefaultInfuraRpc`;
34
- handler: NetworkConnectionBannerController['switchToDefaultInfuraRpc'];
44
+ export type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction = {
45
+ type: `NetworkConnectionBannerController:switchToDefaultInfuraRpcEndpoint`;
46
+ handler: NetworkConnectionBannerController['switchToDefaultInfuraRpcEndpoint'];
35
47
  };
36
48
  /**
37
49
  * Union of all NetworkConnectionBannerController action types.
38
50
  */
39
- export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerInitAction | NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction;
51
+ export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerStartAction | NetworkConnectionBannerControllerStopAction | NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;
40
52
  //# sourceMappingURL=NetworkConnectionBannerController-method-action-types.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController-method-action-types.d.mts","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iCAAiC,EAAE,gDAA4C;AAE7F;;;;;GAKG;AACH,MAAM,MAAM,2CAA2C,GAAG;IACxD,IAAI,EAAE,wCAAwC,CAAC;IAC/C,OAAO,EAAE,iCAAiC,CAAC,MAAM,CAAC,CAAC;CACpD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oDAAoD,GAAG;IACjE,IAAI,EAAE,iDAAiD,CAAC;IACxD,OAAO,EAAE,iCAAiC,CAAC,eAAe,CAAC,CAAC;CAC7D,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,+DAA+D,GAAG;IAC5E,IAAI,EAAE,4DAA4D,CAAC;IACnE,OAAO,EAAE,iCAAiC,CAAC,0BAA0B,CAAC,CAAC;CACxE,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,8CAA8C,GACtD,2CAA2C,GAC3C,oDAAoD,GACpD,+DAA+D,CAAC"}
1
+ {"version":3,"file":"NetworkConnectionBannerController-method-action-types.d.mts","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iCAAiC,EAAE,gDAA4C;AAE7F;;;;;;;;;GASG;AACH,MAAM,MAAM,4CAA4C,GAAG;IACzD,IAAI,EAAE,yCAAyC,CAAC;IAChD,OAAO,EAAE,iCAAiC,CAAC,OAAO,CAAC,CAAC;CACrD,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,2CAA2C,GAAG;IACxD,IAAI,EAAE,wCAAwC,CAAC;IAC/C,OAAO,EAAE,iCAAiC,CAAC,MAAM,CAAC,CAAC;CACpD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oDAAoD,GAAG;IACjE,IAAI,EAAE,iDAAiD,CAAC;IACxD,OAAO,EAAE,iCAAiC,CAAC,eAAe,CAAC,CAAC;CAC7D,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,uEAAuE,GACjF;IACE,IAAI,EAAE,oEAAoE,CAAC;IAC3E,OAAO,EAAE,iCAAiC,CAAC,kCAAkC,CAAC,CAAC;CAChF,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,8CAA8C,GACtD,4CAA4C,GAC5C,2CAA2C,GAC3C,oDAAoD,GACpD,uEAAuE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController-method-action-types.mjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { NetworkConnectionBannerController } from './NetworkConnectionBannerController';\n\n/**\n * Starts evaluating network connection state.\n *\n * This method should be called after the upstream network, network\n * enablement, and connectivity controllers have been initialized.\n */\nexport type NetworkConnectionBannerControllerInitAction = {\n type: `NetworkConnectionBannerController:init`;\n handler: NetworkConnectionBannerController['init'];\n};\n\n/**\n * Clears the banner state such that the banner will be hidden.\n */\nexport type NetworkConnectionBannerControllerDismissBannerAction = {\n type: `NetworkConnectionBannerController:dismissBanner`;\n handler: NetworkConnectionBannerController['dismissBanner'];\n};\n\n/**\n * Switches the chain's default RPC endpoint to its first Infura endpoint,\n * causing the banner to clear once the network becomes available again.\n *\n * @param args - The arguments to this action.\n * @param args.chainId - The chain whose default RPC should be switched.\n * @throws If the chain configuration cannot be found, or if it has no\n * Infura endpoint to switch to, or if the default is already Infura.\n */\nexport type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction = {\n type: `NetworkConnectionBannerController:switchToDefaultInfuraRpc`;\n handler: NetworkConnectionBannerController['switchToDefaultInfuraRpc'];\n};\n\n/**\n * Union of all NetworkConnectionBannerController action types.\n */\nexport type NetworkConnectionBannerControllerMethodActions =\n | NetworkConnectionBannerControllerInitAction\n | NetworkConnectionBannerControllerDismissBannerAction\n | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction;\n"]}
1
+ {"version":3,"file":"NetworkConnectionBannerController-method-action-types.mjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { NetworkConnectionBannerController } from './NetworkConnectionBannerController';\n\n/**\n * Look for a failed network, if any, and populate the initial state of the\n * banner. Reacts to upstream state changes from this point on.\n *\n * Call this when the wallet UI that consumes the banner becomes active\n * (typically when the wallet is unlocked and the home surface mounts) so\n * timers do not run while the user is not looking at the wallet. Should\n * be called after `NetworkController`, `NetworkEnablementController`, and\n * `ConnectivityController` have been initialized. Idempotent.\n */\nexport type NetworkConnectionBannerControllerStartAction = {\n type: `NetworkConnectionBannerController:start`;\n handler: NetworkConnectionBannerController['start'];\n};\n\n/**\n * Stops evaluating network connection state. Clears any pending banner\n * timers and resets state to `available`. Call this when the UI\n * consuming the banner is no longer active. Idempotent.\n */\nexport type NetworkConnectionBannerControllerStopAction = {\n type: `NetworkConnectionBannerController:stop`;\n handler: NetworkConnectionBannerController['stop'];\n};\n\n/**\n * Clears the banner state such that the banner will be hidden.\n */\nexport type NetworkConnectionBannerControllerDismissBannerAction = {\n type: `NetworkConnectionBannerController:dismissBanner`;\n handler: NetworkConnectionBannerController['dismissBanner'];\n};\n\n/**\n * Switches the chain's default RPC endpoint to its Infura endpoint,\n * causing the banner to clear once the network becomes available again.\n *\n * @param chainId - The chain whose default RPC endpoint should be switched.\n * @throws If the chain configuration cannot be found, or if it has no\n * Infura endpoint to switch to, or if the default is already Infura.\n */\nexport type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction =\n {\n type: `NetworkConnectionBannerController:switchToDefaultInfuraRpcEndpoint`;\n handler: NetworkConnectionBannerController['switchToDefaultInfuraRpcEndpoint'];\n };\n\n/**\n * Union of all NetworkConnectionBannerController action types.\n */\nexport type NetworkConnectionBannerControllerMethodActions =\n | NetworkConnectionBannerControllerStartAction\n | NetworkConnectionBannerControllerStopAction\n | NetworkConnectionBannerControllerDismissBannerAction\n | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;\n"]}
@@ -10,7 +10,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
11
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
12
  };
13
- var _NetworkConnectionBannerController_instances, _NetworkConnectionBannerController_degradedTimer, _NetworkConnectionBannerController_unavailableTimer, _NetworkConnectionBannerController_pendingNetworkClientId, _NetworkConnectionBannerController_initialized, _NetworkConnectionBannerController_onUpstreamChange, _NetworkConnectionBannerController_refreshState, _NetworkConnectionBannerController_resetBanner, _NetworkConnectionBannerController_clearTimers, _NetworkConnectionBannerController_findFailedNetwork, _NetworkConnectionBannerController_getEnabledEvmChainIds, _NetworkConnectionBannerController_collectNetworksWithMetadata, _NetworkConnectionBannerController_buildFailedNetwork, _NetworkConnectionBannerController_pickBannerNetwork;
13
+ var _NetworkConnectionBannerController_instances, _NetworkConnectionBannerController_degradedTimer, _NetworkConnectionBannerController_unavailableTimer, _NetworkConnectionBannerController_pendingNetworkClientId, _NetworkConnectionBannerController_isStarted, _NetworkConnectionBannerController_refreshState, _NetworkConnectionBannerController_resetBanner, _NetworkConnectionBannerController_clearTimers, _NetworkConnectionBannerController_findFailedNetwork, _NetworkConnectionBannerController_getEnabledEvmChainIds, _NetworkConnectionBannerController_collectNetworksWithMetadata, _NetworkConnectionBannerController_buildFailedNetwork, _NetworkConnectionBannerController_pickBannerNetwork;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.NetworkConnectionBannerController = exports.getDefaultNetworkConnectionBannerControllerState = void 0;
16
16
  const base_controller_1 = require("@metamask/base-controller");
@@ -20,6 +20,12 @@ const network_enablement_controller_1 = require("@metamask/network-enablement-co
20
20
  const utils_1 = require("@metamask/utils");
21
21
  const reselect_1 = require("reselect");
22
22
  const url_utils_1 = require("./url-utils.cjs");
23
+ /**
24
+ * The name of the {@link NetworkConnectionBannerController}, used to namespace
25
+ * the controller's actions and events and to namespace the controller's state
26
+ * data when composed with other controllers.
27
+ */
28
+ const CONTROLLER_NAME = 'NetworkConnectionBannerController';
23
29
  /**
24
30
  * Selects `networksMetadata` from the `NetworkController` state.
25
31
  *
@@ -49,11 +55,21 @@ const selectNetworkControllerFields = (0, reselect_1.createSelector)([selectNetw
49
55
  networkConfigurationsByChainId,
50
56
  }));
51
57
  /**
52
- * The name of the {@link NetworkConnectionBannerController}, used to namespace
53
- * the controller's actions and events and to namespace the controller's state
54
- * data when composed with other controllers.
58
+ * Selects the `NetworkEnablementController` state field that influences the
59
+ * banner rule.
60
+ *
61
+ * @param state - The `NetworkEnablementController` state.
62
+ * @returns The relevant enablement fields.
63
+ */
64
+ const selectNetworkEnablementControllerFields = (0, reselect_1.createSelector)([network_enablement_controller_1.selectEnabledNetworkMap], (enabledNetworkMap) => ({ enabledNetworkMap }));
65
+ /**
66
+ * Selects the `ConnectivityController` state field that influences the
67
+ * banner rule.
68
+ *
69
+ * @param state - The `ConnectivityController` state.
70
+ * @returns The relevant connectivity fields.
55
71
  */
56
- const controllerName = 'NetworkConnectionBannerController';
72
+ const selectConnectivityControllerFields = (0, reselect_1.createSelector)([connectivity_controller_1.connectivityControllerSelectors.selectConnectivityStatus], (connectivityStatus) => ({ connectivityStatus }));
57
73
  const networkConnectionBannerControllerMetadata = {
58
74
  status: {
59
75
  persist: false,
@@ -91,9 +107,10 @@ const DEGRADED_BANNER_TIMEOUT = 5000;
91
107
  */
92
108
  const UNAVAILABLE_BANNER_TIMEOUT = 30000;
93
109
  const MESSENGER_EXPOSED_METHODS = [
94
- 'init',
110
+ 'start',
111
+ 'stop',
95
112
  'dismissBanner',
96
- 'switchToDefaultInfuraRpc',
113
+ 'switchToDefaultInfuraRpcEndpoint',
97
114
  ];
98
115
  /**
99
116
  * NetworkConnectionBannerController decides whether the network connection
@@ -116,7 +133,7 @@ const MESSENGER_EXPOSED_METHODS = [
116
133
  * can act on.
117
134
  *
118
135
  * Clients only need to render the banner from the controller's state and wire
119
- * click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpc}.
136
+ * click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpcEndpoint}.
120
137
  */
121
138
  class NetworkConnectionBannerController extends base_controller_1.BaseController {
122
139
  /**
@@ -129,38 +146,51 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
129
146
  super({
130
147
  messenger,
131
148
  metadata: networkConnectionBannerControllerMetadata,
132
- name: controllerName,
149
+ name: CONTROLLER_NAME,
133
150
  state: getDefaultNetworkConnectionBannerControllerState(),
134
151
  });
135
152
  _NetworkConnectionBannerController_instances.add(this);
136
153
  _NetworkConnectionBannerController_degradedTimer.set(this, void 0);
137
154
  _NetworkConnectionBannerController_unavailableTimer.set(this, void 0);
138
155
  _NetworkConnectionBannerController_pendingNetworkClientId.set(this, void 0);
139
- _NetworkConnectionBannerController_initialized.set(this, false);
140
- // Scoped selectors per controller guideline so unrelated upstream
141
- // `stateChange` events (e.g. a `NetworkController` selected client id
142
- // update) do not trigger a re-evaluation.
156
+ _NetworkConnectionBannerController_isStarted.set(this, false);
143
157
  // Upstream controllers still expose :stateChange; switch to :stateChanged
144
158
  // once those packages migrate their event types.
145
159
  /* eslint-disable no-restricted-syntax -- awaiting upstream :stateChanged migration */
146
- this.messenger.subscribe('NetworkController:stateChange', () => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_onUpstreamChange).call(this), selectNetworkControllerFields);
147
- this.messenger.subscribe('NetworkEnablementController:stateChange', () => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_onUpstreamChange).call(this), network_enablement_controller_1.selectEnabledNetworkMap);
148
- this.messenger.subscribe('ConnectivityController:stateChange', () => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_onUpstreamChange).call(this), connectivity_controller_1.connectivityControllerSelectors.selectConnectivityStatus);
160
+ this.messenger.subscribe('NetworkController:stateChange', (networkControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, { networkControllerState }), selectNetworkControllerFields);
161
+ this.messenger.subscribe('NetworkEnablementController:stateChange', (networkEnablementControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, { networkEnablementControllerState }), selectNetworkEnablementControllerFields);
162
+ this.messenger.subscribe('ConnectivityController:stateChange', (connectivityControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, { connectivityControllerState }), selectConnectivityControllerFields);
149
163
  /* eslint-enable no-restricted-syntax */
150
164
  this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
151
165
  }
152
166
  /**
153
- * Starts evaluating network connection state.
167
+ * Look for a failed network, if any, and populate the initial state of the
168
+ * banner. Reacts to upstream state changes from this point on.
154
169
  *
155
- * This method should be called after the upstream network, network
156
- * enablement, and connectivity controllers have been initialized.
170
+ * Call this when the wallet UI that consumes the banner becomes active
171
+ * (typically when the wallet is unlocked and the home surface mounts) so
172
+ * timers do not run while the user is not looking at the wallet. Should
173
+ * be called after `NetworkController`, `NetworkEnablementController`, and
174
+ * `ConnectivityController` have been initialized. Idempotent.
157
175
  */
158
- init() {
159
- if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_initialized, "f")) {
176
+ start() {
177
+ if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
160
178
  return;
161
179
  }
180
+ __classPrivateFieldSet(this, _NetworkConnectionBannerController_isStarted, true, "f");
162
181
  __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this);
163
- __classPrivateFieldSet(this, _NetworkConnectionBannerController_initialized, true, "f");
182
+ }
183
+ /**
184
+ * Stops evaluating network connection state. Clears any pending banner
185
+ * timers and resets state to `available`. Call this when the UI
186
+ * consuming the banner is no longer active. Idempotent.
187
+ */
188
+ stop() {
189
+ if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
190
+ return;
191
+ }
192
+ __classPrivateFieldSet(this, _NetworkConnectionBannerController_isStarted, false, "f");
193
+ __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
164
194
  }
165
195
  /**
166
196
  * Clears the banner state such that the banner will be hidden.
@@ -169,15 +199,14 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
169
199
  __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
170
200
  }
171
201
  /**
172
- * Switches the chain's default RPC endpoint to its first Infura endpoint,
202
+ * Switches the chain's default RPC endpoint to its Infura endpoint,
173
203
  * causing the banner to clear once the network becomes available again.
174
204
  *
175
- * @param args - The arguments to this action.
176
- * @param args.chainId - The chain whose default RPC should be switched.
205
+ * @param chainId - The chain whose default RPC endpoint should be switched.
177
206
  * @throws If the chain configuration cannot be found, or if it has no
178
207
  * Infura endpoint to switch to, or if the default is already Infura.
179
208
  */
180
- async switchToDefaultInfuraRpc({ chainId }) {
209
+ async switchToDefaultInfuraRpcEndpoint(chainId) {
181
210
  const networkConfiguration = this.messenger.call('NetworkController:getNetworkConfigurationByChainId', chainId);
182
211
  if (!networkConfiguration) {
183
212
  throw new Error(`No network configuration found for chain ID "${chainId}".`);
@@ -197,17 +226,21 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
197
226
  }
198
227
  }
199
228
  exports.NetworkConnectionBannerController = NetworkConnectionBannerController;
200
- _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnectionBannerController_unavailableTimer = new WeakMap(), _NetworkConnectionBannerController_pendingNetworkClientId = new WeakMap(), _NetworkConnectionBannerController_initialized = new WeakMap(), _NetworkConnectionBannerController_instances = new WeakSet(), _NetworkConnectionBannerController_onUpstreamChange = function _NetworkConnectionBannerController_onUpstreamChange() {
201
- if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_initialized, "f")) {
202
- __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this);
229
+ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnectionBannerController_unavailableTimer = new WeakMap(), _NetworkConnectionBannerController_pendingNetworkClientId = new WeakMap(), _NetworkConnectionBannerController_isStarted = new WeakMap(), _NetworkConnectionBannerController_instances = new WeakSet(), _NetworkConnectionBannerController_refreshState = function _NetworkConnectionBannerController_refreshState({ networkControllerState, networkEnablementControllerState, connectivityControllerState, } = {}) {
230
+ if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
231
+ return;
203
232
  }
204
- }, _NetworkConnectionBannerController_refreshState = function _NetworkConnectionBannerController_refreshState() {
205
- const { connectivityStatus } = this.messenger.call('ConnectivityController:getState');
233
+ const { connectivityStatus } = connectivityControllerState ??
234
+ this.messenger.call('ConnectivityController:getState');
206
235
  if (connectivityStatus === connectivity_controller_1.CONNECTIVITY_STATUSES.Offline) {
207
236
  __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
208
237
  return;
209
238
  }
210
- const failedNetwork = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_findFailedNetwork).call(this);
239
+ const networkState = networkControllerState ??
240
+ this.messenger.call('NetworkController:getState');
241
+ const enablementState = networkEnablementControllerState ??
242
+ this.messenger.call('NetworkEnablementController:getState');
243
+ const failedNetwork = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_findFailedNetwork).call(this, networkState, enablementState);
211
244
  if (!failedNetwork) {
212
245
  __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
213
246
  return;
@@ -227,6 +260,11 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
227
260
  state.status = 'available';
228
261
  state.network = null;
229
262
  });
263
+ // A synchronous listener on our `stateChanged` event above may have
264
+ // called `stop()` re-entrantly. Bail before scheduling anything.
265
+ if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
266
+ return;
267
+ }
230
268
  // Capture the failing network at schedule time. We trust the messenger
231
269
  // contract: if the failure resolves or the target changes during the
232
270
  // wait, our upstream subscriptions will have cancelled or replaced this
@@ -239,6 +277,11 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
239
277
  state.status = 'degraded';
240
278
  state.network = failedNetwork;
241
279
  });
280
+ // A synchronous listener on our `stateChanged` event above may have
281
+ // called `stop()` re-entrantly. Bail before scheduling the escalation.
282
+ if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
283
+ return;
284
+ }
242
285
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, setTimeout(() => {
243
286
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, undefined, "f");
244
287
  this.update((state) => {
@@ -265,20 +308,18 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
265
308
  clearTimeout(__classPrivateFieldGet(this, _NetworkConnectionBannerController_unavailableTimer, "f"));
266
309
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, undefined, "f");
267
310
  }
268
- }, _NetworkConnectionBannerController_findFailedNetwork = function _NetworkConnectionBannerController_findFailedNetwork() {
269
- const networksWithMetadata = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_collectNetworksWithMetadata).call(this);
311
+ }, _NetworkConnectionBannerController_findFailedNetwork = function _NetworkConnectionBannerController_findFailedNetwork(networkState, enablementState) {
312
+ const networksWithMetadata = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_collectNetworksWithMetadata).call(this, networkState, enablementState);
270
313
  const failedNetworks = networksWithMetadata
271
314
  .filter(({ metadata }) => metadata.status !== network_controller_1.NetworkStatus.Available)
272
315
  .map((network) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_buildFailedNetwork).call(this, network));
273
316
  return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_pickBannerNetwork).call(this, failedNetworks, networksWithMetadata.length);
274
- }, _NetworkConnectionBannerController_getEnabledEvmChainIds = function _NetworkConnectionBannerController_getEnabledEvmChainIds() {
275
- const { enabledNetworkMap } = this.messenger.call('NetworkEnablementController:getState');
317
+ }, _NetworkConnectionBannerController_getEnabledEvmChainIds = function _NetworkConnectionBannerController_getEnabledEvmChainIds(enabledNetworkMap) {
276
318
  return Object.entries(enabledNetworkMap[utils_1.KnownCaipNamespace.Eip155] ?? {})
277
319
  .filter(([, enabled]) => enabled)
278
320
  .map(([chainId]) => chainId);
279
- }, _NetworkConnectionBannerController_collectNetworksWithMetadata = function _NetworkConnectionBannerController_collectNetworksWithMetadata() {
280
- const { networkConfigurationsByChainId, networksMetadata } = this.messenger.call('NetworkController:getState');
281
- return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_getEnabledEvmChainIds).call(this).flatMap((chainId) => {
321
+ }, _NetworkConnectionBannerController_collectNetworksWithMetadata = function _NetworkConnectionBannerController_collectNetworksWithMetadata({ networkConfigurationsByChainId, networksMetadata, }, { enabledNetworkMap, }) {
322
+ return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_getEnabledEvmChainIds).call(this, enabledNetworkMap).flatMap((chainId) => {
282
323
  const networkConfiguration = networkConfigurationsByChainId[chainId];
283
324
  if (!networkConfiguration) {
284
325
  return [];
@@ -309,9 +350,9 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
309
350
  // chain that we could offer to switch to.
310
351
  let switchableInfuraNetworkClientId = null;
311
352
  if (!isInfuraEndpoint) {
312
- const otherInfura = rpcEndpoints.find((endpoint, index) => index !== defaultRpcEndpointIndex &&
353
+ const infuraEndpoint = rpcEndpoints.find((endpoint, index) => index !== defaultRpcEndpointIndex &&
313
354
  getIsInfuraEndpoint(endpoint.url));
314
- switchableInfuraNetworkClientId = otherInfura?.networkClientId ?? null;
355
+ switchableInfuraNetworkClientId = infuraEndpoint?.networkClientId ?? null;
315
356
  }
316
357
  return {
317
358
  chainId,
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController.cjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangedEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport {\n CONNECTIVITY_STATUSES,\n connectivityControllerSelectors,\n} from '@metamask/connectivity-controller';\nimport type {\n ConnectivityControllerGetStateAction,\n ConnectivityControllerStateChangeEvent,\n} from '@metamask/connectivity-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkConfiguration,\n NetworkControllerGetNetworkConfigurationByChainIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerUpdateNetworkAction,\n NetworkControllerStateChangeEvent,\n NetworkMetadata,\n NetworkState,\n} from '@metamask/network-controller';\nimport { NetworkStatus } from '@metamask/network-controller';\nimport type {\n NetworkEnablementControllerGetStateAction,\n NetworkEnablementControllerStateChangeEvent,\n} from '@metamask/network-enablement-controller';\nimport { selectEnabledNetworkMap } from '@metamask/network-enablement-controller';\nimport type { Hex } from '@metamask/utils';\nimport { KnownCaipNamespace } from '@metamask/utils';\nimport { createSelector } from 'reselect';\n\nimport type { NetworkConnectionBannerControllerMethodActions } from './NetworkConnectionBannerController-method-action-types';\nimport { getDomain } from './url-utils';\n\n/**\n * Selects `networksMetadata` from the `NetworkController` state.\n *\n * @param state - The `NetworkController` state.\n * @returns The networks metadata map keyed by network client id.\n */\nconst selectNetworksMetadata = (\n state: NetworkState,\n): NetworkState['networksMetadata'] => state.networksMetadata;\n\n/**\n * Selects `networkConfigurationsByChainId` from the `NetworkController`\n * state.\n *\n * @param state - The `NetworkController` state.\n * @returns The network configurations keyed by chain id.\n */\nconst selectNetworkConfigurationsByChainId = (\n state: NetworkState,\n): NetworkState['networkConfigurationsByChainId'] =>\n state.networkConfigurationsByChainId;\n\n/**\n * Selects the `NetworkController` state fields that influence the banner\n * rule. Composed with `createSelector` so the return object stays reference\n * stable while unrelated `NetworkController` state (e.g.\n * `selectedNetworkClientId`) changes.\n *\n * @param state - The `NetworkController` state.\n * @returns The relevant network fields.\n */\nconst selectNetworkControllerFields = createSelector(\n [selectNetworksMetadata, selectNetworkConfigurationsByChainId],\n (networksMetadata, networkConfigurationsByChainId) => ({\n networksMetadata,\n networkConfigurationsByChainId,\n }),\n);\n\n/**\n * The name of the {@link NetworkConnectionBannerController}, used to namespace\n * the controller's actions and events and to namespace the controller's state\n * data when composed with other controllers.\n */\nconst controllerName = 'NetworkConnectionBannerController';\n\n/**\n * Status the banner can be in. `available` means no banner is shown; the\n * `degraded` and `unavailable` values mirror the two-tier escalation that the\n * UI renders.\n */\nexport type NetworkConnectionBannerStatus =\n | 'available'\n | 'degraded'\n | 'unavailable';\n\n/**\n * Details of a failing network the banner describes.\n */\nexport type FailedNetwork = {\n chainId: Hex;\n networkClientId: string;\n name: string;\n rpcUrl: string;\n isInfuraEndpoint: boolean;\n /**\n * The networkClientId of an Infura endpoint on the same chain that the user\n * can switch to. `null` when the failing endpoint is already Infura or when\n * no Infura alternative exists.\n */\n switchableInfuraNetworkClientId: string | null;\n /**\n * The registrable domain (eTLD+1) of `rpcUrl`, used to group endpoints by\n * provider. `null` when the URL is invalid.\n */\n domain: string | null;\n};\n\n/**\n * State for the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerState = {\n status: NetworkConnectionBannerStatus;\n network: FailedNetwork | null;\n};\n\nconst networkConnectionBannerControllerMetadata = {\n status: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n network: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n} satisfies StateMetadata<NetworkConnectionBannerControllerState>;\n\n/**\n * Constructs the default {@link NetworkConnectionBannerController} state.\n *\n * @returns The default state.\n */\nexport function getDefaultNetworkConnectionBannerControllerState(): NetworkConnectionBannerControllerState {\n return {\n status: 'available',\n network: null,\n };\n}\n\n/**\n * How long (in milliseconds) a failing network must remain in a \"failed\"\n * status (\"degraded\" or \"unavailable\") before the degraded banner appears.\n */\nconst DEGRADED_BANNER_TIMEOUT = 5_000;\n\n/**\n * How long (in milliseconds) a failing network must remain in a \"failed\"\n * status before the banner escalates to \"unavailable\".\n */\nconst UNAVAILABLE_BANNER_TIMEOUT = 30_000;\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'init',\n 'dismissBanner',\n 'switchToDefaultInfuraRpc',\n] as const;\n\n/**\n * Retrieves the state of the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n NetworkConnectionBannerControllerState\n >;\n\n/**\n * Actions that {@link NetworkConnectionBannerControllerMessenger} exposes to\n * other consumers.\n */\nexport type NetworkConnectionBannerControllerActions =\n | NetworkConnectionBannerControllerGetStateAction\n | NetworkConnectionBannerControllerMethodActions;\n\n/**\n * Actions from other messengers that\n * {@link NetworkConnectionBannerControllerMessenger} calls.\n */\ntype AllowedActions =\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkConfigurationByChainIdAction\n | NetworkControllerUpdateNetworkAction\n | NetworkEnablementControllerGetStateAction\n | ConnectivityControllerGetStateAction;\n\n/**\n * Published when the state of {@link NetworkConnectionBannerController}\n * changes.\n */\nexport type NetworkConnectionBannerControllerStateChangedEvent =\n ControllerStateChangedEvent<\n typeof controllerName,\n NetworkConnectionBannerControllerState\n >;\n\n/**\n * Events that {@link NetworkConnectionBannerControllerMessenger} exposes to\n * other consumers.\n */\nexport type NetworkConnectionBannerControllerEvents =\n NetworkConnectionBannerControllerStateChangedEvent;\n\n/**\n * Events from other messengers that\n * {@link NetworkConnectionBannerControllerMessenger} subscribes to.\n */\ntype AllowedEvents =\n | NetworkControllerStateChangeEvent\n | NetworkEnablementControllerStateChangeEvent\n | ConnectivityControllerStateChangeEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerMessenger = Messenger<\n typeof controllerName,\n NetworkConnectionBannerControllerActions | AllowedActions,\n NetworkConnectionBannerControllerEvents | AllowedEvents\n>;\n\n/**\n * Options for constructing the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerOptions = {\n /**\n * The messenger for inter-controller communication.\n */\n messenger: NetworkConnectionBannerControllerMessenger;\n};\n\n/**\n * NetworkConnectionBannerController decides whether the network connection\n * banner should be shown for the user, and which failing network it should\n * describe. It encapsulates the rule, the 5s / 30s timer escalation, and the\n * eTLD+1 grouping that was previously duplicated across MetaMask clients.\n *\n * The banner shows when:\n *\n * - The first failing network's default RPC is a custom (non-Infura) endpoint\n * — users always want to be informed about errors with RPCs they've chosen.\n * - Failed RPCs span more than one registrable domain (likely client-side).\n * - Every enabled EVM network with known connectivity status is failing\n * (escape hatch for single-network setups so they still get a signal).\n *\n * A wide single-provider outage (e.g. every `*.infura.io` network goes down at\n * once) collapses to one domain and is suppressed, except in the all-down\n * single-network case. When a custom failure is present, it's surfaced first\n * so the \"Switch to MetaMask default RPC\" CTA targets the network the user\n * can act on.\n *\n * Clients only need to render the banner from the controller's state and wire\n * click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpc}.\n */\nexport class NetworkConnectionBannerController extends BaseController<\n typeof controllerName,\n NetworkConnectionBannerControllerState,\n NetworkConnectionBannerControllerMessenger\n> {\n #degradedTimer: ReturnType<typeof setTimeout> | undefined;\n\n #unavailableTimer: ReturnType<typeof setTimeout> | undefined;\n\n #pendingNetworkClientId: string | undefined;\n\n #initialized = false;\n\n /**\n * Constructs a new {@link NetworkConnectionBannerController}.\n *\n * @param args - The arguments to this controller.\n * @param args.messenger - The messenger suited for this controller.\n */\n constructor({ messenger }: NetworkConnectionBannerControllerOptions) {\n super({\n messenger,\n metadata: networkConnectionBannerControllerMetadata,\n name: controllerName,\n state: getDefaultNetworkConnectionBannerControllerState(),\n });\n\n // Scoped selectors per controller guideline so unrelated upstream\n // `stateChange` events (e.g. a `NetworkController` selected client id\n // update) do not trigger a re-evaluation.\n // Upstream controllers still expose :stateChange; switch to :stateChanged\n // once those packages migrate their event types.\n /* eslint-disable no-restricted-syntax -- awaiting upstream :stateChanged migration */\n this.messenger.subscribe(\n 'NetworkController:stateChange',\n () => this.#onUpstreamChange(),\n selectNetworkControllerFields,\n );\n this.messenger.subscribe(\n 'NetworkEnablementController:stateChange',\n () => this.#onUpstreamChange(),\n selectEnabledNetworkMap,\n );\n this.messenger.subscribe(\n 'ConnectivityController:stateChange',\n () => this.#onUpstreamChange(),\n connectivityControllerSelectors.selectConnectivityStatus,\n );\n /* eslint-enable no-restricted-syntax */\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Starts evaluating network connection state.\n *\n * This method should be called after the upstream network, network\n * enablement, and connectivity controllers have been initialized.\n */\n init(): void {\n if (this.#initialized) {\n return;\n }\n\n this.#refreshState();\n this.#initialized = true;\n }\n\n #onUpstreamChange(): void {\n if (this.#initialized) {\n this.#refreshState();\n }\n }\n\n /**\n * Clears the banner state such that the banner will be hidden.\n */\n dismissBanner(): void {\n this.#resetBanner();\n }\n\n /**\n * Switches the chain's default RPC endpoint to its first Infura endpoint,\n * causing the banner to clear once the network becomes available again.\n *\n * @param args - The arguments to this action.\n * @param args.chainId - The chain whose default RPC should be switched.\n * @throws If the chain configuration cannot be found, or if it has no\n * Infura endpoint to switch to, or if the default is already Infura.\n */\n async switchToDefaultInfuraRpc({ chainId }: { chainId: Hex }): Promise<void> {\n const networkConfiguration = this.messenger.call(\n 'NetworkController:getNetworkConfigurationByChainId',\n chainId,\n );\n if (!networkConfiguration) {\n throw new Error(\n `No network configuration found for chain ID \"${chainId}\".`,\n );\n }\n\n const infuraEndpointIndex = networkConfiguration.rpcEndpoints.findIndex(\n (endpoint) => getIsInfuraEndpoint(endpoint.url),\n );\n if (infuraEndpointIndex === -1) {\n throw new Error(\n `No Infura endpoint available for chain ID \"${chainId}\".`,\n );\n }\n if (infuraEndpointIndex === networkConfiguration.defaultRpcEndpointIndex) {\n // The default is already Infura; nothing to do.\n return;\n }\n\n await this.messenger.call(\n 'NetworkController:updateNetwork',\n chainId,\n {\n ...networkConfiguration,\n defaultRpcEndpointIndex: infuraEndpointIndex,\n },\n { replacementSelectedRpcEndpointIndex: infuraEndpointIndex },\n );\n }\n\n #refreshState(): void {\n const { connectivityStatus } = this.messenger.call(\n 'ConnectivityController:getState',\n );\n if (connectivityStatus === CONNECTIVITY_STATUSES.Offline) {\n this.#resetBanner();\n return;\n }\n\n const failedNetwork = this.#findFailedNetwork();\n\n if (!failedNetwork) {\n this.#resetBanner();\n return;\n }\n\n if (\n this.state.status !== 'available' &&\n this.state.network?.networkClientId === failedNetwork.networkClientId\n ) {\n this.update((state) => {\n state.network = failedNetwork;\n });\n return;\n }\n\n if (this.#pendingNetworkClientId === failedNetwork.networkClientId) {\n return;\n }\n\n this.#clearTimers();\n this.update((state) => {\n state.status = 'available';\n state.network = null;\n });\n\n // Capture the failing network at schedule time. We trust the messenger\n // contract: if the failure resolves or the target changes during the\n // wait, our upstream subscriptions will have cancelled or replaced this\n // timer via `#clearTimers` before it fires.\n this.#pendingNetworkClientId = failedNetwork.networkClientId;\n this.#degradedTimer = setTimeout(() => {\n this.#degradedTimer = undefined;\n this.#pendingNetworkClientId = undefined;\n this.update((state) => {\n state.status = 'degraded';\n state.network = failedNetwork;\n });\n this.#unavailableTimer = setTimeout(() => {\n this.#unavailableTimer = undefined;\n this.update((state) => {\n state.status = 'unavailable';\n state.network = failedNetwork;\n });\n }, UNAVAILABLE_BANNER_TIMEOUT - DEGRADED_BANNER_TIMEOUT);\n }, DEGRADED_BANNER_TIMEOUT);\n }\n\n /**\n * Clears timers and resets banner state to {@link NetworkConnectionBannerStatus|`available`}\n * if it isn't there already.\n */\n #resetBanner(): void {\n this.#clearTimers();\n this.#pendingNetworkClientId = undefined;\n if (this.state.status !== 'available' || this.state.network !== null) {\n this.update((state) => {\n state.status = 'available';\n state.network = null;\n });\n }\n }\n\n #clearTimers(): void {\n if (this.#degradedTimer !== undefined) {\n clearTimeout(this.#degradedTimer);\n this.#degradedTimer = undefined;\n }\n if (this.#unavailableTimer !== undefined) {\n clearTimeout(this.#unavailableTimer);\n this.#unavailableTimer = undefined;\n }\n }\n\n #findFailedNetwork(): FailedNetwork | null {\n const networksWithMetadata = this.#collectNetworksWithMetadata();\n const failedNetworks = networksWithMetadata\n .filter(({ metadata }) => metadata.status !== NetworkStatus.Available)\n .map((network) => this.#buildFailedNetwork(network));\n return this.#pickBannerNetwork(failedNetworks, networksWithMetadata.length);\n }\n\n #getEnabledEvmChainIds(): Hex[] {\n const { enabledNetworkMap } = this.messenger.call(\n 'NetworkEnablementController:getState',\n );\n return Object.entries(enabledNetworkMap[KnownCaipNamespace.Eip155] ?? {})\n .filter(([, enabled]) => enabled)\n .map(([chainId]) => chainId as Hex);\n }\n\n #collectNetworksWithMetadata(): NetworkWithMetadata[] {\n const { networkConfigurationsByChainId, networksMetadata } =\n this.messenger.call('NetworkController:getState');\n\n return this.#getEnabledEvmChainIds().flatMap((chainId) => {\n const networkConfiguration = networkConfigurationsByChainId[chainId];\n if (!networkConfiguration) {\n return [];\n }\n const { rpcEndpoints, defaultRpcEndpointIndex, name } =\n networkConfiguration;\n const defaultRpcEndpoint = rpcEndpoints[defaultRpcEndpointIndex];\n if (!defaultRpcEndpoint) {\n return [];\n }\n const metadata = networksMetadata[defaultRpcEndpoint.networkClientId];\n if (!metadata) {\n return [];\n }\n return [\n {\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n metadata,\n },\n ];\n });\n }\n\n #buildFailedNetwork({\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n }: NetworkWithMetadata): FailedNetwork {\n const isInfuraEndpoint = getIsInfuraEndpoint(defaultRpcEndpoint.url);\n\n // For custom endpoints (non-Infura), find an Infura endpoint on this\n // chain that we could offer to switch to.\n let switchableInfuraNetworkClientId: string | null = null;\n if (!isInfuraEndpoint) {\n const otherInfura = rpcEndpoints.find(\n (endpoint, index) =>\n index !== defaultRpcEndpointIndex &&\n getIsInfuraEndpoint(endpoint.url),\n );\n switchableInfuraNetworkClientId = otherInfura?.networkClientId ?? null;\n }\n\n return {\n chainId,\n networkClientId: defaultRpcEndpoint.networkClientId,\n name,\n rpcUrl: defaultRpcEndpoint.url,\n isInfuraEndpoint,\n switchableInfuraNetworkClientId,\n domain: getDomain(defaultRpcEndpoint.url),\n };\n }\n\n #pickBannerNetwork(\n failedNetworks: FailedNetwork[],\n totalNetworksWithMetadata: number,\n ): FailedNetwork | null {\n if (failedNetworks.length === 0) {\n return null;\n }\n\n const firstCustomFailed = failedNetworks.find(\n (entry) => !entry.isInfuraEndpoint,\n );\n const distinctDomains = new Set(\n failedNetworks\n .map((entry) => entry.domain)\n .filter((domain): domain is string => domain !== null),\n ).size;\n const areAllKnownNetworksFailed =\n failedNetworks.length === totalNetworksWithMetadata;\n\n if (\n !firstCustomFailed &&\n distinctDomains <= 1 &&\n !areAllKnownNetworksFailed\n ) {\n return null;\n }\n\n return firstCustomFailed ?? failedNetworks[0];\n }\n}\n\ntype NetworkWithMetadata = {\n chainId: Hex;\n name: string;\n rpcEndpoints: NetworkConfiguration['rpcEndpoints'];\n defaultRpcEndpointIndex: number;\n defaultRpcEndpoint: NetworkConfiguration['rpcEndpoints'][number];\n metadata: NetworkMetadata;\n};\n\n/**\n * Whether an RPC URL is a MetaMask Infura endpoint. Matches the\n * `{infuraProjectId}` placeholder form that lives on stored network\n * configurations before the wallet substitutes the real project id at\n * request time. URLs that already carry a substituted key are treated as\n * custom (we do not have the project id in this package).\n *\n * @param url - The RPC URL to check.\n * @returns True if the URL is the placeholder form of a MetaMask Infura\n * endpoint.\n */\nfunction getIsInfuraEndpoint(url: string): boolean {\n return /^https:\\/\\/[^./]+\\.infura\\.io\\/v3\\/\\{infuraProjectId\\}$/u.test(url);\n}\n"]}
1
+ {"version":3,"file":"NetworkConnectionBannerController.cjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangedEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport {\n CONNECTIVITY_STATUSES,\n connectivityControllerSelectors,\n} from '@metamask/connectivity-controller';\nimport type {\n ConnectivityControllerGetStateAction,\n ConnectivityControllerState,\n ConnectivityControllerStateChangeEvent,\n} from '@metamask/connectivity-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkConfiguration,\n NetworkControllerGetNetworkConfigurationByChainIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerUpdateNetworkAction,\n NetworkControllerStateChangeEvent,\n NetworkMetadata,\n NetworkState,\n} from '@metamask/network-controller';\nimport { NetworkStatus } from '@metamask/network-controller';\nimport type {\n NetworkEnablementControllerGetStateAction,\n NetworkEnablementControllerState,\n NetworkEnablementControllerStateChangeEvent,\n} from '@metamask/network-enablement-controller';\nimport { selectEnabledNetworkMap } from '@metamask/network-enablement-controller';\nimport type { Hex } from '@metamask/utils';\nimport { KnownCaipNamespace } from '@metamask/utils';\nimport { createSelector } from 'reselect';\n\nimport type { NetworkConnectionBannerControllerMethodActions } from './NetworkConnectionBannerController-method-action-types';\nimport { getDomain } from './url-utils';\n\n/**\n * The name of the {@link NetworkConnectionBannerController}, used to namespace\n * the controller's actions and events and to namespace the controller's state\n * data when composed with other controllers.\n */\nconst CONTROLLER_NAME = 'NetworkConnectionBannerController';\n\n/**\n * Selects `networksMetadata` from the `NetworkController` state.\n *\n * @param state - The `NetworkController` state.\n * @returns The networks metadata map keyed by network client id.\n */\nconst selectNetworksMetadata = (\n state: NetworkState,\n): NetworkState['networksMetadata'] => state.networksMetadata;\n\n/**\n * Selects `networkConfigurationsByChainId` from the `NetworkController`\n * state.\n *\n * @param state - The `NetworkController` state.\n * @returns The network configurations keyed by chain id.\n */\nconst selectNetworkConfigurationsByChainId = (\n state: NetworkState,\n): NetworkState['networkConfigurationsByChainId'] =>\n state.networkConfigurationsByChainId;\n\n/**\n * Selects the `NetworkController` state fields that influence the banner\n * rule. Composed with `createSelector` so the return object stays reference\n * stable while unrelated `NetworkController` state (e.g.\n * `selectedNetworkClientId`) changes.\n *\n * @param state - The `NetworkController` state.\n * @returns The relevant network fields.\n */\nconst selectNetworkControllerFields = createSelector(\n [selectNetworksMetadata, selectNetworkConfigurationsByChainId],\n (networksMetadata, networkConfigurationsByChainId) => ({\n networksMetadata,\n networkConfigurationsByChainId,\n }),\n);\n\n/**\n * Selects the `NetworkEnablementController` state field that influences the\n * banner rule.\n *\n * @param state - The `NetworkEnablementController` state.\n * @returns The relevant enablement fields.\n */\nconst selectNetworkEnablementControllerFields = createSelector(\n [selectEnabledNetworkMap],\n (enabledNetworkMap) => ({ enabledNetworkMap }),\n);\n\n/**\n * Selects the `ConnectivityController` state field that influences the\n * banner rule.\n *\n * @param state - The `ConnectivityController` state.\n * @returns The relevant connectivity fields.\n */\nconst selectConnectivityControllerFields = createSelector(\n [connectivityControllerSelectors.selectConnectivityStatus],\n (connectivityStatus) => ({ connectivityStatus }),\n);\n\n/**\n * Status the banner can be in. `available` means no banner is shown; the\n * `degraded` and `unavailable` values mirror the two-tier escalation that the\n * UI renders.\n */\nexport type NetworkConnectionBannerStatus =\n | 'available'\n | 'degraded'\n | 'unavailable';\n\n/**\n * A network from `NetworkController` state that has a default RPC endpoint\n * with a known metadata status. Used as the input to the failed-network\n * detection pipeline.\n */\ntype NetworkWithMetadata = {\n chainId: Hex;\n name: string;\n rpcEndpoints: NetworkConfiguration['rpcEndpoints'];\n defaultRpcEndpointIndex: number;\n defaultRpcEndpoint: NetworkConfiguration['rpcEndpoints'][number];\n metadata: NetworkMetadata;\n};\n\n/**\n * Details of a failing network the banner describes.\n */\nexport type FailedNetwork = {\n /** The chain id of the failing network. */\n chainId: Hex;\n /** The `networkClientId` of the failing default RPC endpoint. */\n networkClientId: string;\n /** The display name for the failing network. */\n name: string;\n /** The URL of the failing default RPC endpoint. */\n rpcUrl: string;\n /** Whether the failing endpoint is a MetaMask Infura endpoint. */\n isInfuraEndpoint: boolean;\n /**\n * The networkClientId of an Infura endpoint on the same chain that the user\n * can switch to. `null` when the failing endpoint is already Infura or when\n * no Infura alternative exists.\n */\n switchableInfuraNetworkClientId: string | null;\n /**\n * The registrable domain (eTLD+1) of `rpcUrl`, used to group endpoints by\n * provider. `null` when the URL is invalid.\n */\n domain: string | null;\n};\n\n/**\n * State for the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerState = {\n status: NetworkConnectionBannerStatus;\n network: FailedNetwork | null;\n};\n\nconst networkConnectionBannerControllerMetadata = {\n status: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n network: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n} satisfies StateMetadata<NetworkConnectionBannerControllerState>;\n\n/**\n * Constructs the default {@link NetworkConnectionBannerController} state.\n *\n * @returns The default state.\n */\nexport function getDefaultNetworkConnectionBannerControllerState(): NetworkConnectionBannerControllerState {\n return {\n status: 'available',\n network: null,\n };\n}\n\n/**\n * How long (in milliseconds) a failing network must remain in a \"failed\"\n * status (\"degraded\" or \"unavailable\") before the degraded banner appears.\n */\nconst DEGRADED_BANNER_TIMEOUT = 5_000;\n\n/**\n * How long (in milliseconds) a failing network must remain in a \"failed\"\n * status before the banner escalates to \"unavailable\".\n */\nconst UNAVAILABLE_BANNER_TIMEOUT = 30_000;\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'start',\n 'stop',\n 'dismissBanner',\n 'switchToDefaultInfuraRpcEndpoint',\n] as const;\n\n/**\n * Retrieves the state of the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerGetStateAction =\n ControllerGetStateAction<\n typeof CONTROLLER_NAME,\n NetworkConnectionBannerControllerState\n >;\n\n/**\n * Actions that {@link NetworkConnectionBannerControllerMessenger} exposes to\n * other consumers.\n */\nexport type NetworkConnectionBannerControllerActions =\n | NetworkConnectionBannerControllerGetStateAction\n | NetworkConnectionBannerControllerMethodActions;\n\n/**\n * Actions from other messengers that\n * {@link NetworkConnectionBannerControllerMessenger} calls.\n */\ntype AllowedActions =\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkConfigurationByChainIdAction\n | NetworkControllerUpdateNetworkAction\n | NetworkEnablementControllerGetStateAction\n | ConnectivityControllerGetStateAction;\n\n/**\n * Published when the state of {@link NetworkConnectionBannerController}\n * changes.\n */\nexport type NetworkConnectionBannerControllerStateChangedEvent =\n ControllerStateChangedEvent<\n typeof CONTROLLER_NAME,\n NetworkConnectionBannerControllerState\n >;\n\n/**\n * Events that {@link NetworkConnectionBannerControllerMessenger} exposes to\n * other consumers.\n */\nexport type NetworkConnectionBannerControllerEvents =\n NetworkConnectionBannerControllerStateChangedEvent;\n\n/**\n * Events from other messengers that\n * {@link NetworkConnectionBannerControllerMessenger} subscribes to.\n */\ntype AllowedEvents =\n | NetworkControllerStateChangeEvent\n | NetworkEnablementControllerStateChangeEvent\n | ConnectivityControllerStateChangeEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerMessenger = Messenger<\n typeof CONTROLLER_NAME,\n NetworkConnectionBannerControllerActions | AllowedActions,\n NetworkConnectionBannerControllerEvents | AllowedEvents\n>;\n\n/**\n * Options for constructing the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerOptions = {\n /**\n * The messenger for inter-controller communication.\n */\n messenger: NetworkConnectionBannerControllerMessenger;\n};\n\n/**\n * NetworkConnectionBannerController decides whether the network connection\n * banner should be shown for the user, and which failing network it should\n * describe. It encapsulates the rule, the 5s / 30s timer escalation, and the\n * eTLD+1 grouping that was previously duplicated across MetaMask clients.\n *\n * The banner shows when:\n *\n * - The first failing network's default RPC is a custom (non-Infura) endpoint\n * — users always want to be informed about errors with RPCs they've chosen.\n * - Failed RPCs span more than one registrable domain (likely client-side).\n * - Every enabled EVM network with known connectivity status is failing\n * (escape hatch for single-network setups so they still get a signal).\n *\n * A wide single-provider outage (e.g. every `*.infura.io` network goes down at\n * once) collapses to one domain and is suppressed, except in the all-down\n * single-network case. When a custom failure is present, it's surfaced first\n * so the \"Switch to MetaMask default RPC\" CTA targets the network the user\n * can act on.\n *\n * Clients only need to render the banner from the controller's state and wire\n * click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpcEndpoint}.\n */\nexport class NetworkConnectionBannerController extends BaseController<\n typeof CONTROLLER_NAME,\n NetworkConnectionBannerControllerState,\n NetworkConnectionBannerControllerMessenger\n> {\n #degradedTimer: ReturnType<typeof setTimeout> | undefined;\n\n #unavailableTimer: ReturnType<typeof setTimeout> | undefined;\n\n #pendingNetworkClientId: string | undefined;\n\n #isStarted = false;\n\n /**\n * Constructs a new {@link NetworkConnectionBannerController}.\n *\n * @param args - The arguments to this controller.\n * @param args.messenger - The messenger suited for this controller.\n */\n constructor({ messenger }: NetworkConnectionBannerControllerOptions) {\n super({\n messenger,\n metadata: networkConnectionBannerControllerMetadata,\n name: CONTROLLER_NAME,\n state: getDefaultNetworkConnectionBannerControllerState(),\n });\n\n // Upstream controllers still expose :stateChange; switch to :stateChanged\n // once those packages migrate their event types.\n /* eslint-disable no-restricted-syntax -- awaiting upstream :stateChanged migration */\n this.messenger.subscribe(\n 'NetworkController:stateChange',\n (networkControllerState) =>\n this.#refreshState({ networkControllerState }),\n selectNetworkControllerFields,\n );\n this.messenger.subscribe(\n 'NetworkEnablementController:stateChange',\n (networkEnablementControllerState) =>\n this.#refreshState({ networkEnablementControllerState }),\n selectNetworkEnablementControllerFields,\n );\n this.messenger.subscribe(\n 'ConnectivityController:stateChange',\n (connectivityControllerState) =>\n this.#refreshState({ connectivityControllerState }),\n selectConnectivityControllerFields,\n );\n /* eslint-enable no-restricted-syntax */\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Look for a failed network, if any, and populate the initial state of the\n * banner. Reacts to upstream state changes from this point on.\n *\n * Call this when the wallet UI that consumes the banner becomes active\n * (typically when the wallet is unlocked and the home surface mounts) so\n * timers do not run while the user is not looking at the wallet. Should\n * be called after `NetworkController`, `NetworkEnablementController`, and\n * `ConnectivityController` have been initialized. Idempotent.\n */\n start(): void {\n if (this.#isStarted) {\n return;\n }\n\n this.#isStarted = true;\n this.#refreshState();\n }\n\n /**\n * Stops evaluating network connection state. Clears any pending banner\n * timers and resets state to `available`. Call this when the UI\n * consuming the banner is no longer active. Idempotent.\n */\n stop(): void {\n if (!this.#isStarted) {\n return;\n }\n\n this.#isStarted = false;\n this.#resetBanner();\n }\n\n /**\n * Clears the banner state such that the banner will be hidden.\n */\n dismissBanner(): void {\n this.#resetBanner();\n }\n\n /**\n * Switches the chain's default RPC endpoint to its Infura endpoint,\n * causing the banner to clear once the network becomes available again.\n *\n * @param chainId - The chain whose default RPC endpoint should be switched.\n * @throws If the chain configuration cannot be found, or if it has no\n * Infura endpoint to switch to, or if the default is already Infura.\n */\n async switchToDefaultInfuraRpcEndpoint(chainId: Hex): Promise<void> {\n const networkConfiguration = this.messenger.call(\n 'NetworkController:getNetworkConfigurationByChainId',\n chainId,\n );\n if (!networkConfiguration) {\n throw new Error(\n `No network configuration found for chain ID \"${chainId}\".`,\n );\n }\n\n const infuraEndpointIndex = networkConfiguration.rpcEndpoints.findIndex(\n (endpoint) => getIsInfuraEndpoint(endpoint.url),\n );\n if (infuraEndpointIndex === -1) {\n throw new Error(\n `No Infura endpoint available for chain ID \"${chainId}\".`,\n );\n }\n if (infuraEndpointIndex === networkConfiguration.defaultRpcEndpointIndex) {\n // The default is already Infura; nothing to do.\n return;\n }\n\n await this.messenger.call(\n 'NetworkController:updateNetwork',\n chainId,\n {\n ...networkConfiguration,\n defaultRpcEndpointIndex: infuraEndpointIndex,\n },\n { replacementSelectedRpcEndpointIndex: infuraEndpointIndex },\n );\n }\n\n #refreshState({\n networkControllerState,\n networkEnablementControllerState,\n connectivityControllerState,\n }: {\n networkControllerState?: Pick<\n NetworkState,\n 'networkConfigurationsByChainId' | 'networksMetadata'\n >;\n networkEnablementControllerState?: Pick<\n NetworkEnablementControllerState,\n 'enabledNetworkMap'\n >;\n connectivityControllerState?: Pick<\n ConnectivityControllerState,\n 'connectivityStatus'\n >;\n } = {}): void {\n if (!this.#isStarted) {\n return;\n }\n\n const { connectivityStatus } =\n connectivityControllerState ??\n this.messenger.call('ConnectivityController:getState');\n if (connectivityStatus === CONNECTIVITY_STATUSES.Offline) {\n this.#resetBanner();\n return;\n }\n\n const networkState =\n networkControllerState ??\n this.messenger.call('NetworkController:getState');\n const enablementState =\n networkEnablementControllerState ??\n this.messenger.call('NetworkEnablementController:getState');\n\n const failedNetwork = this.#findFailedNetwork(\n networkState,\n enablementState,\n );\n if (!failedNetwork) {\n this.#resetBanner();\n return;\n }\n\n if (\n this.state.status !== 'available' &&\n this.state.network?.networkClientId === failedNetwork.networkClientId\n ) {\n this.update((state) => {\n state.network = failedNetwork;\n });\n return;\n }\n\n if (this.#pendingNetworkClientId === failedNetwork.networkClientId) {\n return;\n }\n\n this.#clearTimers();\n this.update((state) => {\n state.status = 'available';\n state.network = null;\n });\n\n // A synchronous listener on our `stateChanged` event above may have\n // called `stop()` re-entrantly. Bail before scheduling anything.\n if (!this.#isStarted) {\n return;\n }\n\n // Capture the failing network at schedule time. We trust the messenger\n // contract: if the failure resolves or the target changes during the\n // wait, our upstream subscriptions will have cancelled or replaced this\n // timer via `#clearTimers` before it fires.\n this.#pendingNetworkClientId = failedNetwork.networkClientId;\n this.#degradedTimer = setTimeout(() => {\n this.#degradedTimer = undefined;\n this.#pendingNetworkClientId = undefined;\n this.update((state) => {\n state.status = 'degraded';\n state.network = failedNetwork;\n });\n // A synchronous listener on our `stateChanged` event above may have\n // called `stop()` re-entrantly. Bail before scheduling the escalation.\n if (!this.#isStarted) {\n return;\n }\n this.#unavailableTimer = setTimeout(() => {\n this.#unavailableTimer = undefined;\n this.update((state) => {\n state.status = 'unavailable';\n state.network = failedNetwork;\n });\n }, UNAVAILABLE_BANNER_TIMEOUT - DEGRADED_BANNER_TIMEOUT);\n }, DEGRADED_BANNER_TIMEOUT);\n }\n\n /**\n * Clears timers and resets banner state to {@link NetworkConnectionBannerStatus|`available`}\n * if it isn't there already.\n */\n #resetBanner(): void {\n this.#clearTimers();\n this.#pendingNetworkClientId = undefined;\n if (this.state.status !== 'available' || this.state.network !== null) {\n this.update((state) => {\n state.status = 'available';\n state.network = null;\n });\n }\n }\n\n #clearTimers(): void {\n if (this.#degradedTimer !== undefined) {\n clearTimeout(this.#degradedTimer);\n this.#degradedTimer = undefined;\n }\n if (this.#unavailableTimer !== undefined) {\n clearTimeout(this.#unavailableTimer);\n this.#unavailableTimer = undefined;\n }\n }\n\n #findFailedNetwork(\n networkState: Pick<\n NetworkState,\n 'networkConfigurationsByChainId' | 'networksMetadata'\n >,\n enablementState: Pick<\n NetworkEnablementControllerState,\n 'enabledNetworkMap'\n >,\n ): FailedNetwork | null {\n const networksWithMetadata = this.#collectNetworksWithMetadata(\n networkState,\n enablementState,\n );\n const failedNetworks = networksWithMetadata\n .filter(({ metadata }) => metadata.status !== NetworkStatus.Available)\n .map((network) => this.#buildFailedNetwork(network));\n return this.#pickBannerNetwork(failedNetworks, networksWithMetadata.length);\n }\n\n #getEnabledEvmChainIds(\n enabledNetworkMap: NetworkEnablementControllerState['enabledNetworkMap'],\n ): Hex[] {\n return Object.entries(enabledNetworkMap[KnownCaipNamespace.Eip155] ?? {})\n .filter(([, enabled]) => enabled)\n .map(([chainId]) => chainId as Hex);\n }\n\n #collectNetworksWithMetadata(\n {\n networkConfigurationsByChainId,\n networksMetadata,\n }: Pick<\n NetworkState,\n 'networkConfigurationsByChainId' | 'networksMetadata'\n >,\n {\n enabledNetworkMap,\n }: Pick<NetworkEnablementControllerState, 'enabledNetworkMap'>,\n ): NetworkWithMetadata[] {\n return this.#getEnabledEvmChainIds(enabledNetworkMap).flatMap((chainId) => {\n const networkConfiguration = networkConfigurationsByChainId[chainId];\n if (!networkConfiguration) {\n return [];\n }\n const { rpcEndpoints, defaultRpcEndpointIndex, name } =\n networkConfiguration;\n const defaultRpcEndpoint = rpcEndpoints[defaultRpcEndpointIndex];\n if (!defaultRpcEndpoint) {\n return [];\n }\n const metadata = networksMetadata[defaultRpcEndpoint.networkClientId];\n if (!metadata) {\n return [];\n }\n return [\n {\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n metadata,\n },\n ];\n });\n }\n\n #buildFailedNetwork({\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n }: NetworkWithMetadata): FailedNetwork {\n const isInfuraEndpoint = getIsInfuraEndpoint(defaultRpcEndpoint.url);\n\n // For custom endpoints (non-Infura), find an Infura endpoint on this\n // chain that we could offer to switch to.\n let switchableInfuraNetworkClientId: string | null = null;\n if (!isInfuraEndpoint) {\n const infuraEndpoint = rpcEndpoints.find(\n (endpoint, index) =>\n index !== defaultRpcEndpointIndex &&\n getIsInfuraEndpoint(endpoint.url),\n );\n switchableInfuraNetworkClientId = infuraEndpoint?.networkClientId ?? null;\n }\n\n return {\n chainId,\n networkClientId: defaultRpcEndpoint.networkClientId,\n name,\n rpcUrl: defaultRpcEndpoint.url,\n isInfuraEndpoint,\n switchableInfuraNetworkClientId,\n domain: getDomain(defaultRpcEndpoint.url),\n };\n }\n\n #pickBannerNetwork(\n failedNetworks: FailedNetwork[],\n totalNetworksWithMetadata: number,\n ): FailedNetwork | null {\n if (failedNetworks.length === 0) {\n return null;\n }\n\n const firstCustomFailed = failedNetworks.find(\n (entry) => !entry.isInfuraEndpoint,\n );\n const distinctDomains = new Set(\n failedNetworks\n .map((entry) => entry.domain)\n .filter((domain): domain is string => domain !== null),\n ).size;\n const areAllKnownNetworksFailed =\n failedNetworks.length === totalNetworksWithMetadata;\n\n if (\n !firstCustomFailed &&\n distinctDomains <= 1 &&\n !areAllKnownNetworksFailed\n ) {\n return null;\n }\n\n return firstCustomFailed ?? failedNetworks[0];\n }\n}\n\n/**\n * Whether an RPC URL is a MetaMask Infura endpoint. Matches the\n * `{infuraProjectId}` placeholder form that lives on stored network\n * configurations before the wallet substitutes the real project id at\n * request time. URLs that already carry a substituted key are treated as\n * custom (we do not have the project id in this package).\n *\n * @param url - The RPC URL to check.\n * @returns True if the URL is the placeholder form of a MetaMask Infura\n * endpoint.\n */\nfunction getIsInfuraEndpoint(url: string): boolean {\n return /^https:\\/\\/[^./]+\\.infura\\.io\\/v3\\/\\{infuraProjectId\\}$/u.test(url);\n}\n"]}