@metamask-previews/network-connection-banner-controller 0.1.0-preview-bd4161b27 → 0.1.0-preview-68794c0ff

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
@@ -1,9 +1,8 @@
1
1
  # `@metamask/network-connection-banner-controller`
2
2
 
3
3
  NetworkConnectionBannerController decides when and how to surface the network
4
- connection banner based on RPC endpoint health. It encapsulates the rule, the
5
- 5s/30s timer state machine, and the eTLD+1 grouping used to decide when a
6
- wider provider outage is in play.
4
+ connection banner based on RPC endpoint health. It encapsulates the rule and
5
+ the 5s/30s timer state machine.
7
6
 
8
7
  ## Lifecycle
9
8
 
@@ -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_isStarted, _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_collectEnabledNetworks, _NetworkConnectionBannerController_buildFailedNetwork, _NetworkConnectionBannerController_pickFailedNetworkToDisplay;
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");
@@ -115,22 +115,19 @@ const MESSENGER_EXPOSED_METHODS = [
115
115
  /**
116
116
  * NetworkConnectionBannerController decides whether the network connection
117
117
  * banner should be shown for the user, and which failing network it should
118
- * describe. It encapsulates the rule, the 5s / 30s timer escalation, and the
119
- * eTLD+1 grouping used to decide when a wider provider outage is in play.
118
+ * describe. It encapsulates the rule and the 5s / 30s timer escalation.
120
119
  *
121
120
  * The banner shows when:
122
121
  *
123
- * - The first failing network's default RPC is a custom (non-Infura) endpoint
124
- * users always want to be informed about errors with RPCs they've chosen.
125
- * - Failed RPCs span more than one registrable domain (likely client-side).
126
- * - Every enabled EVM network with known connectivity status is failing
127
- * (escape hatch for single-network setups so they still get a signal).
122
+ * - A failing network's default RPC is a custom (non-Infura) endpoint
123
+ * users always want to be informed about errors with RPCs they've chosen.
124
+ * - Every enabled EVM network is reporting a failed connectivity status
125
+ * (escape hatch so Infura-only setups still get a signal).
128
126
  *
129
- * A wide single-provider outage (e.g. every `*.infura.io` network goes down at
130
- * once) collapses to one domain and is suppressed, except in the all-down
131
- * single-network case. When a custom failure is present, it's surfaced first
132
- * so the "Switch to MetaMask default RPC" CTA targets the network the user
133
- * can act on.
127
+ * A partial Infura outage alongside healthy peers is suppressed since it
128
+ * usually resolves on its own and the user cannot act on it. When a custom
129
+ * failure is present, it's surfaced first so the "Switch to MetaMask default
130
+ * RPC" CTA targets the network the user can act on.
134
131
  *
135
132
  * Clients only need to render the banner from the controller's state and wire
136
133
  * click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpcEndpoint}.
@@ -157,9 +154,21 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
157
154
  // Upstream controllers still expose :stateChange; switch to :stateChanged
158
155
  // once those packages migrate their event types.
159
156
  /* eslint-disable no-restricted-syntax -- awaiting upstream :stateChanged migration */
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);
157
+ this.messenger.subscribe('NetworkController:stateChange', (networkControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, {
158
+ networkControllerState,
159
+ networkEnablementControllerState: this.messenger.call('NetworkEnablementController:getState'),
160
+ connectivityControllerState: this.messenger.call('ConnectivityController:getState'),
161
+ }), selectNetworkControllerFields);
162
+ this.messenger.subscribe('NetworkEnablementController:stateChange', (networkEnablementControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, {
163
+ networkControllerState: this.messenger.call('NetworkController:getState'),
164
+ networkEnablementControllerState,
165
+ connectivityControllerState: this.messenger.call('ConnectivityController:getState'),
166
+ }), selectNetworkEnablementControllerFields);
167
+ this.messenger.subscribe('ConnectivityController:stateChange', (connectivityControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, {
168
+ networkControllerState: this.messenger.call('NetworkController:getState'),
169
+ networkEnablementControllerState: this.messenger.call('NetworkEnablementController:getState'),
170
+ connectivityControllerState,
171
+ }), selectConnectivityControllerFields);
163
172
  /* eslint-enable no-restricted-syntax */
164
173
  this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
165
174
  }
@@ -178,7 +187,11 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
178
187
  return;
179
188
  }
180
189
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_isStarted, true, "f");
181
- __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this);
190
+ __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, {
191
+ networkControllerState: this.messenger.call('NetworkController:getState'),
192
+ networkEnablementControllerState: this.messenger.call('NetworkEnablementController:getState'),
193
+ connectivityControllerState: this.messenger.call('ConnectivityController:getState'),
194
+ });
182
195
  }
183
196
  /**
184
197
  * Stops evaluating network connection state. Clears any pending banner
@@ -226,21 +239,16 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
226
239
  }
227
240
  }
228
241
  exports.NetworkConnectionBannerController = NetworkConnectionBannerController;
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, } = {}) {
242
+ _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
243
  if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
231
244
  return;
232
245
  }
233
- const { connectivityStatus } = connectivityControllerState ??
234
- this.messenger.call('ConnectivityController:getState');
235
- if (connectivityStatus === connectivity_controller_1.CONNECTIVITY_STATUSES.Offline) {
246
+ if (connectivityControllerState.connectivityStatus ===
247
+ connectivity_controller_1.CONNECTIVITY_STATUSES.Offline) {
236
248
  __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
237
249
  return;
238
250
  }
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);
251
+ const failedNetwork = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_findFailedNetwork).call(this, networkControllerState, networkEnablementControllerState);
244
252
  if (!failedNetwork) {
245
253
  __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
246
254
  return;
@@ -249,10 +257,16 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
249
257
  this.state.networkConnectionBannerNetwork?.networkClientId ===
250
258
  failedNetwork.networkClientId) {
251
259
  this.update((state) => {
260
+ // Even if the network client ID has not changed, save the current
261
+ // version of the failed network to state in case the user has updated
262
+ // its RPC URL.
252
263
  state.networkConnectionBannerNetwork = failedNetwork;
253
264
  });
254
265
  return;
255
266
  }
267
+ // A degraded timer is already counting down for this same network.
268
+ // Repeated `stateChange` events must not clear and restart it, or a
269
+ // steady stream of updates could postpone the banner indefinitely.
256
270
  if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_pendingNetworkClientId, "f") === failedNetwork.networkClientId) {
257
271
  return;
258
272
  }
@@ -261,15 +275,15 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
261
275
  state.networkConnectionBannerStatus = 'available';
262
276
  state.networkConnectionBannerNetwork = null;
263
277
  });
264
- // A synchronous listener on our `stateChanged` event above may have
265
- // called `stop()` re-entrantly. Bail before scheduling anything.
278
+ // If `stop` is called before scheduling timers, bail early.
266
279
  if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
267
280
  return;
268
281
  }
269
- // Capture the failing network at schedule time. We trust the messenger
270
- // contract: if the failure resolves or the target changes during the
271
- // wait, our upstream subscriptions will have cancelled or replaced this
272
- // timer via `#clearTimers` before it fires.
282
+ // Remember which network the pending timer is for (see the guard above)
283
+ // and capture the failing network at schedule time. If the failure
284
+ // resolves or a different network becomes the banner target while we
285
+ // wait, the subscription handlers re-enter this method and cancel or
286
+ // replace the timer before it fires.
273
287
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_pendingNetworkClientId, failedNetwork.networkClientId, "f");
274
288
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_degradedTimer, setTimeout(() => {
275
289
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_degradedTimer, undefined, "f");
@@ -278,8 +292,7 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
278
292
  state.networkConnectionBannerStatus = 'degraded';
279
293
  state.networkConnectionBannerNetwork = failedNetwork;
280
294
  });
281
- // A synchronous listener on our `stateChanged` event above may have
282
- // called `stop()` re-entrantly. Bail before scheduling the escalation.
295
+ // If `stop` is called before scheduling timers, bail early.
283
296
  if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
284
297
  return;
285
298
  }
@@ -311,16 +324,17 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
311
324
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, undefined, "f");
312
325
  }
313
326
  }, _NetworkConnectionBannerController_findFailedNetwork = function _NetworkConnectionBannerController_findFailedNetwork(networkState, enablementState) {
314
- const networksWithMetadata = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_collectNetworksWithMetadata).call(this, networkState, enablementState);
315
- const failedNetworks = networksWithMetadata
316
- .filter(({ metadata }) => metadata.status !== network_controller_1.NetworkStatus.Available)
327
+ const enabledNetworks = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_collectEnabledNetworks).call(this, networkState, enablementState);
328
+ // Networks whose connectivity has not been looked up yet are not failed.
329
+ const failedNetworks = enabledNetworks
330
+ .filter(({ metadata }) => metadata !== undefined && metadata.status !== network_controller_1.NetworkStatus.Available)
317
331
  .map((network) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_buildFailedNetwork).call(this, network));
318
- return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_pickBannerNetwork).call(this, failedNetworks, networksWithMetadata.length);
332
+ return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_pickFailedNetworkToDisplay).call(this, failedNetworks, enabledNetworks.length);
319
333
  }, _NetworkConnectionBannerController_getEnabledEvmChainIds = function _NetworkConnectionBannerController_getEnabledEvmChainIds(enabledNetworkMap) {
320
334
  return Object.entries(enabledNetworkMap[utils_1.KnownCaipNamespace.Eip155] ?? {})
321
335
  .filter(([, enabled]) => enabled)
322
336
  .map(([chainId]) => chainId);
323
- }, _NetworkConnectionBannerController_collectNetworksWithMetadata = function _NetworkConnectionBannerController_collectNetworksWithMetadata({ networkConfigurationsByChainId, networksMetadata, }, { enabledNetworkMap, }) {
337
+ }, _NetworkConnectionBannerController_collectEnabledNetworks = function _NetworkConnectionBannerController_collectEnabledNetworks({ networkConfigurationsByChainId, networksMetadata, }, { enabledNetworkMap, }) {
324
338
  return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_getEnabledEvmChainIds).call(this, enabledNetworkMap).flatMap((chainId) => {
325
339
  const networkConfiguration = networkConfigurationsByChainId[chainId];
326
340
  if (!networkConfiguration) {
@@ -331,10 +345,6 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
331
345
  if (!defaultRpcEndpoint) {
332
346
  return [];
333
347
  }
334
- const metadata = networksMetadata[defaultRpcEndpoint.networkClientId];
335
- if (!metadata) {
336
- return [];
337
- }
338
348
  return [
339
349
  {
340
350
  chainId,
@@ -342,7 +352,7 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
342
352
  rpcEndpoints,
343
353
  defaultRpcEndpointIndex,
344
354
  defaultRpcEndpoint,
345
- metadata,
355
+ metadata: networksMetadata[defaultRpcEndpoint.networkClientId],
346
356
  },
347
357
  ];
348
358
  });
@@ -363,20 +373,14 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
363
373
  rpcUrl: defaultRpcEndpoint.url,
364
374
  isInfuraEndpoint,
365
375
  switchableInfuraNetworkClientId,
366
- domain: (0, url_utils_1.getDomain)(defaultRpcEndpoint.url),
367
376
  };
368
- }, _NetworkConnectionBannerController_pickBannerNetwork = function _NetworkConnectionBannerController_pickBannerNetwork(failedNetworks, totalNetworksWithMetadata) {
377
+ }, _NetworkConnectionBannerController_pickFailedNetworkToDisplay = function _NetworkConnectionBannerController_pickFailedNetworkToDisplay(failedNetworks, totalEnabledNetworks) {
369
378
  if (failedNetworks.length === 0) {
370
379
  return null;
371
380
  }
372
381
  const firstCustomFailed = failedNetworks.find((entry) => !entry.isInfuraEndpoint);
373
- const distinctDomains = new Set(failedNetworks
374
- .map((entry) => entry.domain)
375
- .filter((domain) => domain !== null)).size;
376
- const areAllKnownNetworksFailed = failedNetworks.length === totalNetworksWithMetadata;
377
- if (!firstCustomFailed &&
378
- distinctDomains <= 1 &&
379
- !areAllKnownNetworksFailed) {
382
+ const areAllEnabledNetworksFailed = failedNetworks.length === totalEnabledNetworks;
383
+ if (!firstCustomFailed && !areAllEnabledNetworksFailed) {
380
384
  return null;
381
385
  }
382
386
  return firstCustomFailed ?? failedNetworks[0];
@@ -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 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, getIsInfuraEndpoint } 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 *\n * The keys carry the controller-domain prefix (like\n * `ConnectivityController`'s `connectivityStatus`) because some clients merge\n * all controller states into one flat object, where generic names like\n * `status` collide across controllers.\n */\nexport type NetworkConnectionBannerControllerState = {\n networkConnectionBannerStatus: NetworkConnectionBannerStatus;\n networkConnectionBannerNetwork: FailedNetwork | null;\n};\n\nconst networkConnectionBannerControllerMetadata = {\n networkConnectionBannerStatus: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n networkConnectionBannerNetwork: {\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 networkConnectionBannerStatus: 'available',\n networkConnectionBannerNetwork: 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 used to decide when a wider provider outage is in play.\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.networkConnectionBannerStatus !== 'available' &&\n this.state.networkConnectionBannerNetwork?.networkClientId ===\n failedNetwork.networkClientId\n ) {\n this.update((state) => {\n state.networkConnectionBannerNetwork = 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.networkConnectionBannerStatus = 'available';\n state.networkConnectionBannerNetwork = 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.networkConnectionBannerStatus = 'degraded';\n state.networkConnectionBannerNetwork = 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.networkConnectionBannerStatus = 'unavailable';\n state.networkConnectionBannerNetwork = 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 (\n this.state.networkConnectionBannerStatus !== 'available' ||\n this.state.networkConnectionBannerNetwork !== null\n ) {\n this.update((state) => {\n state.networkConnectionBannerStatus = 'available';\n state.networkConnectionBannerNetwork = 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"]}
1
+ {"version":3,"file":"NetworkConnectionBannerController.cjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAKA,+DAA2D;AAC3D,+EAG2C;AAgB3C,qEAA6D;AAM7D,2FAAkF;AAElF,2CAAqD;AACrD,uCAA0C;AAG1C,+CAAkD;AAElD;;;;GAIG;AACH,MAAM,eAAe,GAAG,mCAAmC,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,sBAAsB,GAAG,CAC7B,KAAmB,EACe,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC;AAE9D;;;;;;GAMG;AACH,MAAM,oCAAoC,GAAG,CAC3C,KAAmB,EAC6B,EAAE,CAClD,KAAK,CAAC,8BAA8B,CAAC;AAEvC;;;;;;;;GAQG;AACH,MAAM,6BAA6B,GAAG,IAAA,yBAAc,EAClD,CAAC,sBAAsB,EAAE,oCAAoC,CAAC,EAC9D,CAAC,gBAAgB,EAAE,8BAA8B,EAAE,EAAE,CAAC,CAAC;IACrD,gBAAgB;IAChB,8BAA8B;CAC/B,CAAC,CACH,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,uCAAuC,GAAG,IAAA,yBAAc,EAC5D,CAAC,uDAAuB,CAAC,EACzB,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC,CAC/C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,kCAAkC,GAAG,IAAA,yBAAc,EACvD,CAAC,yDAA+B,CAAC,wBAAwB,CAAC,EAC1D,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC,CACjD,CAAC;AA6DF,MAAM,yCAAyC,GAAG;IAChD,6BAA6B,EAAE;QAC7B,OAAO,EAAE,KAAK;QACd,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,IAAI;KACf;IACD,8BAA8B,EAAE;QAC9B,OAAO,EAAE,KAAK;QACd,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,IAAI;KACf;CAC8D,CAAC;AAElE;;;;GAIG;AACH,SAAgB,gDAAgD;IAC9D,OAAO;QACL,6BAA6B,EAAE,WAAW;QAC1C,8BAA8B,EAAE,IAAI;KACrC,CAAC;AACJ,CAAC;AALD,4GAKC;AAED;;;GAGG;AACH,MAAM,uBAAuB,GAAG,IAAK,CAAC;AAEtC;;;GAGG;AACH,MAAM,0BAA0B,GAAG,KAAM,CAAC;AAE1C,MAAM,yBAAyB,GAAG;IAChC,OAAO;IACP,MAAM;IACN,eAAe;IACf,kCAAkC;CAC1B,CAAC;AA4EX;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAa,iCAAkC,SAAQ,gCAItD;IASC;;;;;OAKG;IACH,YAAY,EAAE,SAAS,EAA4C;QACjE,KAAK,CAAC;YACJ,SAAS;YACT,QAAQ,EAAE,yCAAyC;YACnD,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,gDAAgD,EAAE;SAC1D,CAAC,CAAC;;QApBL,mEAA0D;QAE1D,sEAA6D;QAE7D,4EAA4C;QAE5C,uDAAa,KAAK,EAAC;QAgBjB,0EAA0E;QAC1E,iDAAiD;QACjD,sFAAsF;QACtF,IAAI,CAAC,SAAS,CAAC,SAAS,CACtB,+BAA+B,EAC/B,CAAC,sBAAsB,EAAE,EAAE,CACzB,uBAAA,IAAI,qGAAc,MAAlB,IAAI,EAAe;YACjB,sBAAsB;YACtB,gCAAgC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CACnD,sCAAsC,CACvC;YACD,2BAA2B,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAC9C,iCAAiC,CAClC;SACF,CAAC,EACJ,6BAA6B,CAC9B,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,SAAS,CACtB,yCAAyC,EACzC,CAAC,gCAAgC,EAAE,EAAE,CACnC,uBAAA,IAAI,qGAAc,MAAlB,IAAI,EAAe;YACjB,sBAAsB,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CACzC,4BAA4B,CAC7B;YACD,gCAAgC;YAChC,2BAA2B,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAC9C,iCAAiC,CAClC;SACF,CAAC,EACJ,uCAAuC,CACxC,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,SAAS,CACtB,oCAAoC,EACpC,CAAC,2BAA2B,EAAE,EAAE,CAC9B,uBAAA,IAAI,qGAAc,MAAlB,IAAI,EAAe;YACjB,sBAAsB,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CACzC,4BAA4B,CAC7B;YACD,gCAAgC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CACnD,sCAAsC,CACvC;YACD,2BAA2B;SAC5B,CAAC,EACJ,kCAAkC,CACnC,CAAC;QACF,wCAAwC;QAExC,IAAI,CAAC,SAAS,CAAC,4BAA4B,CACzC,IAAI,EACJ,yBAAyB,CAC1B,CAAC;IACJ,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK;QACH,IAAI,uBAAA,IAAI,oDAAW,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QAED,uBAAA,IAAI,gDAAc,IAAI,MAAA,CAAC;QACvB,uBAAA,IAAI,qGAAc,MAAlB,IAAI,EAAe;YACjB,sBAAsB,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,4BAA4B,CAAC;YACzE,gCAAgC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CACnD,sCAAsC,CACvC;YACD,2BAA2B,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAC9C,iCAAiC,CAClC;SACF,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,IAAI;QACF,IAAI,CAAC,uBAAA,IAAI,oDAAW,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QAED,uBAAA,IAAI,gDAAc,KAAK,MAAA,CAAC;QACxB,uBAAA,IAAI,oGAAa,MAAjB,IAAI,CAAe,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,aAAa;QACX,uBAAA,IAAI,oGAAa,MAAjB,IAAI,CAAe,CAAC;IACtB,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,gCAAgC,CAAC,OAAY;QACjD,MAAM,oBAAoB,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAC9C,oDAAoD,EACpD,OAAO,CACR,CAAC;QACF,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,gDAAgD,OAAO,IAAI,CAC5D,CAAC;QACJ,CAAC;QAED,MAAM,mBAAmB,GAAG,oBAAoB,CAAC,YAAY,CAAC,SAAS,CACrE,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAA,+BAAmB,EAAC,QAAQ,CAAC,GAAG,CAAC,CAChD,CAAC;QACF,IAAI,mBAAmB,KAAK,CAAC,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CACb,8CAA8C,OAAO,IAAI,CAC1D,CAAC;QACJ,CAAC;QACD,IAAI,mBAAmB,KAAK,oBAAoB,CAAC,uBAAuB,EAAE,CAAC;YACzE,gDAAgD;YAChD,OAAO;QACT,CAAC;QAED,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CACvB,iCAAiC,EACjC,OAAO,EACP;YACE,GAAG,oBAAoB;YACvB,uBAAuB,EAAE,mBAAmB;SAC7C,EACD,EAAE,mCAAmC,EAAE,mBAAmB,EAAE,CAC7D,CAAC;IACJ,CAAC;CA2PF;AApaD,8EAoaC;ybAzPe,EACZ,sBAAsB,EACtB,gCAAgC,EAChC,2BAA2B,GAc5B;IACC,IAAI,CAAC,uBAAA,IAAI,oDAAW,EAAE,CAAC;QACrB,OAAO;IACT,CAAC;IAED,IACE,2BAA2B,CAAC,kBAAkB;QAC9C,+CAAqB,CAAC,OAAO,EAC7B,CAAC;QACD,uBAAA,IAAI,oGAAa,MAAjB,IAAI,CAAe,CAAC;QACpB,OAAO;IACT,CAAC;IAED,MAAM,aAAa,GAAG,uBAAA,IAAI,0GAAmB,MAAvB,IAAI,EACxB,sBAAsB,EACtB,gCAAgC,CACjC,CAAC;IACF,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,uBAAA,IAAI,oGAAa,MAAjB,IAAI,CAAe,CAAC;QACpB,OAAO;IACT,CAAC;IAED,IACE,IAAI,CAAC,KAAK,CAAC,6BAA6B,KAAK,WAAW;QACxD,IAAI,CAAC,KAAK,CAAC,8BAA8B,EAAE,eAAe;YACxD,aAAa,CAAC,eAAe,EAC/B,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,kEAAkE;YAClE,sEAAsE;YACtE,eAAe;YACf,KAAK,CAAC,8BAA8B,GAAG,aAAa,CAAC;QACvD,CAAC,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,mEAAmE;IACnE,oEAAoE;IACpE,mEAAmE;IACnE,IAAI,uBAAA,IAAI,iEAAwB,KAAK,aAAa,CAAC,eAAe,EAAE,CAAC;QACnE,OAAO;IACT,CAAC;IAED,uBAAA,IAAI,oGAAa,MAAjB,IAAI,CAAe,CAAC;IACpB,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,KAAK,CAAC,6BAA6B,GAAG,WAAW,CAAC;QAClD,KAAK,CAAC,8BAA8B,GAAG,IAAI,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,4DAA4D;IAC5D,IAAI,CAAC,uBAAA,IAAI,oDAAW,EAAE,CAAC;QACrB,OAAO;IACT,CAAC;IAED,wEAAwE;IACxE,mEAAmE;IACnE,qEAAqE;IACrE,qEAAqE;IACrE,qCAAqC;IACrC,uBAAA,IAAI,6DAA2B,aAAa,CAAC,eAAe,MAAA,CAAC;IAC7D,uBAAA,IAAI,oDAAkB,UAAU,CAAC,GAAG,EAAE;QACpC,uBAAA,IAAI,oDAAkB,SAAS,MAAA,CAAC;QAChC,uBAAA,IAAI,6DAA2B,SAAS,MAAA,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,6BAA6B,GAAG,UAAU,CAAC;YACjD,KAAK,CAAC,8BAA8B,GAAG,aAAa,CAAC;QACvD,CAAC,CAAC,CAAC;QACH,4DAA4D;QAC5D,IAAI,CAAC,uBAAA,IAAI,oDAAW,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QACD,uBAAA,IAAI,uDAAqB,UAAU,CAAC,GAAG,EAAE;YACvC,uBAAA,IAAI,uDAAqB,SAAS,MAAA,CAAC;YACnC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpB,KAAK,CAAC,6BAA6B,GAAG,aAAa,CAAC;gBACpD,KAAK,CAAC,8BAA8B,GAAG,aAAa,CAAC;YACvD,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,0BAA0B,GAAG,uBAAuB,CAAC,MAAA,CAAC;IAC3D,CAAC,EAAE,uBAAuB,CAAC,MAAA,CAAC;AAC9B,CAAC;IAOC,uBAAA,IAAI,oGAAa,MAAjB,IAAI,CAAe,CAAC;IACpB,uBAAA,IAAI,6DAA2B,SAAS,MAAA,CAAC;IACzC,IACE,IAAI,CAAC,KAAK,CAAC,6BAA6B,KAAK,WAAW;QACxD,IAAI,CAAC,KAAK,CAAC,8BAA8B,KAAK,IAAI,EAClD,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,6BAA6B,GAAG,WAAW,CAAC;YAClD,KAAK,CAAC,8BAA8B,GAAG,IAAI,CAAC;QAC9C,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;IAGC,IAAI,uBAAA,IAAI,wDAAe,KAAK,SAAS,EAAE,CAAC;QACtC,YAAY,CAAC,uBAAA,IAAI,wDAAe,CAAC,CAAC;QAClC,uBAAA,IAAI,oDAAkB,SAAS,MAAA,CAAC;IAClC,CAAC;IACD,IAAI,uBAAA,IAAI,2DAAkB,KAAK,SAAS,EAAE,CAAC;QACzC,YAAY,CAAC,uBAAA,IAAI,2DAAkB,CAAC,CAAC;QACrC,uBAAA,IAAI,uDAAqB,SAAS,MAAA,CAAC;IACrC,CAAC;AACH,CAAC,uHAGC,YAGC,EACD,eAGC;IAED,MAAM,eAAe,GAAG,uBAAA,IAAI,+GAAwB,MAA5B,IAAI,EAC1B,YAAY,EACZ,eAAe,CAChB,CAAC;IACF,yEAAyE;IACzE,MAAM,cAAc,GAAG,eAAe;SACnC,MAAM,CACL,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CACf,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,kCAAa,CAAC,SAAS,CACxE;SACA,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,uBAAA,IAAI,2GAAoB,MAAxB,IAAI,EAAqB,OAAO,CAAC,CAAC,CAAC;IACvD,OAAO,uBAAA,IAAI,mHAA4B,MAAhC,IAAI,EACT,cAAc,EACd,eAAe,CAAC,MAAM,CACvB,CAAC;AACJ,CAAC,+HAGC,iBAAwE;IAExE,OAAO,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,0BAAkB,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;SACtE,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC;SAChC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,OAAc,CAAC,CAAC;AACxC,CAAC,iIAGC,EACE,8BAA8B,EAC9B,gBAAgB,GAIjB,EACD,EACE,iBAAiB,GAC2C;IAE9D,OAAO,uBAAA,IAAI,8GAAuB,MAA3B,IAAI,EAAwB,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACxE,MAAM,oBAAoB,GAAG,8BAA8B,CAAC,OAAO,CAAC,CAAC;QACrE,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC1B,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,EAAE,YAAY,EAAE,uBAAuB,EAAE,IAAI,EAAE,GACnD,oBAAoB,CAAC;QACvB,MAAM,kBAAkB,GAAG,YAAY,CAAC,uBAAuB,CAAC,CAAC;QACjE,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACxB,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO;YACL;gBACE,OAAO;gBACP,IAAI;gBACJ,YAAY;gBACZ,uBAAuB;gBACvB,kBAAkB;gBAClB,QAAQ,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,eAAe,CAAC;aAC/D;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,yHAEmB,EAClB,OAAO,EACP,IAAI,EACJ,YAAY,EACZ,uBAAuB,EACvB,kBAAkB,GACH;IACf,MAAM,gBAAgB,GAAG,IAAA,+BAAmB,EAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAErE,qEAAqE;IACrE,0CAA0C;IAC1C,IAAI,+BAA+B,GAAkB,IAAI,CAAC;IAC1D,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,cAAc,GAAG,YAAY,CAAC,IAAI,CACtC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,CAClB,KAAK,KAAK,uBAAuB;YACjC,IAAA,+BAAmB,EAAC,QAAQ,CAAC,GAAG,CAAC,CACpC,CAAC;QACF,+BAA+B,GAAG,cAAc,EAAE,eAAe,IAAI,IAAI,CAAC;IAC5E,CAAC;IAED,OAAO;QACL,OAAO;QACP,eAAe,EAAE,kBAAkB,CAAC,eAAe;QACnD,IAAI;QACJ,MAAM,EAAE,kBAAkB,CAAC,GAAG;QAC9B,gBAAgB;QAChB,+BAA+B;KAChC,CAAC;AACJ,CAAC,yIAGC,cAA+B,EAC/B,oBAA4B;IAE5B,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,iBAAiB,GAAG,cAAc,CAAC,IAAI,CAC3C,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CACnC,CAAC;IACF,MAAM,2BAA2B,GAC/B,cAAc,CAAC,MAAM,KAAK,oBAAoB,CAAC;IAEjD,IAAI,CAAC,iBAAiB,IAAI,CAAC,2BAA2B,EAAE,CAAC;QACvD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,iBAAiB,IAAI,cAAc,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC","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 { getIsInfuraEndpoint } 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 * An enabled network from `NetworkController` state with a default RPC\n * endpoint. Used as the input to the failed-network detection pipeline.\n * `metadata` is missing until the network's connectivity has been looked up.\n */\ntype EnabledNetwork = {\n chainId: Hex;\n name: string;\n rpcEndpoints: NetworkConfiguration['rpcEndpoints'];\n defaultRpcEndpointIndex: number;\n defaultRpcEndpoint: NetworkConfiguration['rpcEndpoints'][number];\n metadata: NetworkMetadata | undefined;\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\n/**\n * State for the {@link NetworkConnectionBannerController}.\n *\n * The keys carry the controller-domain prefix (like\n * `ConnectivityController`'s `connectivityStatus`) because some clients merge\n * all controller states into one flat object, where generic names like\n * `status` collide across controllers.\n */\nexport type NetworkConnectionBannerControllerState = {\n networkConnectionBannerStatus: NetworkConnectionBannerStatus;\n networkConnectionBannerNetwork: FailedNetwork | null;\n};\n\nconst networkConnectionBannerControllerMetadata = {\n networkConnectionBannerStatus: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n networkConnectionBannerNetwork: {\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 networkConnectionBannerStatus: 'available',\n networkConnectionBannerNetwork: 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 and the 5s / 30s timer escalation.\n *\n * The banner shows when:\n *\n * - A 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 * - Every enabled EVM network is reporting a failed connectivity status\n * (escape hatch so Infura-only setups still get a signal).\n *\n * A partial Infura outage alongside healthy peers is suppressed since it\n * usually resolves on its own and the user cannot act on it. When a custom\n * failure is present, it's surfaced first so the \"Switch to MetaMask default\n * RPC\" CTA targets the network the user 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({\n networkControllerState,\n networkEnablementControllerState: this.messenger.call(\n 'NetworkEnablementController:getState',\n ),\n connectivityControllerState: this.messenger.call(\n 'ConnectivityController:getState',\n ),\n }),\n selectNetworkControllerFields,\n );\n this.messenger.subscribe(\n 'NetworkEnablementController:stateChange',\n (networkEnablementControllerState) =>\n this.#refreshState({\n networkControllerState: this.messenger.call(\n 'NetworkController:getState',\n ),\n networkEnablementControllerState,\n connectivityControllerState: this.messenger.call(\n 'ConnectivityController:getState',\n ),\n }),\n selectNetworkEnablementControllerFields,\n );\n this.messenger.subscribe(\n 'ConnectivityController:stateChange',\n (connectivityControllerState) =>\n this.#refreshState({\n networkControllerState: this.messenger.call(\n 'NetworkController:getState',\n ),\n networkEnablementControllerState: this.messenger.call(\n 'NetworkEnablementController:getState',\n ),\n connectivityControllerState,\n }),\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 networkControllerState: this.messenger.call('NetworkController:getState'),\n networkEnablementControllerState: this.messenger.call(\n 'NetworkEnablementController:getState',\n ),\n connectivityControllerState: this.messenger.call(\n 'ConnectivityController:getState',\n ),\n });\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 if (\n connectivityControllerState.connectivityStatus ===\n CONNECTIVITY_STATUSES.Offline\n ) {\n this.#resetBanner();\n return;\n }\n\n const failedNetwork = this.#findFailedNetwork(\n networkControllerState,\n networkEnablementControllerState,\n );\n if (!failedNetwork) {\n this.#resetBanner();\n return;\n }\n\n if (\n this.state.networkConnectionBannerStatus !== 'available' &&\n this.state.networkConnectionBannerNetwork?.networkClientId ===\n failedNetwork.networkClientId\n ) {\n this.update((state) => {\n // Even if the network client ID has not changed, save the current\n // version of the failed network to state in case the user has updated\n // its RPC URL.\n state.networkConnectionBannerNetwork = failedNetwork;\n });\n return;\n }\n\n // A degraded timer is already counting down for this same network.\n // Repeated `stateChange` events must not clear and restart it, or a\n // steady stream of updates could postpone the banner indefinitely.\n if (this.#pendingNetworkClientId === failedNetwork.networkClientId) {\n return;\n }\n\n this.#clearTimers();\n this.update((state) => {\n state.networkConnectionBannerStatus = 'available';\n state.networkConnectionBannerNetwork = null;\n });\n\n // If `stop` is called before scheduling timers, bail early.\n if (!this.#isStarted) {\n return;\n }\n\n // Remember which network the pending timer is for (see the guard above)\n // and capture the failing network at schedule time. If the failure\n // resolves or a different network becomes the banner target while we\n // wait, the subscription handlers re-enter this method and cancel or\n // replace the timer 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.networkConnectionBannerStatus = 'degraded';\n state.networkConnectionBannerNetwork = failedNetwork;\n });\n // If `stop` is called before scheduling timers, bail early.\n if (!this.#isStarted) {\n return;\n }\n this.#unavailableTimer = setTimeout(() => {\n this.#unavailableTimer = undefined;\n this.update((state) => {\n state.networkConnectionBannerStatus = 'unavailable';\n state.networkConnectionBannerNetwork = 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 (\n this.state.networkConnectionBannerStatus !== 'available' ||\n this.state.networkConnectionBannerNetwork !== null\n ) {\n this.update((state) => {\n state.networkConnectionBannerStatus = 'available';\n state.networkConnectionBannerNetwork = 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 enabledNetworks = this.#collectEnabledNetworks(\n networkState,\n enablementState,\n );\n // Networks whose connectivity has not been looked up yet are not failed.\n const failedNetworks = enabledNetworks\n .filter(\n ({ metadata }) =>\n metadata !== undefined && metadata.status !== NetworkStatus.Available,\n )\n .map((network) => this.#buildFailedNetwork(network));\n return this.#pickFailedNetworkToDisplay(\n failedNetworks,\n enabledNetworks.length,\n );\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 #collectEnabledNetworks(\n {\n networkConfigurationsByChainId,\n networksMetadata,\n }: Pick<\n NetworkState,\n 'networkConfigurationsByChainId' | 'networksMetadata'\n >,\n {\n enabledNetworkMap,\n }: Pick<NetworkEnablementControllerState, 'enabledNetworkMap'>,\n ): EnabledNetwork[] {\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 return [\n {\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n metadata: networksMetadata[defaultRpcEndpoint.networkClientId],\n },\n ];\n });\n }\n\n #buildFailedNetwork({\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n }: EnabledNetwork): 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 };\n }\n\n #pickFailedNetworkToDisplay(\n failedNetworks: FailedNetwork[],\n totalEnabledNetworks: 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 areAllEnabledNetworksFailed =\n failedNetworks.length === totalEnabledNetworks;\n\n if (!firstCustomFailed && !areAllEnabledNetworksFailed) {\n return null;\n }\n\n return firstCustomFailed ?? failedNetworks[0];\n }\n}\n"]}
@@ -38,11 +38,6 @@ export type FailedNetwork = {
38
38
  * no Infura alternative exists.
39
39
  */
40
40
  switchableInfuraNetworkClientId: string | null;
41
- /**
42
- * The registrable domain (eTLD+1) of `rpcUrl`, used to group endpoints by
43
- * provider. `null` when the URL is invalid.
44
- */
45
- domain: string | null;
46
41
  };
47
42
  /**
48
43
  * State for the {@link NetworkConnectionBannerController}.
@@ -108,22 +103,19 @@ export type NetworkConnectionBannerControllerOptions = {
108
103
  /**
109
104
  * NetworkConnectionBannerController decides whether the network connection
110
105
  * banner should be shown for the user, and which failing network it should
111
- * describe. It encapsulates the rule, the 5s / 30s timer escalation, and the
112
- * eTLD+1 grouping used to decide when a wider provider outage is in play.
106
+ * describe. It encapsulates the rule and the 5s / 30s timer escalation.
113
107
  *
114
108
  * The banner shows when:
115
109
  *
116
- * - The first failing network's default RPC is a custom (non-Infura) endpoint
117
- * users always want to be informed about errors with RPCs they've chosen.
118
- * - Failed RPCs span more than one registrable domain (likely client-side).
119
- * - Every enabled EVM network with known connectivity status is failing
120
- * (escape hatch for single-network setups so they still get a signal).
110
+ * - A failing network's default RPC is a custom (non-Infura) endpoint
111
+ * users always want to be informed about errors with RPCs they've chosen.
112
+ * - Every enabled EVM network is reporting a failed connectivity status
113
+ * (escape hatch so Infura-only setups still get a signal).
121
114
  *
122
- * A wide single-provider outage (e.g. every `*.infura.io` network goes down at
123
- * once) collapses to one domain and is suppressed, except in the all-down
124
- * single-network case. When a custom failure is present, it's surfaced first
125
- * so the "Switch to MetaMask default RPC" CTA targets the network the user
126
- * can act on.
115
+ * A partial Infura outage alongside healthy peers is suppressed since it
116
+ * usually resolves on its own and the user cannot act on it. When a custom
117
+ * failure is present, it's surfaced first so the "Switch to MetaMask default
118
+ * RPC" CTA targets the network the user can act on.
127
119
  *
128
120
  * Clients only need to render the banner from the controller's state and wire
129
121
  * click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpcEndpoint}.
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController.d.cts","sourceRoot":"","sources":["../src/NetworkConnectionBannerController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"NetworkConnectionBannerController.d.cts","sourceRoot":"","sources":["../src/NetworkConnectionBannerController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,2BAA2B,EAE5B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAK3D,OAAO,KAAK,EACV,oCAAoC,EAEpC,sCAAsC,EACvC,0CAA0C;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EAEV,uDAAuD,EACvD,+BAA+B,EAC/B,oCAAoC,EACpC,iCAAiC,EAGlC,qCAAqC;AAEtC,OAAO,KAAK,EACV,yCAAyC,EAEzC,2CAA2C,EAC5C,gDAAgD;AAEjD,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAI3C,OAAO,KAAK,EAAE,8CAA8C,EAAE,oEAAgE;AAG9H;;;;GAIG;AACH,QAAA,MAAM,eAAe,sCAAsC,CAAC;AAiE5D;;;;GAIG;AACH,MAAM,MAAM,6BAA6B,GACrC,WAAW,GACX,UAAU,GACV,aAAa,CAAC;AAgBlB;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,2CAA2C;IAC3C,OAAO,EAAE,GAAG,CAAC;IACb,iEAAiE;IACjE,eAAe,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;CAChD,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,sCAAsC,GAAG;IACnD,6BAA6B,EAAE,6BAA6B,CAAC;IAC7D,8BAA8B,EAAE,aAAa,GAAG,IAAI,CAAC;CACtD,CAAC;AAiBF;;;;GAIG;AACH,wBAAgB,gDAAgD,IAAI,sCAAsC,CAKzG;AAqBD;;GAEG;AACH,MAAM,MAAM,+CAA+C,GACzD,wBAAwB,CACtB,OAAO,eAAe,EACtB,sCAAsC,CACvC,CAAC;AAEJ;;;GAGG;AACH,MAAM,MAAM,wCAAwC,GAChD,+CAA+C,GAC/C,8CAA8C,CAAC;AAEnD;;;GAGG;AACH,KAAK,cAAc,GACf,+BAA+B,GAC/B,uDAAuD,GACvD,oCAAoC,GACpC,yCAAyC,GACzC,oCAAoC,CAAC;AAEzC;;;GAGG;AACH,MAAM,MAAM,kDAAkD,GAC5D,2BAA2B,CACzB,OAAO,eAAe,EACtB,sCAAsC,CACvC,CAAC;AAEJ;;;GAGG;AACH,MAAM,MAAM,uCAAuC,GACjD,kDAAkD,CAAC;AAErD;;;GAGG;AACH,KAAK,aAAa,GACd,iCAAiC,GACjC,2CAA2C,GAC3C,sCAAsC,CAAC;AAE3C;;;GAGG;AACH,MAAM,MAAM,0CAA0C,GAAG,SAAS,CAChE,OAAO,eAAe,EACtB,wCAAwC,GAAG,cAAc,EACzD,uCAAuC,GAAG,aAAa,CACxD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wCAAwC,GAAG;IACrD;;OAEG;IACH,SAAS,EAAE,0CAA0C,CAAC;CACvD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,iCAAkC,SAAQ,cAAc,CACnE,OAAO,eAAe,EACtB,sCAAsC,EACtC,0CAA0C,CAC3C;;IASC;;;;;OAKG;gBACS,EAAE,SAAS,EAAE,EAAE,wCAAwC;IA6DnE;;;;;;;;;OASG;IACH,KAAK,IAAI,IAAI;IAiBb;;;;OAIG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;;;;;;OAOG;IACG,gCAAgC,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;CA4RpE"}
@@ -38,11 +38,6 @@ export type FailedNetwork = {
38
38
  * no Infura alternative exists.
39
39
  */
40
40
  switchableInfuraNetworkClientId: string | null;
41
- /**
42
- * The registrable domain (eTLD+1) of `rpcUrl`, used to group endpoints by
43
- * provider. `null` when the URL is invalid.
44
- */
45
- domain: string | null;
46
41
  };
47
42
  /**
48
43
  * State for the {@link NetworkConnectionBannerController}.
@@ -108,22 +103,19 @@ export type NetworkConnectionBannerControllerOptions = {
108
103
  /**
109
104
  * NetworkConnectionBannerController decides whether the network connection
110
105
  * banner should be shown for the user, and which failing network it should
111
- * describe. It encapsulates the rule, the 5s / 30s timer escalation, and the
112
- * eTLD+1 grouping used to decide when a wider provider outage is in play.
106
+ * describe. It encapsulates the rule and the 5s / 30s timer escalation.
113
107
  *
114
108
  * The banner shows when:
115
109
  *
116
- * - The first failing network's default RPC is a custom (non-Infura) endpoint
117
- * users always want to be informed about errors with RPCs they've chosen.
118
- * - Failed RPCs span more than one registrable domain (likely client-side).
119
- * - Every enabled EVM network with known connectivity status is failing
120
- * (escape hatch for single-network setups so they still get a signal).
110
+ * - A failing network's default RPC is a custom (non-Infura) endpoint
111
+ * users always want to be informed about errors with RPCs they've chosen.
112
+ * - Every enabled EVM network is reporting a failed connectivity status
113
+ * (escape hatch so Infura-only setups still get a signal).
121
114
  *
122
- * A wide single-provider outage (e.g. every `*.infura.io` network goes down at
123
- * once) collapses to one domain and is suppressed, except in the all-down
124
- * single-network case. When a custom failure is present, it's surfaced first
125
- * so the "Switch to MetaMask default RPC" CTA targets the network the user
126
- * can act on.
115
+ * A partial Infura outage alongside healthy peers is suppressed since it
116
+ * usually resolves on its own and the user cannot act on it. When a custom
117
+ * failure is present, it's surfaced first so the "Switch to MetaMask default
118
+ * RPC" CTA targets the network the user can act on.
127
119
  *
128
120
  * Clients only need to render the banner from the controller's state and wire
129
121
  * click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpcEndpoint}.
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController.d.mts","sourceRoot":"","sources":["../src/NetworkConnectionBannerController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"NetworkConnectionBannerController.d.mts","sourceRoot":"","sources":["../src/NetworkConnectionBannerController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,2BAA2B,EAE5B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAK3D,OAAO,KAAK,EACV,oCAAoC,EAEpC,sCAAsC,EACvC,0CAA0C;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EAEV,uDAAuD,EACvD,+BAA+B,EAC/B,oCAAoC,EACpC,iCAAiC,EAGlC,qCAAqC;AAEtC,OAAO,KAAK,EACV,yCAAyC,EAEzC,2CAA2C,EAC5C,gDAAgD;AAEjD,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAI3C,OAAO,KAAK,EAAE,8CAA8C,EAAE,oEAAgE;AAG9H;;;;GAIG;AACH,QAAA,MAAM,eAAe,sCAAsC,CAAC;AAiE5D;;;;GAIG;AACH,MAAM,MAAM,6BAA6B,GACrC,WAAW,GACX,UAAU,GACV,aAAa,CAAC;AAgBlB;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,2CAA2C;IAC3C,OAAO,EAAE,GAAG,CAAC;IACb,iEAAiE;IACjE,eAAe,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;CAChD,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,sCAAsC,GAAG;IACnD,6BAA6B,EAAE,6BAA6B,CAAC;IAC7D,8BAA8B,EAAE,aAAa,GAAG,IAAI,CAAC;CACtD,CAAC;AAiBF;;;;GAIG;AACH,wBAAgB,gDAAgD,IAAI,sCAAsC,CAKzG;AAqBD;;GAEG;AACH,MAAM,MAAM,+CAA+C,GACzD,wBAAwB,CACtB,OAAO,eAAe,EACtB,sCAAsC,CACvC,CAAC;AAEJ;;;GAGG;AACH,MAAM,MAAM,wCAAwC,GAChD,+CAA+C,GAC/C,8CAA8C,CAAC;AAEnD;;;GAGG;AACH,KAAK,cAAc,GACf,+BAA+B,GAC/B,uDAAuD,GACvD,oCAAoC,GACpC,yCAAyC,GACzC,oCAAoC,CAAC;AAEzC;;;GAGG;AACH,MAAM,MAAM,kDAAkD,GAC5D,2BAA2B,CACzB,OAAO,eAAe,EACtB,sCAAsC,CACvC,CAAC;AAEJ;;;GAGG;AACH,MAAM,MAAM,uCAAuC,GACjD,kDAAkD,CAAC;AAErD;;;GAGG;AACH,KAAK,aAAa,GACd,iCAAiC,GACjC,2CAA2C,GAC3C,sCAAsC,CAAC;AAE3C;;;GAGG;AACH,MAAM,MAAM,0CAA0C,GAAG,SAAS,CAChE,OAAO,eAAe,EACtB,wCAAwC,GAAG,cAAc,EACzD,uCAAuC,GAAG,aAAa,CACxD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wCAAwC,GAAG;IACrD;;OAEG;IACH,SAAS,EAAE,0CAA0C,CAAC;CACvD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,iCAAkC,SAAQ,cAAc,CACnE,OAAO,eAAe,EACtB,sCAAsC,EACtC,0CAA0C,CAC3C;;IASC;;;;;OAKG;gBACS,EAAE,SAAS,EAAE,EAAE,wCAAwC;IA6DnE;;;;;;;;;OASG;IACH,KAAK,IAAI,IAAI;IAiBb;;;;OAIG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;;;;;;OAOG;IACG,gCAAgC,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;CA4RpE"}
@@ -9,14 +9,14 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
9
9
  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");
10
10
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
11
  };
12
- 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;
12
+ var _NetworkConnectionBannerController_instances, _NetworkConnectionBannerController_degradedTimer, _NetworkConnectionBannerController_unavailableTimer, _NetworkConnectionBannerController_pendingNetworkClientId, _NetworkConnectionBannerController_isStarted, _NetworkConnectionBannerController_refreshState, _NetworkConnectionBannerController_resetBanner, _NetworkConnectionBannerController_clearTimers, _NetworkConnectionBannerController_findFailedNetwork, _NetworkConnectionBannerController_getEnabledEvmChainIds, _NetworkConnectionBannerController_collectEnabledNetworks, _NetworkConnectionBannerController_buildFailedNetwork, _NetworkConnectionBannerController_pickFailedNetworkToDisplay;
13
13
  import { BaseController } from "@metamask/base-controller";
14
14
  import { CONNECTIVITY_STATUSES, connectivityControllerSelectors } from "@metamask/connectivity-controller";
15
15
  import { NetworkStatus } from "@metamask/network-controller";
16
16
  import { selectEnabledNetworkMap } from "@metamask/network-enablement-controller";
17
17
  import { KnownCaipNamespace } from "@metamask/utils";
18
18
  import { createSelector } from "reselect";
19
- import { getDomain, getIsInfuraEndpoint } from "./url-utils.mjs";
19
+ import { getIsInfuraEndpoint } from "./url-utils.mjs";
20
20
  /**
21
21
  * The name of the {@link NetworkConnectionBannerController}, used to namespace
22
22
  * the controller's actions and events and to namespace the controller's state
@@ -111,22 +111,19 @@ const MESSENGER_EXPOSED_METHODS = [
111
111
  /**
112
112
  * NetworkConnectionBannerController decides whether the network connection
113
113
  * banner should be shown for the user, and which failing network it should
114
- * describe. It encapsulates the rule, the 5s / 30s timer escalation, and the
115
- * eTLD+1 grouping used to decide when a wider provider outage is in play.
114
+ * describe. It encapsulates the rule and the 5s / 30s timer escalation.
116
115
  *
117
116
  * The banner shows when:
118
117
  *
119
- * - The first failing network's default RPC is a custom (non-Infura) endpoint
120
- * users always want to be informed about errors with RPCs they've chosen.
121
- * - Failed RPCs span more than one registrable domain (likely client-side).
122
- * - Every enabled EVM network with known connectivity status is failing
123
- * (escape hatch for single-network setups so they still get a signal).
118
+ * - A failing network's default RPC is a custom (non-Infura) endpoint
119
+ * users always want to be informed about errors with RPCs they've chosen.
120
+ * - Every enabled EVM network is reporting a failed connectivity status
121
+ * (escape hatch so Infura-only setups still get a signal).
124
122
  *
125
- * A wide single-provider outage (e.g. every `*.infura.io` network goes down at
126
- * once) collapses to one domain and is suppressed, except in the all-down
127
- * single-network case. When a custom failure is present, it's surfaced first
128
- * so the "Switch to MetaMask default RPC" CTA targets the network the user
129
- * can act on.
123
+ * A partial Infura outage alongside healthy peers is suppressed since it
124
+ * usually resolves on its own and the user cannot act on it. When a custom
125
+ * failure is present, it's surfaced first so the "Switch to MetaMask default
126
+ * RPC" CTA targets the network the user can act on.
130
127
  *
131
128
  * Clients only need to render the banner from the controller's state and wire
132
129
  * click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpcEndpoint}.
@@ -153,9 +150,21 @@ export class NetworkConnectionBannerController extends BaseController {
153
150
  // Upstream controllers still expose :stateChange; switch to :stateChanged
154
151
  // once those packages migrate their event types.
155
152
  /* eslint-disable no-restricted-syntax -- awaiting upstream :stateChanged migration */
156
- this.messenger.subscribe('NetworkController:stateChange', (networkControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, { networkControllerState }), selectNetworkControllerFields);
157
- this.messenger.subscribe('NetworkEnablementController:stateChange', (networkEnablementControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, { networkEnablementControllerState }), selectNetworkEnablementControllerFields);
158
- this.messenger.subscribe('ConnectivityController:stateChange', (connectivityControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, { connectivityControllerState }), selectConnectivityControllerFields);
153
+ this.messenger.subscribe('NetworkController:stateChange', (networkControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, {
154
+ networkControllerState,
155
+ networkEnablementControllerState: this.messenger.call('NetworkEnablementController:getState'),
156
+ connectivityControllerState: this.messenger.call('ConnectivityController:getState'),
157
+ }), selectNetworkControllerFields);
158
+ this.messenger.subscribe('NetworkEnablementController:stateChange', (networkEnablementControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, {
159
+ networkControllerState: this.messenger.call('NetworkController:getState'),
160
+ networkEnablementControllerState,
161
+ connectivityControllerState: this.messenger.call('ConnectivityController:getState'),
162
+ }), selectNetworkEnablementControllerFields);
163
+ this.messenger.subscribe('ConnectivityController:stateChange', (connectivityControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, {
164
+ networkControllerState: this.messenger.call('NetworkController:getState'),
165
+ networkEnablementControllerState: this.messenger.call('NetworkEnablementController:getState'),
166
+ connectivityControllerState,
167
+ }), selectConnectivityControllerFields);
159
168
  /* eslint-enable no-restricted-syntax */
160
169
  this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
161
170
  }
@@ -174,7 +183,11 @@ export class NetworkConnectionBannerController extends BaseController {
174
183
  return;
175
184
  }
176
185
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_isStarted, true, "f");
177
- __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this);
186
+ __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, {
187
+ networkControllerState: this.messenger.call('NetworkController:getState'),
188
+ networkEnablementControllerState: this.messenger.call('NetworkEnablementController:getState'),
189
+ connectivityControllerState: this.messenger.call('ConnectivityController:getState'),
190
+ });
178
191
  }
179
192
  /**
180
193
  * Stops evaluating network connection state. Clears any pending banner
@@ -221,21 +234,16 @@ export class NetworkConnectionBannerController extends BaseController {
221
234
  }, { replacementSelectedRpcEndpointIndex: infuraEndpointIndex });
222
235
  }
223
236
  }
224
- _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, } = {}) {
237
+ _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, }) {
225
238
  if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
226
239
  return;
227
240
  }
228
- const { connectivityStatus } = connectivityControllerState ??
229
- this.messenger.call('ConnectivityController:getState');
230
- if (connectivityStatus === CONNECTIVITY_STATUSES.Offline) {
241
+ if (connectivityControllerState.connectivityStatus ===
242
+ CONNECTIVITY_STATUSES.Offline) {
231
243
  __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
232
244
  return;
233
245
  }
234
- const networkState = networkControllerState ??
235
- this.messenger.call('NetworkController:getState');
236
- const enablementState = networkEnablementControllerState ??
237
- this.messenger.call('NetworkEnablementController:getState');
238
- const failedNetwork = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_findFailedNetwork).call(this, networkState, enablementState);
246
+ const failedNetwork = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_findFailedNetwork).call(this, networkControllerState, networkEnablementControllerState);
239
247
  if (!failedNetwork) {
240
248
  __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
241
249
  return;
@@ -244,10 +252,16 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
244
252
  this.state.networkConnectionBannerNetwork?.networkClientId ===
245
253
  failedNetwork.networkClientId) {
246
254
  this.update((state) => {
255
+ // Even if the network client ID has not changed, save the current
256
+ // version of the failed network to state in case the user has updated
257
+ // its RPC URL.
247
258
  state.networkConnectionBannerNetwork = failedNetwork;
248
259
  });
249
260
  return;
250
261
  }
262
+ // A degraded timer is already counting down for this same network.
263
+ // Repeated `stateChange` events must not clear and restart it, or a
264
+ // steady stream of updates could postpone the banner indefinitely.
251
265
  if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_pendingNetworkClientId, "f") === failedNetwork.networkClientId) {
252
266
  return;
253
267
  }
@@ -256,15 +270,15 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
256
270
  state.networkConnectionBannerStatus = 'available';
257
271
  state.networkConnectionBannerNetwork = null;
258
272
  });
259
- // A synchronous listener on our `stateChanged` event above may have
260
- // called `stop()` re-entrantly. Bail before scheduling anything.
273
+ // If `stop` is called before scheduling timers, bail early.
261
274
  if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
262
275
  return;
263
276
  }
264
- // Capture the failing network at schedule time. We trust the messenger
265
- // contract: if the failure resolves or the target changes during the
266
- // wait, our upstream subscriptions will have cancelled or replaced this
267
- // timer via `#clearTimers` before it fires.
277
+ // Remember which network the pending timer is for (see the guard above)
278
+ // and capture the failing network at schedule time. If the failure
279
+ // resolves or a different network becomes the banner target while we
280
+ // wait, the subscription handlers re-enter this method and cancel or
281
+ // replace the timer before it fires.
268
282
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_pendingNetworkClientId, failedNetwork.networkClientId, "f");
269
283
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_degradedTimer, setTimeout(() => {
270
284
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_degradedTimer, undefined, "f");
@@ -273,8 +287,7 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
273
287
  state.networkConnectionBannerStatus = 'degraded';
274
288
  state.networkConnectionBannerNetwork = failedNetwork;
275
289
  });
276
- // A synchronous listener on our `stateChanged` event above may have
277
- // called `stop()` re-entrantly. Bail before scheduling the escalation.
290
+ // If `stop` is called before scheduling timers, bail early.
278
291
  if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
279
292
  return;
280
293
  }
@@ -306,16 +319,17 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
306
319
  __classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, undefined, "f");
307
320
  }
308
321
  }, _NetworkConnectionBannerController_findFailedNetwork = function _NetworkConnectionBannerController_findFailedNetwork(networkState, enablementState) {
309
- const networksWithMetadata = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_collectNetworksWithMetadata).call(this, networkState, enablementState);
310
- const failedNetworks = networksWithMetadata
311
- .filter(({ metadata }) => metadata.status !== NetworkStatus.Available)
322
+ const enabledNetworks = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_collectEnabledNetworks).call(this, networkState, enablementState);
323
+ // Networks whose connectivity has not been looked up yet are not failed.
324
+ const failedNetworks = enabledNetworks
325
+ .filter(({ metadata }) => metadata !== undefined && metadata.status !== NetworkStatus.Available)
312
326
  .map((network) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_buildFailedNetwork).call(this, network));
313
- return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_pickBannerNetwork).call(this, failedNetworks, networksWithMetadata.length);
327
+ return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_pickFailedNetworkToDisplay).call(this, failedNetworks, enabledNetworks.length);
314
328
  }, _NetworkConnectionBannerController_getEnabledEvmChainIds = function _NetworkConnectionBannerController_getEnabledEvmChainIds(enabledNetworkMap) {
315
329
  return Object.entries(enabledNetworkMap[KnownCaipNamespace.Eip155] ?? {})
316
330
  .filter(([, enabled]) => enabled)
317
331
  .map(([chainId]) => chainId);
318
- }, _NetworkConnectionBannerController_collectNetworksWithMetadata = function _NetworkConnectionBannerController_collectNetworksWithMetadata({ networkConfigurationsByChainId, networksMetadata, }, { enabledNetworkMap, }) {
332
+ }, _NetworkConnectionBannerController_collectEnabledNetworks = function _NetworkConnectionBannerController_collectEnabledNetworks({ networkConfigurationsByChainId, networksMetadata, }, { enabledNetworkMap, }) {
319
333
  return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_getEnabledEvmChainIds).call(this, enabledNetworkMap).flatMap((chainId) => {
320
334
  const networkConfiguration = networkConfigurationsByChainId[chainId];
321
335
  if (!networkConfiguration) {
@@ -326,10 +340,6 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
326
340
  if (!defaultRpcEndpoint) {
327
341
  return [];
328
342
  }
329
- const metadata = networksMetadata[defaultRpcEndpoint.networkClientId];
330
- if (!metadata) {
331
- return [];
332
- }
333
343
  return [
334
344
  {
335
345
  chainId,
@@ -337,7 +347,7 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
337
347
  rpcEndpoints,
338
348
  defaultRpcEndpointIndex,
339
349
  defaultRpcEndpoint,
340
- metadata,
350
+ metadata: networksMetadata[defaultRpcEndpoint.networkClientId],
341
351
  },
342
352
  ];
343
353
  });
@@ -358,20 +368,14 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
358
368
  rpcUrl: defaultRpcEndpoint.url,
359
369
  isInfuraEndpoint,
360
370
  switchableInfuraNetworkClientId,
361
- domain: getDomain(defaultRpcEndpoint.url),
362
371
  };
363
- }, _NetworkConnectionBannerController_pickBannerNetwork = function _NetworkConnectionBannerController_pickBannerNetwork(failedNetworks, totalNetworksWithMetadata) {
372
+ }, _NetworkConnectionBannerController_pickFailedNetworkToDisplay = function _NetworkConnectionBannerController_pickFailedNetworkToDisplay(failedNetworks, totalEnabledNetworks) {
364
373
  if (failedNetworks.length === 0) {
365
374
  return null;
366
375
  }
367
376
  const firstCustomFailed = failedNetworks.find((entry) => !entry.isInfuraEndpoint);
368
- const distinctDomains = new Set(failedNetworks
369
- .map((entry) => entry.domain)
370
- .filter((domain) => domain !== null)).size;
371
- const areAllKnownNetworksFailed = failedNetworks.length === totalNetworksWithMetadata;
372
- if (!firstCustomFailed &&
373
- distinctDomains <= 1 &&
374
- !areAllKnownNetworksFailed) {
377
+ const areAllEnabledNetworksFailed = failedNetworks.length === totalEnabledNetworks;
378
+ if (!firstCustomFailed && !areAllEnabledNetworksFailed) {
375
379
  return null;
376
380
  }
377
381
  return firstCustomFailed ?? failedNetworks[0];
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController.mjs","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, getIsInfuraEndpoint } 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 *\n * The keys carry the controller-domain prefix (like\n * `ConnectivityController`'s `connectivityStatus`) because some clients merge\n * all controller states into one flat object, where generic names like\n * `status` collide across controllers.\n */\nexport type NetworkConnectionBannerControllerState = {\n networkConnectionBannerStatus: NetworkConnectionBannerStatus;\n networkConnectionBannerNetwork: FailedNetwork | null;\n};\n\nconst networkConnectionBannerControllerMetadata = {\n networkConnectionBannerStatus: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n networkConnectionBannerNetwork: {\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 networkConnectionBannerStatus: 'available',\n networkConnectionBannerNetwork: 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 used to decide when a wider provider outage is in play.\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.networkConnectionBannerStatus !== 'available' &&\n this.state.networkConnectionBannerNetwork?.networkClientId ===\n failedNetwork.networkClientId\n ) {\n this.update((state) => {\n state.networkConnectionBannerNetwork = 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.networkConnectionBannerStatus = 'available';\n state.networkConnectionBannerNetwork = 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.networkConnectionBannerStatus = 'degraded';\n state.networkConnectionBannerNetwork = 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.networkConnectionBannerStatus = 'unavailable';\n state.networkConnectionBannerNetwork = 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 (\n this.state.networkConnectionBannerStatus !== 'available' ||\n this.state.networkConnectionBannerNetwork !== null\n ) {\n this.update((state) => {\n state.networkConnectionBannerStatus = 'available';\n state.networkConnectionBannerNetwork = 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"]}
1
+ {"version":3,"file":"NetworkConnectionBannerController.mjs","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 { getIsInfuraEndpoint } 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 * An enabled network from `NetworkController` state with a default RPC\n * endpoint. Used as the input to the failed-network detection pipeline.\n * `metadata` is missing until the network's connectivity has been looked up.\n */\ntype EnabledNetwork = {\n chainId: Hex;\n name: string;\n rpcEndpoints: NetworkConfiguration['rpcEndpoints'];\n defaultRpcEndpointIndex: number;\n defaultRpcEndpoint: NetworkConfiguration['rpcEndpoints'][number];\n metadata: NetworkMetadata | undefined;\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\n/**\n * State for the {@link NetworkConnectionBannerController}.\n *\n * The keys carry the controller-domain prefix (like\n * `ConnectivityController`'s `connectivityStatus`) because some clients merge\n * all controller states into one flat object, where generic names like\n * `status` collide across controllers.\n */\nexport type NetworkConnectionBannerControllerState = {\n networkConnectionBannerStatus: NetworkConnectionBannerStatus;\n networkConnectionBannerNetwork: FailedNetwork | null;\n};\n\nconst networkConnectionBannerControllerMetadata = {\n networkConnectionBannerStatus: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n networkConnectionBannerNetwork: {\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 networkConnectionBannerStatus: 'available',\n networkConnectionBannerNetwork: 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 and the 5s / 30s timer escalation.\n *\n * The banner shows when:\n *\n * - A 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 * - Every enabled EVM network is reporting a failed connectivity status\n * (escape hatch so Infura-only setups still get a signal).\n *\n * A partial Infura outage alongside healthy peers is suppressed since it\n * usually resolves on its own and the user cannot act on it. When a custom\n * failure is present, it's surfaced first so the \"Switch to MetaMask default\n * RPC\" CTA targets the network the user 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({\n networkControllerState,\n networkEnablementControllerState: this.messenger.call(\n 'NetworkEnablementController:getState',\n ),\n connectivityControllerState: this.messenger.call(\n 'ConnectivityController:getState',\n ),\n }),\n selectNetworkControllerFields,\n );\n this.messenger.subscribe(\n 'NetworkEnablementController:stateChange',\n (networkEnablementControllerState) =>\n this.#refreshState({\n networkControllerState: this.messenger.call(\n 'NetworkController:getState',\n ),\n networkEnablementControllerState,\n connectivityControllerState: this.messenger.call(\n 'ConnectivityController:getState',\n ),\n }),\n selectNetworkEnablementControllerFields,\n );\n this.messenger.subscribe(\n 'ConnectivityController:stateChange',\n (connectivityControllerState) =>\n this.#refreshState({\n networkControllerState: this.messenger.call(\n 'NetworkController:getState',\n ),\n networkEnablementControllerState: this.messenger.call(\n 'NetworkEnablementController:getState',\n ),\n connectivityControllerState,\n }),\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 networkControllerState: this.messenger.call('NetworkController:getState'),\n networkEnablementControllerState: this.messenger.call(\n 'NetworkEnablementController:getState',\n ),\n connectivityControllerState: this.messenger.call(\n 'ConnectivityController:getState',\n ),\n });\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 if (\n connectivityControllerState.connectivityStatus ===\n CONNECTIVITY_STATUSES.Offline\n ) {\n this.#resetBanner();\n return;\n }\n\n const failedNetwork = this.#findFailedNetwork(\n networkControllerState,\n networkEnablementControllerState,\n );\n if (!failedNetwork) {\n this.#resetBanner();\n return;\n }\n\n if (\n this.state.networkConnectionBannerStatus !== 'available' &&\n this.state.networkConnectionBannerNetwork?.networkClientId ===\n failedNetwork.networkClientId\n ) {\n this.update((state) => {\n // Even if the network client ID has not changed, save the current\n // version of the failed network to state in case the user has updated\n // its RPC URL.\n state.networkConnectionBannerNetwork = failedNetwork;\n });\n return;\n }\n\n // A degraded timer is already counting down for this same network.\n // Repeated `stateChange` events must not clear and restart it, or a\n // steady stream of updates could postpone the banner indefinitely.\n if (this.#pendingNetworkClientId === failedNetwork.networkClientId) {\n return;\n }\n\n this.#clearTimers();\n this.update((state) => {\n state.networkConnectionBannerStatus = 'available';\n state.networkConnectionBannerNetwork = null;\n });\n\n // If `stop` is called before scheduling timers, bail early.\n if (!this.#isStarted) {\n return;\n }\n\n // Remember which network the pending timer is for (see the guard above)\n // and capture the failing network at schedule time. If the failure\n // resolves or a different network becomes the banner target while we\n // wait, the subscription handlers re-enter this method and cancel or\n // replace the timer 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.networkConnectionBannerStatus = 'degraded';\n state.networkConnectionBannerNetwork = failedNetwork;\n });\n // If `stop` is called before scheduling timers, bail early.\n if (!this.#isStarted) {\n return;\n }\n this.#unavailableTimer = setTimeout(() => {\n this.#unavailableTimer = undefined;\n this.update((state) => {\n state.networkConnectionBannerStatus = 'unavailable';\n state.networkConnectionBannerNetwork = 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 (\n this.state.networkConnectionBannerStatus !== 'available' ||\n this.state.networkConnectionBannerNetwork !== null\n ) {\n this.update((state) => {\n state.networkConnectionBannerStatus = 'available';\n state.networkConnectionBannerNetwork = 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 enabledNetworks = this.#collectEnabledNetworks(\n networkState,\n enablementState,\n );\n // Networks whose connectivity has not been looked up yet are not failed.\n const failedNetworks = enabledNetworks\n .filter(\n ({ metadata }) =>\n metadata !== undefined && metadata.status !== NetworkStatus.Available,\n )\n .map((network) => this.#buildFailedNetwork(network));\n return this.#pickFailedNetworkToDisplay(\n failedNetworks,\n enabledNetworks.length,\n );\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 #collectEnabledNetworks(\n {\n networkConfigurationsByChainId,\n networksMetadata,\n }: Pick<\n NetworkState,\n 'networkConfigurationsByChainId' | 'networksMetadata'\n >,\n {\n enabledNetworkMap,\n }: Pick<NetworkEnablementControllerState, 'enabledNetworkMap'>,\n ): EnabledNetwork[] {\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 return [\n {\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n metadata: networksMetadata[defaultRpcEndpoint.networkClientId],\n },\n ];\n });\n }\n\n #buildFailedNetwork({\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n }: EnabledNetwork): 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 };\n }\n\n #pickFailedNetworkToDisplay(\n failedNetworks: FailedNetwork[],\n totalEnabledNetworks: 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 areAllEnabledNetworksFailed =\n failedNetworks.length === totalEnabledNetworks;\n\n if (!firstCustomFailed && !areAllEnabledNetworksFailed) {\n return null;\n }\n\n return firstCustomFailed ?? failedNetworks[0];\n }\n}\n"]}
@@ -1,57 +1,6 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getIsInfuraEndpoint = exports.getDomain = exports.isLocalhostOrIPAddress = void 0;
7
- const ip_regex_1 = __importDefault(require("ip-regex/index.js"));
8
- const psl_1 = require("psl");
9
- /**
10
- * Check if a hostname is localhost or an IP address (v4 or v6). Public RPC
11
- * providers use domain names, not raw IP addresses, so a `true` result means
12
- * the host should not be reduced to an eTLD+1.
13
- *
14
- * @param hostname - The hostname to check.
15
- * @returns True if the hostname is localhost or an IP address.
16
- */
17
- function isLocalhostOrIPAddress(hostname) {
18
- const lowerHostname = hostname.toLowerCase();
19
- if (lowerHostname === 'localhost') {
20
- return true;
21
- }
22
- // Remove brackets from IPv6 addresses for testing (e.g., [::1] -> ::1)
23
- const hostnameWithoutBrackets = lowerHostname.replace(/^\[|\]$/gu, '');
24
- return (0, ip_regex_1.default)({ exact: true }).test(hostnameWithoutBrackets);
25
- }
26
- exports.isLocalhostOrIPAddress = isLocalhostOrIPAddress;
27
- /**
28
- * Registrable domain (eTLD+1) for a URL, computed via the Public Suffix List
29
- * so multi-part suffixes like ".co.uk" resolve correctly. Used to group RPC
30
- * endpoints by provider so a single provider's wide outage (e.g. *.infura.io)
31
- * is treated as one failure rather than many.
32
- *
33
- * Localhost, IP literals, and single-label hosts are returned verbatim rather
34
- * than reduced to a domain (psl returns null or garbage for those, and callers
35
- * grouping by domain still need to distinguish them).
36
- *
37
- * @param urlString - The URL to extract a domain from.
38
- * @returns The domain, or null if the URL is invalid.
39
- */
40
- function getDomain(urlString) {
41
- let url;
42
- try {
43
- url = new URL(urlString);
44
- }
45
- catch {
46
- return null;
47
- }
48
- const { hostname } = url;
49
- if (!hostname.includes('.') || isLocalhostOrIPAddress(hostname)) {
50
- return hostname;
51
- }
52
- return (0, psl_1.get)(hostname) ?? hostname;
53
- }
54
- exports.getDomain = getDomain;
3
+ exports.getIsInfuraEndpoint = void 0;
55
4
  /**
56
5
  * Whether an RPC URL is a MetaMask Infura endpoint. Stored network
57
6
  * configurations keep the `{infuraProjectId}` placeholder (substitution
@@ -1 +1 @@
1
- {"version":3,"file":"url-utils.cjs","sourceRoot":"","sources":["../src/url-utils.ts"],"names":[],"mappings":";;;;;;AAAA,iEAA+B;AAC/B,6BAAoC;AAEpC;;;;;;;GAOG;AACH,SAAgB,sBAAsB,CAAC,QAAgB;IACrD,MAAM,aAAa,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IAE7C,IAAI,aAAa,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,uEAAuE;IACvE,MAAM,uBAAuB,GAAG,aAAa,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAEvE,OAAO,IAAA,kBAAO,EAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;AAChE,CAAC;AAXD,wDAWC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,SAAS,CAAC,SAAiB;IACzC,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;IAEzB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,sBAAsB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChE,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,IAAA,SAAM,EAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC;AACtC,CAAC;AAfD,8BAeC;AAED;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CAAC,GAAW;IAC7C,OAAO,0DAA0D,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9E,CAAC;AAFD,kDAEC","sourcesContent":["import ipRegex from 'ip-regex';\nimport { get as pslGet } from 'psl';\n\n/**\n * Check if a hostname is localhost or an IP address (v4 or v6). Public RPC\n * providers use domain names, not raw IP addresses, so a `true` result means\n * the host should not be reduced to an eTLD+1.\n *\n * @param hostname - The hostname to check.\n * @returns True if the hostname is localhost or an IP address.\n */\nexport function isLocalhostOrIPAddress(hostname: string): boolean {\n const lowerHostname = hostname.toLowerCase();\n\n if (lowerHostname === 'localhost') {\n return true;\n }\n\n // Remove brackets from IPv6 addresses for testing (e.g., [::1] -> ::1)\n const hostnameWithoutBrackets = lowerHostname.replace(/^\\[|\\]$/gu, '');\n\n return ipRegex({ exact: true }).test(hostnameWithoutBrackets);\n}\n\n/**\n * Registrable domain (eTLD+1) for a URL, computed via the Public Suffix List\n * so multi-part suffixes like \".co.uk\" resolve correctly. Used to group RPC\n * endpoints by provider so a single provider's wide outage (e.g. *.infura.io)\n * is treated as one failure rather than many.\n *\n * Localhost, IP literals, and single-label hosts are returned verbatim rather\n * than reduced to a domain (psl returns null or garbage for those, and callers\n * grouping by domain still need to distinguish them).\n *\n * @param urlString - The URL to extract a domain from.\n * @returns The domain, or null if the URL is invalid.\n */\nexport function getDomain(urlString: string): string | null {\n let url: URL;\n try {\n url = new URL(urlString);\n } catch {\n return null;\n }\n\n const { hostname } = url;\n\n if (!hostname.includes('.') || isLocalhostOrIPAddress(hostname)) {\n return hostname;\n }\n\n return pslGet(hostname) ?? hostname;\n}\n\n/**\n * Whether an RPC URL is a MetaMask Infura endpoint. Stored network\n * configurations keep the `{infuraProjectId}` placeholder (substitution\n * happens at request time), so we match that shape.\n *\n * @param url - The RPC URL to check.\n * @returns True if the URL is a MetaMask Infura endpoint.\n */\nexport function getIsInfuraEndpoint(url: string): boolean {\n return /^https:\\/\\/[^./]+\\.infura\\.io\\/v3\\/\\{infuraProjectId\\}$/u.test(url);\n}\n"]}
1
+ {"version":3,"file":"url-utils.cjs","sourceRoot":"","sources":["../src/url-utils.ts"],"names":[],"mappings":";;;AAAA;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CAAC,GAAW;IAC7C,OAAO,0DAA0D,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9E,CAAC;AAFD,kDAEC","sourcesContent":["/**\n * Whether an RPC URL is a MetaMask Infura endpoint. Stored network\n * configurations keep the `{infuraProjectId}` placeholder (substitution\n * happens at request time), so we match that shape.\n *\n * @param url - The RPC URL to check.\n * @returns True if the URL is a MetaMask Infura endpoint.\n */\nexport function getIsInfuraEndpoint(url: string): boolean {\n return /^https:\\/\\/[^./]+\\.infura\\.io\\/v3\\/\\{infuraProjectId\\}$/u.test(url);\n}\n"]}
@@ -1,26 +1,3 @@
1
- /**
2
- * Check if a hostname is localhost or an IP address (v4 or v6). Public RPC
3
- * providers use domain names, not raw IP addresses, so a `true` result means
4
- * the host should not be reduced to an eTLD+1.
5
- *
6
- * @param hostname - The hostname to check.
7
- * @returns True if the hostname is localhost or an IP address.
8
- */
9
- export declare function isLocalhostOrIPAddress(hostname: string): boolean;
10
- /**
11
- * Registrable domain (eTLD+1) for a URL, computed via the Public Suffix List
12
- * so multi-part suffixes like ".co.uk" resolve correctly. Used to group RPC
13
- * endpoints by provider so a single provider's wide outage (e.g. *.infura.io)
14
- * is treated as one failure rather than many.
15
- *
16
- * Localhost, IP literals, and single-label hosts are returned verbatim rather
17
- * than reduced to a domain (psl returns null or garbage for those, and callers
18
- * grouping by domain still need to distinguish them).
19
- *
20
- * @param urlString - The URL to extract a domain from.
21
- * @returns The domain, or null if the URL is invalid.
22
- */
23
- export declare function getDomain(urlString: string): string | null;
24
1
  /**
25
2
  * Whether an RPC URL is a MetaMask Infura endpoint. Stored network
26
3
  * configurations keep the `{infuraProjectId}` placeholder (substitution
@@ -1 +1 @@
1
- {"version":3,"file":"url-utils.d.cts","sourceRoot":"","sources":["../src/url-utils.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAWhE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe1D;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD"}
1
+ {"version":3,"file":"url-utils.d.cts","sourceRoot":"","sources":["../src/url-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD"}
@@ -1,26 +1,3 @@
1
- /**
2
- * Check if a hostname is localhost or an IP address (v4 or v6). Public RPC
3
- * providers use domain names, not raw IP addresses, so a `true` result means
4
- * the host should not be reduced to an eTLD+1.
5
- *
6
- * @param hostname - The hostname to check.
7
- * @returns True if the hostname is localhost or an IP address.
8
- */
9
- export declare function isLocalhostOrIPAddress(hostname: string): boolean;
10
- /**
11
- * Registrable domain (eTLD+1) for a URL, computed via the Public Suffix List
12
- * so multi-part suffixes like ".co.uk" resolve correctly. Used to group RPC
13
- * endpoints by provider so a single provider's wide outage (e.g. *.infura.io)
14
- * is treated as one failure rather than many.
15
- *
16
- * Localhost, IP literals, and single-label hosts are returned verbatim rather
17
- * than reduced to a domain (psl returns null or garbage for those, and callers
18
- * grouping by domain still need to distinguish them).
19
- *
20
- * @param urlString - The URL to extract a domain from.
21
- * @returns The domain, or null if the URL is invalid.
22
- */
23
- export declare function getDomain(urlString: string): string | null;
24
1
  /**
25
2
  * Whether an RPC URL is a MetaMask Infura endpoint. Stored network
26
3
  * configurations keep the `{infuraProjectId}` placeholder (substitution
@@ -1 +1 @@
1
- {"version":3,"file":"url-utils.d.mts","sourceRoot":"","sources":["../src/url-utils.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAWhE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe1D;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD"}
1
+ {"version":3,"file":"url-utils.d.mts","sourceRoot":"","sources":["../src/url-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD"}
@@ -1,56 +1,3 @@
1
- function $importDefault(module) {
2
- if (module?.__esModule) {
3
- return module.default;
4
- }
5
- return module;
6
- }
7
- import $ipRegex from "ip-regex/index.js";
8
- const ipRegex = $importDefault($ipRegex);
9
- import { get as pslGet } from "psl";
10
- /**
11
- * Check if a hostname is localhost or an IP address (v4 or v6). Public RPC
12
- * providers use domain names, not raw IP addresses, so a `true` result means
13
- * the host should not be reduced to an eTLD+1.
14
- *
15
- * @param hostname - The hostname to check.
16
- * @returns True if the hostname is localhost or an IP address.
17
- */
18
- export function isLocalhostOrIPAddress(hostname) {
19
- const lowerHostname = hostname.toLowerCase();
20
- if (lowerHostname === 'localhost') {
21
- return true;
22
- }
23
- // Remove brackets from IPv6 addresses for testing (e.g., [::1] -> ::1)
24
- const hostnameWithoutBrackets = lowerHostname.replace(/^\[|\]$/gu, '');
25
- return ipRegex({ exact: true }).test(hostnameWithoutBrackets);
26
- }
27
- /**
28
- * Registrable domain (eTLD+1) for a URL, computed via the Public Suffix List
29
- * so multi-part suffixes like ".co.uk" resolve correctly. Used to group RPC
30
- * endpoints by provider so a single provider's wide outage (e.g. *.infura.io)
31
- * is treated as one failure rather than many.
32
- *
33
- * Localhost, IP literals, and single-label hosts are returned verbatim rather
34
- * than reduced to a domain (psl returns null or garbage for those, and callers
35
- * grouping by domain still need to distinguish them).
36
- *
37
- * @param urlString - The URL to extract a domain from.
38
- * @returns The domain, or null if the URL is invalid.
39
- */
40
- export function getDomain(urlString) {
41
- let url;
42
- try {
43
- url = new URL(urlString);
44
- }
45
- catch {
46
- return null;
47
- }
48
- const { hostname } = url;
49
- if (!hostname.includes('.') || isLocalhostOrIPAddress(hostname)) {
50
- return hostname;
51
- }
52
- return pslGet(hostname) ?? hostname;
53
- }
54
1
  /**
55
2
  * Whether an RPC URL is a MetaMask Infura endpoint. Stored network
56
3
  * configurations keep the `{infuraProjectId}` placeholder (substitution
@@ -1 +1 @@
1
- {"version":3,"file":"url-utils.mjs","sourceRoot":"","sources":["../src/url-utils.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,QAAO,0BAAiB;;AAC/B,OAAO,EAAE,GAAG,IAAI,MAAM,EAAE,YAAY;AAEpC;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAAgB;IACrD,MAAM,aAAa,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IAE7C,IAAI,aAAa,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,uEAAuE;IACvE,MAAM,uBAAuB,GAAG,aAAa,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAEvE,OAAO,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CAAC,SAAiB;IACzC,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;IAEzB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,sBAAsB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChE,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,OAAO,0DAA0D,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9E,CAAC","sourcesContent":["import ipRegex from 'ip-regex';\nimport { get as pslGet } from 'psl';\n\n/**\n * Check if a hostname is localhost or an IP address (v4 or v6). Public RPC\n * providers use domain names, not raw IP addresses, so a `true` result means\n * the host should not be reduced to an eTLD+1.\n *\n * @param hostname - The hostname to check.\n * @returns True if the hostname is localhost or an IP address.\n */\nexport function isLocalhostOrIPAddress(hostname: string): boolean {\n const lowerHostname = hostname.toLowerCase();\n\n if (lowerHostname === 'localhost') {\n return true;\n }\n\n // Remove brackets from IPv6 addresses for testing (e.g., [::1] -> ::1)\n const hostnameWithoutBrackets = lowerHostname.replace(/^\\[|\\]$/gu, '');\n\n return ipRegex({ exact: true }).test(hostnameWithoutBrackets);\n}\n\n/**\n * Registrable domain (eTLD+1) for a URL, computed via the Public Suffix List\n * so multi-part suffixes like \".co.uk\" resolve correctly. Used to group RPC\n * endpoints by provider so a single provider's wide outage (e.g. *.infura.io)\n * is treated as one failure rather than many.\n *\n * Localhost, IP literals, and single-label hosts are returned verbatim rather\n * than reduced to a domain (psl returns null or garbage for those, and callers\n * grouping by domain still need to distinguish them).\n *\n * @param urlString - The URL to extract a domain from.\n * @returns The domain, or null if the URL is invalid.\n */\nexport function getDomain(urlString: string): string | null {\n let url: URL;\n try {\n url = new URL(urlString);\n } catch {\n return null;\n }\n\n const { hostname } = url;\n\n if (!hostname.includes('.') || isLocalhostOrIPAddress(hostname)) {\n return hostname;\n }\n\n return pslGet(hostname) ?? hostname;\n}\n\n/**\n * Whether an RPC URL is a MetaMask Infura endpoint. Stored network\n * configurations keep the `{infuraProjectId}` placeholder (substitution\n * happens at request time), so we match that shape.\n *\n * @param url - The RPC URL to check.\n * @returns True if the URL is a MetaMask Infura endpoint.\n */\nexport function getIsInfuraEndpoint(url: string): boolean {\n return /^https:\\/\\/[^./]+\\.infura\\.io\\/v3\\/\\{infuraProjectId\\}$/u.test(url);\n}\n"]}
1
+ {"version":3,"file":"url-utils.mjs","sourceRoot":"","sources":["../src/url-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,OAAO,0DAA0D,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9E,CAAC","sourcesContent":["/**\n * Whether an RPC URL is a MetaMask Infura endpoint. Stored network\n * configurations keep the `{infuraProjectId}` placeholder (substitution\n * happens at request time), so we match that shape.\n *\n * @param url - The RPC URL to check.\n * @returns True if the URL is a MetaMask Infura endpoint.\n */\nexport function getIsInfuraEndpoint(url: string): boolean {\n return /^https:\\/\\/[^./]+\\.infura\\.io\\/v3\\/\\{infuraProjectId\\}$/u.test(url);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/network-connection-banner-controller",
3
- "version": "0.1.0-preview-bd4161b27",
3
+ "version": "0.1.0-preview-68794c0ff",
4
4
  "description": "Decides when and how to surface the network connection banner based on RPC endpoint health",
5
5
  "keywords": [
6
6
  "Ethereum",
@@ -59,8 +59,6 @@
59
59
  "@metamask/network-controller": "^34.0.0",
60
60
  "@metamask/network-enablement-controller": "^5.4.1",
61
61
  "@metamask/utils": "^11.11.0",
62
- "ip-regex": "^4.3.0",
63
- "psl": "^1.15.0",
64
62
  "reselect": "^5.1.1"
65
63
  },
66
64
  "devDependencies": {