@metamask-previews/network-connection-banner-controller 0.1.0-preview-2fc626b → 0.1.0-preview-3c0e8a808
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/CHANGELOG.md +4 -1
- package/README.md +13 -0
- package/dist/NetworkConnectionBannerController-method-action-types.cjs.map +1 -1
- package/dist/NetworkConnectionBannerController-method-action-types.d.cts +12 -4
- package/dist/NetworkConnectionBannerController-method-action-types.d.cts.map +1 -1
- package/dist/NetworkConnectionBannerController-method-action-types.d.mts +12 -4
- package/dist/NetworkConnectionBannerController-method-action-types.d.mts.map +1 -1
- package/dist/NetworkConnectionBannerController-method-action-types.mjs.map +1 -1
- package/dist/NetworkConnectionBannerController.cjs +153 -94
- package/dist/NetworkConnectionBannerController.cjs.map +1 -1
- package/dist/NetworkConnectionBannerController.d.cts +25 -16
- package/dist/NetworkConnectionBannerController.d.cts.map +1 -1
- package/dist/NetworkConnectionBannerController.d.mts +25 -16
- package/dist/NetworkConnectionBannerController.d.mts.map +1 -1
- package/dist/NetworkConnectionBannerController.mjs +154 -95
- package/dist/NetworkConnectionBannerController.mjs.map +1 -1
- package/dist/index.cjs +1 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +0 -1
- package/dist/index.mjs.map +1 -1
- package/dist/selectors.cjs.map +1 -1
- package/dist/selectors.d.cts +2 -2
- package/dist/selectors.d.cts.map +1 -1
- package/dist/selectors.d.mts +2 -2
- package/dist/selectors.d.mts.map +1 -1
- package/dist/selectors.mjs.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
-
-
|
|
12
|
+
- Add `NetworkConnectionBannerController`, which evaluates enabled network RPC
|
|
13
|
+
health after initialization and manages degraded and unavailable banner state,
|
|
14
|
+
dismissal, and switching custom RPC endpoints to an available Infura endpoint
|
|
15
|
+
([#9041](https://github.com/MetaMask/core/pull/9041))
|
|
13
16
|
|
|
14
17
|
[Unreleased]: https://github.com/MetaMask/core/
|
package/README.md
CHANGED
|
@@ -5,6 +5,19 @@ 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
|
|
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.
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
networkConnectionBannerController.init();
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`init()` is idempotent and immediately evaluates the latest upstream state.
|
|
20
|
+
|
|
8
21
|
## Installation
|
|
9
22
|
|
|
10
23
|
`yarn add @metamask/network-connection-banner-controller`
|
|
@@ -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 *
|
|
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"]}
|
|
@@ -4,9 +4,17 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { NetworkConnectionBannerController } from "./NetworkConnectionBannerController.cjs";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Starts evaluating network connection state.
|
|
8
|
+
*
|
|
9
|
+
* This method should be called after the upstream network, network
|
|
10
|
+
* enablement, and connectivity controllers have been initialized.
|
|
11
|
+
*/
|
|
12
|
+
export type NetworkConnectionBannerControllerInitAction = {
|
|
13
|
+
type: `NetworkConnectionBannerController:init`;
|
|
14
|
+
handler: NetworkConnectionBannerController['init'];
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Clears the banner state such that the banner will be hidden.
|
|
10
18
|
*/
|
|
11
19
|
export type NetworkConnectionBannerControllerDismissBannerAction = {
|
|
12
20
|
type: `NetworkConnectionBannerController:dismissBanner`;
|
|
@@ -28,5 +36,5 @@ export type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction = {
|
|
|
28
36
|
/**
|
|
29
37
|
* Union of all NetworkConnectionBannerController action types.
|
|
30
38
|
*/
|
|
31
|
-
export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction;
|
|
39
|
+
export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerInitAction | NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction;
|
|
32
40
|
//# 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
|
|
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"}
|
|
@@ -4,9 +4,17 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { NetworkConnectionBannerController } from "./NetworkConnectionBannerController.mjs";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Starts evaluating network connection state.
|
|
8
|
+
*
|
|
9
|
+
* This method should be called after the upstream network, network
|
|
10
|
+
* enablement, and connectivity controllers have been initialized.
|
|
11
|
+
*/
|
|
12
|
+
export type NetworkConnectionBannerControllerInitAction = {
|
|
13
|
+
type: `NetworkConnectionBannerController:init`;
|
|
14
|
+
handler: NetworkConnectionBannerController['init'];
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Clears the banner state such that the banner will be hidden.
|
|
10
18
|
*/
|
|
11
19
|
export type NetworkConnectionBannerControllerDismissBannerAction = {
|
|
12
20
|
type: `NetworkConnectionBannerController:dismissBanner`;
|
|
@@ -28,5 +36,5 @@ export type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction = {
|
|
|
28
36
|
/**
|
|
29
37
|
* Union of all NetworkConnectionBannerController action types.
|
|
30
38
|
*/
|
|
31
|
-
export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction;
|
|
39
|
+
export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerInitAction | NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction;
|
|
32
40
|
//# 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
|
|
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 +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 *
|
|
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"]}
|
|
@@ -10,14 +10,44 @@ 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,
|
|
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;
|
|
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");
|
|
17
17
|
const connectivity_controller_1 = require("@metamask/connectivity-controller");
|
|
18
18
|
const network_controller_1 = require("@metamask/network-controller");
|
|
19
|
+
const network_enablement_controller_1 = require("@metamask/network-enablement-controller");
|
|
19
20
|
const utils_1 = require("@metamask/utils");
|
|
21
|
+
const reselect_1 = require("reselect");
|
|
20
22
|
const url_utils_1 = require("./url-utils.cjs");
|
|
23
|
+
/**
|
|
24
|
+
* Selects `networksMetadata` from the `NetworkController` state.
|
|
25
|
+
*
|
|
26
|
+
* @param state - The `NetworkController` state.
|
|
27
|
+
* @returns The networks metadata map keyed by network client id.
|
|
28
|
+
*/
|
|
29
|
+
const selectNetworksMetadata = (state) => state.networksMetadata;
|
|
30
|
+
/**
|
|
31
|
+
* Selects `networkConfigurationsByChainId` from the `NetworkController`
|
|
32
|
+
* state.
|
|
33
|
+
*
|
|
34
|
+
* @param state - The `NetworkController` state.
|
|
35
|
+
* @returns The network configurations keyed by chain id.
|
|
36
|
+
*/
|
|
37
|
+
const selectNetworkConfigurationsByChainId = (state) => state.networkConfigurationsByChainId;
|
|
38
|
+
/**
|
|
39
|
+
* Selects the `NetworkController` state fields that influence the banner
|
|
40
|
+
* rule. Composed with `createSelector` so the return object stays reference
|
|
41
|
+
* stable while unrelated `NetworkController` state (e.g.
|
|
42
|
+
* `selectedNetworkClientId`) changes.
|
|
43
|
+
*
|
|
44
|
+
* @param state - The `NetworkController` state.
|
|
45
|
+
* @returns The relevant network fields.
|
|
46
|
+
*/
|
|
47
|
+
const selectNetworkControllerFields = (0, reselect_1.createSelector)([selectNetworksMetadata, selectNetworkConfigurationsByChainId], (networksMetadata, networkConfigurationsByChainId) => ({
|
|
48
|
+
networksMetadata,
|
|
49
|
+
networkConfigurationsByChainId,
|
|
50
|
+
}));
|
|
21
51
|
/**
|
|
22
52
|
* The name of the {@link NetworkConnectionBannerController}, used to namespace
|
|
23
53
|
* the controller's actions and events and to namespace the controller's state
|
|
@@ -50,9 +80,18 @@ function getDefaultNetworkConnectionBannerControllerState() {
|
|
|
50
80
|
};
|
|
51
81
|
}
|
|
52
82
|
exports.getDefaultNetworkConnectionBannerControllerState = getDefaultNetworkConnectionBannerControllerState;
|
|
53
|
-
|
|
54
|
-
|
|
83
|
+
/**
|
|
84
|
+
* How long (in milliseconds) a failing network must remain in a "failed"
|
|
85
|
+
* status ("degraded" or "unavailable") before the degraded banner appears.
|
|
86
|
+
*/
|
|
87
|
+
const DEGRADED_BANNER_TIMEOUT = 5000;
|
|
88
|
+
/**
|
|
89
|
+
* How long (in milliseconds) a failing network must remain in a "failed"
|
|
90
|
+
* status before the banner escalates to "unavailable".
|
|
91
|
+
*/
|
|
92
|
+
const UNAVAILABLE_BANNER_TIMEOUT = 30000;
|
|
55
93
|
const MESSENGER_EXPOSED_METHODS = [
|
|
94
|
+
'init',
|
|
56
95
|
'dismissBanner',
|
|
57
96
|
'switchToDefaultInfuraRpc',
|
|
58
97
|
];
|
|
@@ -67,8 +106,8 @@ const MESSENGER_EXPOSED_METHODS = [
|
|
|
67
106
|
* - The first failing network's default RPC is a custom (non-Infura) endpoint
|
|
68
107
|
* — users always want to be informed about errors with RPCs they've chosen.
|
|
69
108
|
* - Failed RPCs span more than one registrable domain (likely client-side).
|
|
70
|
-
* - Every enabled EVM network
|
|
71
|
-
* setups so they still get a signal).
|
|
109
|
+
* - Every enabled EVM network with known connectivity status is failing
|
|
110
|
+
* (escape hatch for single-network setups so they still get a signal).
|
|
72
111
|
*
|
|
73
112
|
* A wide single-provider outage (e.g. every `*.infura.io` network goes down at
|
|
74
113
|
* once) collapses to one domain and is suppressed, except in the all-down
|
|
@@ -97,20 +136,34 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
|
|
|
97
136
|
_NetworkConnectionBannerController_degradedTimer.set(this, void 0);
|
|
98
137
|
_NetworkConnectionBannerController_unavailableTimer.set(this, void 0);
|
|
99
138
|
_NetworkConnectionBannerController_pendingNetworkClientId.set(this, void 0);
|
|
100
|
-
|
|
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.
|
|
101
143
|
// Upstream controllers still expose :stateChange; switch to :stateChanged
|
|
102
144
|
// once those packages migrate their event types.
|
|
103
145
|
/* eslint-disable no-restricted-syntax -- awaiting upstream :stateChanged migration */
|
|
104
|
-
this.messenger.subscribe('NetworkController:stateChange',
|
|
105
|
-
this.messenger.subscribe('NetworkEnablementController:stateChange',
|
|
106
|
-
this.messenger.subscribe('ConnectivityController:stateChange',
|
|
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);
|
|
107
149
|
/* eslint-enable no-restricted-syntax */
|
|
108
150
|
this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
109
151
|
}
|
|
110
152
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
153
|
+
* Starts evaluating network connection state.
|
|
154
|
+
*
|
|
155
|
+
* This method should be called after the upstream network, network
|
|
156
|
+
* enablement, and connectivity controllers have been initialized.
|
|
157
|
+
*/
|
|
158
|
+
init() {
|
|
159
|
+
if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_initialized, "f")) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this);
|
|
163
|
+
__classPrivateFieldSet(this, _NetworkConnectionBannerController_initialized, true, "f");
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Clears the banner state such that the banner will be hidden.
|
|
114
167
|
*/
|
|
115
168
|
dismissBanner() {
|
|
116
169
|
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
|
|
@@ -129,7 +182,7 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
|
|
|
129
182
|
if (!networkConfiguration) {
|
|
130
183
|
throw new Error(`No network configuration found for chain ID "${chainId}".`);
|
|
131
184
|
}
|
|
132
|
-
const infuraEndpointIndex = networkConfiguration.rpcEndpoints.findIndex((endpoint) =>
|
|
185
|
+
const infuraEndpointIndex = networkConfiguration.rpcEndpoints.findIndex((endpoint) => getIsInfuraEndpoint(endpoint.url));
|
|
133
186
|
if (infuraEndpointIndex === -1) {
|
|
134
187
|
throw new Error(`No Infura endpoint available for chain ID "${chainId}".`);
|
|
135
188
|
}
|
|
@@ -144,66 +197,66 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
|
|
|
144
197
|
}
|
|
145
198
|
}
|
|
146
199
|
exports.NetworkConnectionBannerController = NetworkConnectionBannerController;
|
|
147
|
-
_NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnectionBannerController_unavailableTimer = new WeakMap(), _NetworkConnectionBannerController_pendingNetworkClientId = new WeakMap(), _NetworkConnectionBannerController_instances = new WeakSet(),
|
|
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);
|
|
203
|
+
}
|
|
204
|
+
}, _NetworkConnectionBannerController_refreshState = function _NetworkConnectionBannerController_refreshState() {
|
|
148
205
|
const { connectivityStatus } = this.messenger.call('ConnectivityController:getState');
|
|
149
206
|
if (connectivityStatus === connectivity_controller_1.CONNECTIVITY_STATUSES.Offline) {
|
|
150
207
|
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
|
|
151
208
|
return;
|
|
152
209
|
}
|
|
153
|
-
const
|
|
154
|
-
if (!
|
|
210
|
+
const failedNetwork = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_findFailedNetwork).call(this);
|
|
211
|
+
if (!failedNetwork) {
|
|
155
212
|
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
|
|
156
213
|
return;
|
|
157
214
|
}
|
|
158
215
|
if (this.state.status !== 'available' &&
|
|
159
|
-
this.state.network?.networkClientId ===
|
|
160
|
-
this.update((
|
|
161
|
-
|
|
216
|
+
this.state.network?.networkClientId === failedNetwork.networkClientId) {
|
|
217
|
+
this.update((state) => {
|
|
218
|
+
state.network = failedNetwork;
|
|
162
219
|
});
|
|
163
220
|
return;
|
|
164
221
|
}
|
|
165
|
-
if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_pendingNetworkClientId, "f") ===
|
|
222
|
+
if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_pendingNetworkClientId, "f") === failedNetwork.networkClientId) {
|
|
166
223
|
return;
|
|
167
224
|
}
|
|
168
225
|
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_clearTimers).call(this);
|
|
169
|
-
this.update((
|
|
170
|
-
|
|
171
|
-
|
|
226
|
+
this.update((state) => {
|
|
227
|
+
state.status = 'available';
|
|
228
|
+
state.network = null;
|
|
172
229
|
});
|
|
173
|
-
|
|
230
|
+
// Capture the failing network at schedule time. We trust the messenger
|
|
231
|
+
// contract: if the failure resolves or the target changes during the
|
|
232
|
+
// wait, our upstream subscriptions will have cancelled or replaced this
|
|
233
|
+
// timer via `#clearTimers` before it fires.
|
|
234
|
+
__classPrivateFieldSet(this, _NetworkConnectionBannerController_pendingNetworkClientId, failedNetwork.networkClientId, "f");
|
|
174
235
|
__classPrivateFieldSet(this, _NetworkConnectionBannerController_degradedTimer, setTimeout(() => {
|
|
175
236
|
__classPrivateFieldSet(this, _NetworkConnectionBannerController_degradedTimer, undefined, "f");
|
|
176
237
|
__classPrivateFieldSet(this, _NetworkConnectionBannerController_pendingNetworkClientId, undefined, "f");
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
this.update((draft) => {
|
|
182
|
-
draft.status = 'degraded';
|
|
183
|
-
draft.network = stillFailed;
|
|
238
|
+
this.update((state) => {
|
|
239
|
+
state.status = 'degraded';
|
|
240
|
+
state.network = failedNetwork;
|
|
184
241
|
});
|
|
185
242
|
__classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, setTimeout(() => {
|
|
186
243
|
__classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, undefined, "f");
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
this.update((draft) => {
|
|
192
|
-
draft.status = 'unavailable';
|
|
193
|
-
draft.network = stillFailedAtEscalation;
|
|
244
|
+
this.update((state) => {
|
|
245
|
+
state.status = 'unavailable';
|
|
246
|
+
state.network = failedNetwork;
|
|
194
247
|
});
|
|
195
|
-
},
|
|
196
|
-
},
|
|
248
|
+
}, UNAVAILABLE_BANNER_TIMEOUT - DEGRADED_BANNER_TIMEOUT), "f");
|
|
249
|
+
}, DEGRADED_BANNER_TIMEOUT), "f");
|
|
197
250
|
}, _NetworkConnectionBannerController_resetBanner = function _NetworkConnectionBannerController_resetBanner() {
|
|
198
251
|
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_clearTimers).call(this);
|
|
252
|
+
__classPrivateFieldSet(this, _NetworkConnectionBannerController_pendingNetworkClientId, undefined, "f");
|
|
199
253
|
if (this.state.status !== 'available' || this.state.network !== null) {
|
|
200
|
-
this.update((
|
|
201
|
-
|
|
202
|
-
|
|
254
|
+
this.update((state) => {
|
|
255
|
+
state.status = 'available';
|
|
256
|
+
state.network = null;
|
|
203
257
|
});
|
|
204
258
|
}
|
|
205
259
|
}, _NetworkConnectionBannerController_clearTimers = function _NetworkConnectionBannerController_clearTimers() {
|
|
206
|
-
__classPrivateFieldSet(this, _NetworkConnectionBannerController_pendingNetworkClientId, undefined, "f");
|
|
207
260
|
if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_degradedTimer, "f") !== undefined) {
|
|
208
261
|
clearTimeout(__classPrivateFieldGet(this, _NetworkConnectionBannerController_degradedTimer, "f"));
|
|
209
262
|
__classPrivateFieldSet(this, _NetworkConnectionBannerController_degradedTimer, undefined, "f");
|
|
@@ -212,48 +265,64 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
|
|
|
212
265
|
clearTimeout(__classPrivateFieldGet(this, _NetworkConnectionBannerController_unavailableTimer, "f"));
|
|
213
266
|
__classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, undefined, "f");
|
|
214
267
|
}
|
|
215
|
-
},
|
|
268
|
+
}, _NetworkConnectionBannerController_findFailedNetwork = function _NetworkConnectionBannerController_findFailedNetwork() {
|
|
269
|
+
const networksWithMetadata = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_collectNetworksWithMetadata).call(this);
|
|
270
|
+
const failedNetworks = networksWithMetadata
|
|
271
|
+
.filter(({ metadata }) => metadata.status !== network_controller_1.NetworkStatus.Available)
|
|
272
|
+
.map((network) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_buildFailedNetwork).call(this, network));
|
|
273
|
+
return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_pickBannerNetwork).call(this, failedNetworks, networksWithMetadata.length);
|
|
274
|
+
}, _NetworkConnectionBannerController_getEnabledEvmChainIds = function _NetworkConnectionBannerController_getEnabledEvmChainIds() {
|
|
216
275
|
const { enabledNetworkMap } = this.messenger.call('NetworkEnablementController:getState');
|
|
217
|
-
|
|
276
|
+
return Object.entries(enabledNetworkMap[utils_1.KnownCaipNamespace.Eip155] ?? {})
|
|
218
277
|
.filter(([, enabled]) => enabled)
|
|
219
278
|
.map(([chainId]) => chainId);
|
|
279
|
+
}, _NetworkConnectionBannerController_collectNetworksWithMetadata = function _NetworkConnectionBannerController_collectNetworksWithMetadata() {
|
|
220
280
|
const { networkConfigurationsByChainId, networksMetadata } = this.messenger.call('NetworkController:getState');
|
|
221
|
-
|
|
222
|
-
let totalEnabled = 0;
|
|
223
|
-
for (const chainId of enabledEvmChainIds) {
|
|
281
|
+
return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_getEnabledEvmChainIds).call(this).flatMap((chainId) => {
|
|
224
282
|
const networkConfiguration = networkConfigurationsByChainId[chainId];
|
|
225
283
|
if (!networkConfiguration) {
|
|
226
|
-
|
|
284
|
+
return [];
|
|
227
285
|
}
|
|
228
286
|
const { rpcEndpoints, defaultRpcEndpointIndex, name } = networkConfiguration;
|
|
229
287
|
const defaultRpcEndpoint = rpcEndpoints[defaultRpcEndpointIndex];
|
|
230
288
|
if (!defaultRpcEndpoint) {
|
|
231
|
-
|
|
289
|
+
return [];
|
|
232
290
|
}
|
|
233
|
-
totalEnabled += 1;
|
|
234
291
|
const metadata = networksMetadata[defaultRpcEndpoint.networkClientId];
|
|
235
|
-
if (metadata
|
|
236
|
-
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
const endpointIsInfura = isInfuraEndpoint(defaultRpcEndpoint.url);
|
|
240
|
-
// For custom endpoints (non-Infura), find an Infura endpoint on this
|
|
241
|
-
// chain that we could offer to switch to.
|
|
242
|
-
let infuraNetworkClientId = null;
|
|
243
|
-
if (!endpointIsInfura) {
|
|
244
|
-
const otherInfura = rpcEndpoints.find((endpoint, index) => index !== defaultRpcEndpointIndex && isInfuraEndpoint(endpoint.url));
|
|
245
|
-
infuraNetworkClientId = otherInfura?.networkClientId ?? null;
|
|
292
|
+
if (!metadata) {
|
|
293
|
+
return [];
|
|
246
294
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
295
|
+
return [
|
|
296
|
+
{
|
|
297
|
+
chainId,
|
|
298
|
+
name,
|
|
299
|
+
rpcEndpoints,
|
|
300
|
+
defaultRpcEndpointIndex,
|
|
301
|
+
defaultRpcEndpoint,
|
|
302
|
+
metadata,
|
|
303
|
+
},
|
|
304
|
+
];
|
|
305
|
+
});
|
|
306
|
+
}, _NetworkConnectionBannerController_buildFailedNetwork = function _NetworkConnectionBannerController_buildFailedNetwork({ chainId, name, rpcEndpoints, defaultRpcEndpointIndex, defaultRpcEndpoint, }) {
|
|
307
|
+
const isInfuraEndpoint = getIsInfuraEndpoint(defaultRpcEndpoint.url);
|
|
308
|
+
// For custom endpoints (non-Infura), find an Infura endpoint on this
|
|
309
|
+
// chain that we could offer to switch to.
|
|
310
|
+
let switchableInfuraNetworkClientId = null;
|
|
311
|
+
if (!isInfuraEndpoint) {
|
|
312
|
+
const otherInfura = rpcEndpoints.find((endpoint, index) => index !== defaultRpcEndpointIndex &&
|
|
313
|
+
getIsInfuraEndpoint(endpoint.url));
|
|
314
|
+
switchableInfuraNetworkClientId = otherInfura?.networkClientId ?? null;
|
|
256
315
|
}
|
|
316
|
+
return {
|
|
317
|
+
chainId,
|
|
318
|
+
networkClientId: defaultRpcEndpoint.networkClientId,
|
|
319
|
+
name,
|
|
320
|
+
rpcUrl: defaultRpcEndpoint.url,
|
|
321
|
+
isInfuraEndpoint,
|
|
322
|
+
switchableInfuraNetworkClientId,
|
|
323
|
+
domain: (0, url_utils_1.getDomain)(defaultRpcEndpoint.url),
|
|
324
|
+
};
|
|
325
|
+
}, _NetworkConnectionBannerController_pickBannerNetwork = function _NetworkConnectionBannerController_pickBannerNetwork(failedNetworks, totalNetworksWithMetadata) {
|
|
257
326
|
if (failedNetworks.length === 0) {
|
|
258
327
|
return null;
|
|
259
328
|
}
|
|
@@ -261,36 +330,26 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
|
|
|
261
330
|
const distinctDomains = new Set(failedNetworks
|
|
262
331
|
.map((entry) => entry.domain)
|
|
263
332
|
.filter((domain) => domain !== null)).size;
|
|
264
|
-
const
|
|
333
|
+
const areAllKnownNetworksFailed = failedNetworks.length === totalNetworksWithMetadata;
|
|
265
334
|
if (!firstCustomFailed &&
|
|
266
335
|
distinctDomains <= 1 &&
|
|
267
|
-
!
|
|
336
|
+
!areAllKnownNetworksFailed) {
|
|
268
337
|
return null;
|
|
269
338
|
}
|
|
270
|
-
|
|
271
|
-
return {
|
|
272
|
-
chainId: selected.chainId,
|
|
273
|
-
networkClientId: selected.networkClientId,
|
|
274
|
-
networkName: selected.networkName,
|
|
275
|
-
rpcUrl: selected.rpcUrl,
|
|
276
|
-
isInfuraEndpoint: selected.isInfuraEndpoint,
|
|
277
|
-
infuraNetworkClientId: selected.infuraNetworkClientId,
|
|
278
|
-
};
|
|
339
|
+
return firstCustomFailed ?? failedNetworks[0];
|
|
279
340
|
};
|
|
280
341
|
/**
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
342
|
+
* Whether an RPC URL is a MetaMask Infura endpoint. Matches the
|
|
343
|
+
* `{infuraProjectId}` placeholder form that lives on stored network
|
|
344
|
+
* configurations before the wallet substitutes the real project id at
|
|
345
|
+
* request time. URLs that already carry a substituted key are treated as
|
|
346
|
+
* custom (we do not have the project id in this package).
|
|
284
347
|
*
|
|
285
348
|
* @param url - The RPC URL to check.
|
|
286
|
-
* @returns True if the URL
|
|
349
|
+
* @returns True if the URL is the placeholder form of a MetaMask Infura
|
|
350
|
+
* endpoint.
|
|
287
351
|
*/
|
|
288
|
-
function
|
|
289
|
-
|
|
290
|
-
return new URL(url).hostname.toLowerCase().endsWith('.infura.io');
|
|
291
|
-
}
|
|
292
|
-
catch {
|
|
293
|
-
return false;
|
|
294
|
-
}
|
|
352
|
+
function getIsInfuraEndpoint(url) {
|
|
353
|
+
return /^https:\/\/[^./]+\.infura\.io\/v3\/\{infuraProjectId\}$/u.test(url);
|
|
295
354
|
}
|
|
296
355
|
//# sourceMappingURL=NetworkConnectionBannerController.cjs.map
|