@metamask-previews/config-registry-controller 0.0.0-preview-32ed9958c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/LICENSE +20 -0
- package/README.md +15 -0
- package/dist/ConfigRegistryController.cjs +132 -0
- package/dist/ConfigRegistryController.cjs.map +1 -0
- package/dist/ConfigRegistryController.d.cts +134 -0
- package/dist/ConfigRegistryController.d.cts.map +1 -0
- package/dist/ConfigRegistryController.d.mts +134 -0
- package/dist/ConfigRegistryController.d.mts.map +1 -0
- package/dist/ConfigRegistryController.mjs +128 -0
- package/dist/ConfigRegistryController.mjs.map +1 -0
- package/dist/config-registry-api-service/config-registry-api-service.cjs +123 -0
- package/dist/config-registry-api-service/config-registry-api-service.cjs.map +1 -0
- package/dist/config-registry-api-service/config-registry-api-service.d.cts +56 -0
- package/dist/config-registry-api-service/config-registry-api-service.d.cts.map +1 -0
- package/dist/config-registry-api-service/config-registry-api-service.d.mts +56 -0
- package/dist/config-registry-api-service/config-registry-api-service.d.mts.map +1 -0
- package/dist/config-registry-api-service/config-registry-api-service.mjs +119 -0
- package/dist/config-registry-api-service/config-registry-api-service.mjs.map +1 -0
- package/dist/config-registry-api-service/filters.cjs +30 -0
- package/dist/config-registry-api-service/filters.cjs.map +1 -0
- package/dist/config-registry-api-service/filters.d.cts +15 -0
- package/dist/config-registry-api-service/filters.d.cts.map +1 -0
- package/dist/config-registry-api-service/filters.d.mts +15 -0
- package/dist/config-registry-api-service/filters.d.mts.map +1 -0
- package/dist/config-registry-api-service/filters.mjs +26 -0
- package/dist/config-registry-api-service/filters.mjs.map +1 -0
- package/dist/config-registry-api-service/index.cjs +8 -0
- package/dist/config-registry-api-service/index.cjs.map +1 -0
- package/dist/config-registry-api-service/index.d.cts +6 -0
- package/dist/config-registry-api-service/index.d.cts.map +1 -0
- package/dist/config-registry-api-service/index.d.mts +6 -0
- package/dist/config-registry-api-service/index.d.mts.map +1 -0
- package/dist/config-registry-api-service/index.mjs +3 -0
- package/dist/config-registry-api-service/index.mjs.map +1 -0
- package/dist/config-registry-api-service/types.cjs +69 -0
- package/dist/config-registry-api-service/types.cjs.map +1 -0
- package/dist/config-registry-api-service/types.d.cts +476 -0
- package/dist/config-registry-api-service/types.d.cts.map +1 -0
- package/dist/config-registry-api-service/types.d.mts +476 -0
- package/dist/config-registry-api-service/types.d.mts.map +1 -0
- package/dist/config-registry-api-service/types.mjs +65 -0
- package/dist/config-registry-api-service/types.mjs.map +1 -0
- package/dist/index.cjs +15 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +5 -0
- package/dist/index.mjs.map +1 -0
- package/dist/selectors.cjs +34 -0
- package/dist/selectors.cjs.map +1 -0
- package/dist/selectors.d.cts +314 -0
- package/dist/selectors.d.cts.map +1 -0
- package/dist/selectors.d.mts +314 -0
- package/dist/selectors.d.mts.map +1 -0
- package/dist/selectors.mjs +30 -0
- package/dist/selectors.mjs.map +1 -0
- package/dist/utils/feature-flags.cjs +27 -0
- package/dist/utils/feature-flags.cjs.map +1 -0
- package/dist/utils/feature-flags.d.cts +9 -0
- package/dist/utils/feature-flags.d.cts.map +1 -0
- package/dist/utils/feature-flags.d.mts +9 -0
- package/dist/utils/feature-flags.d.mts.map +1 -0
- package/dist/utils/feature-flags.mjs +23 -0
- package/dist/utils/feature-flags.mjs.map +1 -0
- package/package.json +81 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release ([#7668](https://github.com/MetaMask/core/pull/7668), [#7809](https://github.com/MetaMask/core/pull/7809))
|
|
13
|
+
|
|
14
|
+
[Unreleased]: https://github.com/MetaMask/core/
|
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 MetaMask
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# `@metamask/config-registry-controller`
|
|
2
|
+
|
|
3
|
+
Manages configuration registry for MetaMask
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
`yarn add @metamask/config-registry-controller`
|
|
8
|
+
|
|
9
|
+
or
|
|
10
|
+
|
|
11
|
+
`npm install @metamask/config-registry-controller`
|
|
12
|
+
|
|
13
|
+
## Contributing
|
|
14
|
+
|
|
15
|
+
This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme).
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
+
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");
|
|
6
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
+
};
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var _ConfigRegistryController_isConfigRegistryApiEnabled;
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.ConfigRegistryController = exports.DEFAULT_POLLING_INTERVAL = void 0;
|
|
16
|
+
const polling_controller_1 = require("@metamask/polling-controller");
|
|
17
|
+
const utils_1 = require("@metamask/utils");
|
|
18
|
+
const feature_flags_1 = require("./utils/feature-flags.cjs");
|
|
19
|
+
const controllerName = 'ConfigRegistryController';
|
|
20
|
+
exports.DEFAULT_POLLING_INTERVAL = (0, utils_1.inMilliseconds)(1, utils_1.Duration.Day);
|
|
21
|
+
const stateMetadata = {
|
|
22
|
+
configs: {
|
|
23
|
+
persist: true,
|
|
24
|
+
includeInStateLogs: false,
|
|
25
|
+
includeInDebugSnapshot: true,
|
|
26
|
+
usedInUi: true,
|
|
27
|
+
},
|
|
28
|
+
version: {
|
|
29
|
+
persist: true,
|
|
30
|
+
includeInStateLogs: true,
|
|
31
|
+
includeInDebugSnapshot: true,
|
|
32
|
+
usedInUi: false,
|
|
33
|
+
},
|
|
34
|
+
lastFetched: {
|
|
35
|
+
persist: true,
|
|
36
|
+
includeInStateLogs: true,
|
|
37
|
+
includeInDebugSnapshot: true,
|
|
38
|
+
usedInUi: false,
|
|
39
|
+
},
|
|
40
|
+
etag: {
|
|
41
|
+
persist: true,
|
|
42
|
+
includeInStateLogs: false,
|
|
43
|
+
includeInDebugSnapshot: false,
|
|
44
|
+
usedInUi: false,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Default fallback configuration when no configs are available.
|
|
49
|
+
*/
|
|
50
|
+
const DEFAULT_FALLBACK_CONFIG = {};
|
|
51
|
+
class ConfigRegistryController extends (0, polling_controller_1.StaticIntervalPollingController)() {
|
|
52
|
+
/**
|
|
53
|
+
* @param options - The controller options.
|
|
54
|
+
* @param options.messenger - The controller messenger. Must have
|
|
55
|
+
* `ConfigRegistryApiService:fetchConfig` action handler registered.
|
|
56
|
+
* @param options.state - Initial state.
|
|
57
|
+
* @param options.pollingInterval - Polling interval in milliseconds.
|
|
58
|
+
* @param options.fallbackConfig - Fallback configuration.
|
|
59
|
+
* @param options.isConfigRegistryApiEnabled - Function to check if the config
|
|
60
|
+
* registry API is enabled. Defaults to checking the remote feature flag.
|
|
61
|
+
*/
|
|
62
|
+
constructor({ messenger, state = {}, pollingInterval = exports.DEFAULT_POLLING_INTERVAL, fallbackConfig = DEFAULT_FALLBACK_CONFIG, isConfigRegistryApiEnabled = feature_flags_1.isConfigRegistryApiEnabled, }) {
|
|
63
|
+
super({
|
|
64
|
+
name: controllerName,
|
|
65
|
+
metadata: stateMetadata,
|
|
66
|
+
messenger,
|
|
67
|
+
state: {
|
|
68
|
+
configs: {
|
|
69
|
+
networks: state.configs?.networks ?? { ...fallbackConfig },
|
|
70
|
+
},
|
|
71
|
+
version: state.version ?? null,
|
|
72
|
+
lastFetched: state.lastFetched ?? null,
|
|
73
|
+
etag: state.etag ?? null,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
_ConfigRegistryController_isConfigRegistryApiEnabled.set(this, void 0);
|
|
77
|
+
this.setIntervalLength(pollingInterval);
|
|
78
|
+
__classPrivateFieldSet(this, _ConfigRegistryController_isConfigRegistryApiEnabled, isConfigRegistryApiEnabled, "f");
|
|
79
|
+
this.messenger.registerActionHandler(`${controllerName}:startPolling`, this.startPolling.bind(this));
|
|
80
|
+
this.messenger.registerActionHandler(`${controllerName}:stopPolling`, this.stopAllPolling.bind(this));
|
|
81
|
+
this.messenger.subscribe('KeyringController:unlock', () => this.startPolling(null));
|
|
82
|
+
this.messenger.subscribe('KeyringController:lock', () => this.stopAllPolling());
|
|
83
|
+
}
|
|
84
|
+
async _executePoll(_input) {
|
|
85
|
+
const isApiEnabled = __classPrivateFieldGet(this, _ConfigRegistryController_isConfigRegistryApiEnabled, "f").call(this, this.messenger);
|
|
86
|
+
// Skip fetch when API is disabled; client uses static config.
|
|
87
|
+
if (!isApiEnabled) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const interval = this.getIntervalLength() ?? exports.DEFAULT_POLLING_INTERVAL;
|
|
91
|
+
if (this.state.lastFetched !== null &&
|
|
92
|
+
Date.now() - this.state.lastFetched < interval) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const result = await this.messenger.call('ConfigRegistryApiService:fetchConfig', {
|
|
97
|
+
etag: this.state.etag ?? undefined,
|
|
98
|
+
});
|
|
99
|
+
if (!result.modified) {
|
|
100
|
+
this.update((state) => {
|
|
101
|
+
state.lastFetched = Date.now();
|
|
102
|
+
if (result.etag !== undefined) {
|
|
103
|
+
state.etag = result.etag ?? null;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const apiChains = result.data.data.chains;
|
|
109
|
+
const newConfigs = {};
|
|
110
|
+
// duplicate chainIds from API response are not expected
|
|
111
|
+
apiChains.forEach((chainConfig) => {
|
|
112
|
+
const { chainId } = chainConfig;
|
|
113
|
+
newConfigs[chainId] = chainConfig;
|
|
114
|
+
});
|
|
115
|
+
this.update((state) => {
|
|
116
|
+
state.configs.networks = newConfigs;
|
|
117
|
+
state.version = result.data.data.version;
|
|
118
|
+
state.lastFetched = Date.now();
|
|
119
|
+
if (result.etag !== undefined) {
|
|
120
|
+
state.etag = result.etag;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
const errorInstance = error instanceof Error ? error : new Error(String(error));
|
|
126
|
+
this.messenger.captureException?.(errorInstance);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
exports.ConfigRegistryController = ConfigRegistryController;
|
|
131
|
+
_ConfigRegistryController_isConfigRegistryApiEnabled = new WeakMap();
|
|
132
|
+
//# sourceMappingURL=ConfigRegistryController.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigRegistryController.cjs","sourceRoot":"","sources":["../src/ConfigRegistryController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller';\nimport { Duration, inMilliseconds } from '@metamask/utils';\n\nimport type {\n FetchConfigOptions,\n FetchConfigResult,\n RegistryNetworkConfig,\n} from './config-registry-api-service';\nimport { isConfigRegistryApiEnabled as defaultIsConfigRegistryApiEnabled } from './utils/feature-flags';\n\nconst controllerName = 'ConfigRegistryController';\n\nexport const DEFAULT_POLLING_INTERVAL = inMilliseconds(1, Duration.Day);\n\n/**\n * State for the ConfigRegistryController.\n *\n * Tracks network configurations fetched from the config registry API,\n * along with metadata about the fetch status and caching.\n */\nexport type ConfigRegistryControllerState = {\n /**\n * Network configurations organized by chain ID.\n * Stores the full API response including isFeatured, isTestnet, etc.\n * Use selectors (e.g. selectFeaturedNetworks) to filter when needed.\n */\n configs: {\n networks: Record<string, RegistryNetworkConfig>;\n };\n /**\n * Semantic version string of the configuration data from the API.\n * Indicates the version/schema of the configuration structure itself\n * (e.g., \"v1.0.0\", \"1.0.0\").\n * This is different from `etag` which is used for HTTP cache validation.\n */\n version: string | null;\n /**\n * Timestamp (milliseconds since epoch) of when the configuration\n * was last successfully fetched from the API.\n */\n lastFetched: number | null;\n /**\n * HTTP entity tag (ETag) used for cache validation.\n * Sent as `If-None-Match` header in subsequent requests to check\n * if the content has changed. If the server returns 304 Not Modified,\n * the full response body is not downloaded, improving efficiency.\n * This is different from `version` which is a semantic version string\n * indicating the schema/version of the configuration data itself.\n */\n etag: string | null;\n};\n\nconst stateMetadata = {\n configs: {\n persist: true,\n includeInStateLogs: false,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n version: {\n persist: true,\n includeInStateLogs: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n lastFetched: {\n persist: true,\n includeInStateLogs: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n etag: {\n persist: true,\n includeInStateLogs: false,\n includeInDebugSnapshot: false,\n usedInUi: false,\n },\n} satisfies StateMetadata<ConfigRegistryControllerState>;\n\n/**\n * Default fallback configuration when no configs are available.\n */\nconst DEFAULT_FALLBACK_CONFIG: Record<string, RegistryNetworkConfig> = {};\n\n/**\n * Published when the state of {@link ConfigRegistryController} changes.\n */\nexport type ConfigRegistryControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n ConfigRegistryControllerState\n >;\n\n/**\n * Retrieves the state of the {@link ConfigRegistryController}.\n */\nexport type ConfigRegistryControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n ConfigRegistryControllerState\n>;\n\n/**\n * Starts polling the config registry API. Returns a polling token that can be\n * used to stop this polling session.\n */\nexport type ConfigRegistryControllerStartPollingAction = {\n type: `${typeof controllerName}:startPolling`;\n handler: (input: null) => string;\n};\n\n/**\n * Stops all config registry polling.\n */\nexport type ConfigRegistryControllerStopPollingAction = {\n type: `${typeof controllerName}:stopPolling`;\n handler: () => void;\n};\n\n/**\n * Actions that {@link ConfigRegistryControllerMessenger} exposes to other consumers.\n */\nexport type ConfigRegistryControllerActions =\n | ConfigRegistryControllerGetStateAction\n | ConfigRegistryControllerStartPollingAction\n | ConfigRegistryControllerStopPollingAction;\n\n/**\n * Actions from other messengers that {@link ConfigRegistryControllerMessenger}\n * calls.\n */\ntype AllowedActions =\n | RemoteFeatureFlagControllerGetStateAction\n | {\n type: 'ConfigRegistryApiService:fetchConfig';\n handler: (options?: FetchConfigOptions) => Promise<FetchConfigResult>;\n };\n\n/**\n * Events that {@link ConfigRegistryControllerMessenger} exposes to other consumers.\n */\nexport type ConfigRegistryControllerEvents =\n ConfigRegistryControllerStateChangeEvent;\n\n/**\n * Events from other messengers that {@link ConfigRegistryControllerMessenger}\n * subscribes to.\n */\ntype AllowedEvents = KeyringControllerUnlockEvent | KeyringControllerLockEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link ConfigRegistryController}.\n */\nexport type ConfigRegistryControllerMessenger = Messenger<\n typeof controllerName,\n ConfigRegistryControllerActions | AllowedActions,\n ConfigRegistryControllerEvents | AllowedEvents\n>;\n\n/** @deprecated Use {@link ConfigRegistryControllerMessenger} instead. */\nexport type ConfigRegistryMessenger = ConfigRegistryControllerMessenger;\n\nexport type ConfigRegistryControllerOptions = {\n messenger: ConfigRegistryMessenger;\n state?: Partial<ConfigRegistryControllerState>;\n pollingInterval?: number;\n fallbackConfig?: Record<string, RegistryNetworkConfig>;\n isConfigRegistryApiEnabled?: (messenger: ConfigRegistryMessenger) => boolean;\n};\n\nexport class ConfigRegistryController extends StaticIntervalPollingController<null>()<\n typeof controllerName,\n ConfigRegistryControllerState,\n ConfigRegistryMessenger\n> {\n readonly #isConfigRegistryApiEnabled: (\n messenger: ConfigRegistryMessenger,\n ) => boolean;\n\n /**\n * @param options - The controller options.\n * @param options.messenger - The controller messenger. Must have\n * `ConfigRegistryApiService:fetchConfig` action handler registered.\n * @param options.state - Initial state.\n * @param options.pollingInterval - Polling interval in milliseconds.\n * @param options.fallbackConfig - Fallback configuration.\n * @param options.isConfigRegistryApiEnabled - Function to check if the config\n * registry API is enabled. Defaults to checking the remote feature flag.\n */\n constructor({\n messenger,\n state = {},\n pollingInterval = DEFAULT_POLLING_INTERVAL,\n fallbackConfig = DEFAULT_FALLBACK_CONFIG,\n isConfigRegistryApiEnabled = defaultIsConfigRegistryApiEnabled,\n }: ConfigRegistryControllerOptions) {\n super({\n name: controllerName,\n metadata: stateMetadata,\n messenger,\n state: {\n configs: {\n networks: state.configs?.networks ?? { ...fallbackConfig },\n },\n version: state.version ?? null,\n lastFetched: state.lastFetched ?? null,\n etag: state.etag ?? null,\n },\n });\n\n this.setIntervalLength(pollingInterval);\n this.#isConfigRegistryApiEnabled = isConfigRegistryApiEnabled;\n\n this.messenger.registerActionHandler(\n `${controllerName}:startPolling`,\n this.startPolling.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:stopPolling`,\n this.stopAllPolling.bind(this),\n );\n\n this.messenger.subscribe('KeyringController:unlock', () =>\n this.startPolling(null),\n );\n\n this.messenger.subscribe('KeyringController:lock', () =>\n this.stopAllPolling(),\n );\n }\n\n async _executePoll(_input: null): Promise<void> {\n const isApiEnabled = this.#isConfigRegistryApiEnabled(this.messenger);\n\n // Skip fetch when API is disabled; client uses static config.\n if (!isApiEnabled) {\n return;\n }\n\n const interval = this.getIntervalLength() ?? DEFAULT_POLLING_INTERVAL;\n if (\n this.state.lastFetched !== null &&\n Date.now() - this.state.lastFetched < interval\n ) {\n return;\n }\n\n try {\n const result: FetchConfigResult = await this.messenger.call(\n 'ConfigRegistryApiService:fetchConfig',\n {\n etag: this.state.etag ?? undefined,\n },\n );\n\n if (!result.modified) {\n this.update((state) => {\n state.lastFetched = Date.now();\n if (result.etag !== undefined) {\n state.etag = result.etag ?? null;\n }\n });\n return;\n }\n\n const apiChains = result.data.data.chains;\n const newConfigs: Record<string, RegistryNetworkConfig> = {};\n // duplicate chainIds from API response are not expected\n apiChains.forEach((chainConfig) => {\n const { chainId } = chainConfig;\n newConfigs[chainId] = chainConfig;\n });\n\n this.update((state) => {\n state.configs.networks = newConfigs;\n state.version = result.data.data.version;\n state.lastFetched = Date.now();\n if (result.etag !== undefined) {\n state.etag = result.etag;\n }\n });\n } catch (error) {\n const errorInstance =\n error instanceof Error ? error : new Error(String(error));\n\n this.messenger.captureException?.(errorInstance);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { ControllerGetStateAction, ControllerStateChangeEvent } from "@metamask/base-controller";
|
|
2
|
+
import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent } from "@metamask/keyring-controller";
|
|
3
|
+
import type { Messenger } from "@metamask/messenger";
|
|
4
|
+
import { RemoteFeatureFlagControllerGetStateAction } from "@metamask/remote-feature-flag-controller";
|
|
5
|
+
import type { FetchConfigOptions, FetchConfigResult, RegistryNetworkConfig } from "./config-registry-api-service/index.cjs";
|
|
6
|
+
declare const controllerName = "ConfigRegistryController";
|
|
7
|
+
export declare const DEFAULT_POLLING_INTERVAL: number;
|
|
8
|
+
/**
|
|
9
|
+
* State for the ConfigRegistryController.
|
|
10
|
+
*
|
|
11
|
+
* Tracks network configurations fetched from the config registry API,
|
|
12
|
+
* along with metadata about the fetch status and caching.
|
|
13
|
+
*/
|
|
14
|
+
export type ConfigRegistryControllerState = {
|
|
15
|
+
/**
|
|
16
|
+
* Network configurations organized by chain ID.
|
|
17
|
+
* Stores the full API response including isFeatured, isTestnet, etc.
|
|
18
|
+
* Use selectors (e.g. selectFeaturedNetworks) to filter when needed.
|
|
19
|
+
*/
|
|
20
|
+
configs: {
|
|
21
|
+
networks: Record<string, RegistryNetworkConfig>;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Semantic version string of the configuration data from the API.
|
|
25
|
+
* Indicates the version/schema of the configuration structure itself
|
|
26
|
+
* (e.g., "v1.0.0", "1.0.0").
|
|
27
|
+
* This is different from `etag` which is used for HTTP cache validation.
|
|
28
|
+
*/
|
|
29
|
+
version: string | null;
|
|
30
|
+
/**
|
|
31
|
+
* Timestamp (milliseconds since epoch) of when the configuration
|
|
32
|
+
* was last successfully fetched from the API.
|
|
33
|
+
*/
|
|
34
|
+
lastFetched: number | null;
|
|
35
|
+
/**
|
|
36
|
+
* HTTP entity tag (ETag) used for cache validation.
|
|
37
|
+
* Sent as `If-None-Match` header in subsequent requests to check
|
|
38
|
+
* if the content has changed. If the server returns 304 Not Modified,
|
|
39
|
+
* the full response body is not downloaded, improving efficiency.
|
|
40
|
+
* This is different from `version` which is a semantic version string
|
|
41
|
+
* indicating the schema/version of the configuration data itself.
|
|
42
|
+
*/
|
|
43
|
+
etag: string | null;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Published when the state of {@link ConfigRegistryController} changes.
|
|
47
|
+
*/
|
|
48
|
+
export type ConfigRegistryControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, ConfigRegistryControllerState>;
|
|
49
|
+
/**
|
|
50
|
+
* Retrieves the state of the {@link ConfigRegistryController}.
|
|
51
|
+
*/
|
|
52
|
+
export type ConfigRegistryControllerGetStateAction = ControllerGetStateAction<typeof controllerName, ConfigRegistryControllerState>;
|
|
53
|
+
/**
|
|
54
|
+
* Starts polling the config registry API. Returns a polling token that can be
|
|
55
|
+
* used to stop this polling session.
|
|
56
|
+
*/
|
|
57
|
+
export type ConfigRegistryControllerStartPollingAction = {
|
|
58
|
+
type: `${typeof controllerName}:startPolling`;
|
|
59
|
+
handler: (input: null) => string;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Stops all config registry polling.
|
|
63
|
+
*/
|
|
64
|
+
export type ConfigRegistryControllerStopPollingAction = {
|
|
65
|
+
type: `${typeof controllerName}:stopPolling`;
|
|
66
|
+
handler: () => void;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Actions that {@link ConfigRegistryControllerMessenger} exposes to other consumers.
|
|
70
|
+
*/
|
|
71
|
+
export type ConfigRegistryControllerActions = ConfigRegistryControllerGetStateAction | ConfigRegistryControllerStartPollingAction | ConfigRegistryControllerStopPollingAction;
|
|
72
|
+
/**
|
|
73
|
+
* Actions from other messengers that {@link ConfigRegistryControllerMessenger}
|
|
74
|
+
* calls.
|
|
75
|
+
*/
|
|
76
|
+
type AllowedActions = RemoteFeatureFlagControllerGetStateAction | {
|
|
77
|
+
type: 'ConfigRegistryApiService:fetchConfig';
|
|
78
|
+
handler: (options?: FetchConfigOptions) => Promise<FetchConfigResult>;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Events that {@link ConfigRegistryControllerMessenger} exposes to other consumers.
|
|
82
|
+
*/
|
|
83
|
+
export type ConfigRegistryControllerEvents = ConfigRegistryControllerStateChangeEvent;
|
|
84
|
+
/**
|
|
85
|
+
* Events from other messengers that {@link ConfigRegistryControllerMessenger}
|
|
86
|
+
* subscribes to.
|
|
87
|
+
*/
|
|
88
|
+
type AllowedEvents = KeyringControllerUnlockEvent | KeyringControllerLockEvent;
|
|
89
|
+
/**
|
|
90
|
+
* The messenger restricted to actions and events accessed by
|
|
91
|
+
* {@link ConfigRegistryController}.
|
|
92
|
+
*/
|
|
93
|
+
export type ConfigRegistryControllerMessenger = Messenger<typeof controllerName, ConfigRegistryControllerActions | AllowedActions, ConfigRegistryControllerEvents | AllowedEvents>;
|
|
94
|
+
/** @deprecated Use {@link ConfigRegistryControllerMessenger} instead. */
|
|
95
|
+
export type ConfigRegistryMessenger = ConfigRegistryControllerMessenger;
|
|
96
|
+
export type ConfigRegistryControllerOptions = {
|
|
97
|
+
messenger: ConfigRegistryMessenger;
|
|
98
|
+
state?: Partial<ConfigRegistryControllerState>;
|
|
99
|
+
pollingInterval?: number;
|
|
100
|
+
fallbackConfig?: Record<string, RegistryNetworkConfig>;
|
|
101
|
+
isConfigRegistryApiEnabled?: (messenger: ConfigRegistryMessenger) => boolean;
|
|
102
|
+
};
|
|
103
|
+
declare const ConfigRegistryController_base: (abstract new (...args: any[]) => {
|
|
104
|
+
readonly "__#15@#intervalIds": Record<string, NodeJS.Timeout>;
|
|
105
|
+
"__#15@#intervalLength": number | undefined;
|
|
106
|
+
setIntervalLength(intervalLength: number): void;
|
|
107
|
+
getIntervalLength(): number | undefined;
|
|
108
|
+
_startPolling(input: null): void;
|
|
109
|
+
_stopPollingByPollingTokenSetId(key: string): void;
|
|
110
|
+
readonly "__#3@#pollingTokenSets": Map<string, Set<string>>;
|
|
111
|
+
readonly "__#3@#callbacks": Map<string, Set<(input: null) => void>>;
|
|
112
|
+
_executePoll(input: null): Promise<void>;
|
|
113
|
+
startPolling(input: null): string;
|
|
114
|
+
stopAllPolling(): void;
|
|
115
|
+
stopPollingByPollingToken(pollingToken: string): void;
|
|
116
|
+
onPollingComplete(input: null, callback: (input: null) => void): void;
|
|
117
|
+
}) & typeof import("@metamask/base-controller").BaseController;
|
|
118
|
+
export declare class ConfigRegistryController extends ConfigRegistryController_base<typeof controllerName, ConfigRegistryControllerState, ConfigRegistryMessenger> {
|
|
119
|
+
#private;
|
|
120
|
+
/**
|
|
121
|
+
* @param options - The controller options.
|
|
122
|
+
* @param options.messenger - The controller messenger. Must have
|
|
123
|
+
* `ConfigRegistryApiService:fetchConfig` action handler registered.
|
|
124
|
+
* @param options.state - Initial state.
|
|
125
|
+
* @param options.pollingInterval - Polling interval in milliseconds.
|
|
126
|
+
* @param options.fallbackConfig - Fallback configuration.
|
|
127
|
+
* @param options.isConfigRegistryApiEnabled - Function to check if the config
|
|
128
|
+
* registry API is enabled. Defaults to checking the remote feature flag.
|
|
129
|
+
*/
|
|
130
|
+
constructor({ messenger, state, pollingInterval, fallbackConfig, isConfigRegistryApiEnabled, }: ConfigRegistryControllerOptions);
|
|
131
|
+
_executePoll(_input: null): Promise<void>;
|
|
132
|
+
}
|
|
133
|
+
export {};
|
|
134
|
+
//# sourceMappingURL=ConfigRegistryController.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigRegistryController.d.cts","sourceRoot":"","sources":["../src/ConfigRegistryController.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { ControllerGetStateAction, ControllerStateChangeEvent } from "@metamask/base-controller";
|
|
2
|
+
import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent } from "@metamask/keyring-controller";
|
|
3
|
+
import type { Messenger } from "@metamask/messenger";
|
|
4
|
+
import { RemoteFeatureFlagControllerGetStateAction } from "@metamask/remote-feature-flag-controller";
|
|
5
|
+
import type { FetchConfigOptions, FetchConfigResult, RegistryNetworkConfig } from "./config-registry-api-service/index.mjs";
|
|
6
|
+
declare const controllerName = "ConfigRegistryController";
|
|
7
|
+
export declare const DEFAULT_POLLING_INTERVAL: number;
|
|
8
|
+
/**
|
|
9
|
+
* State for the ConfigRegistryController.
|
|
10
|
+
*
|
|
11
|
+
* Tracks network configurations fetched from the config registry API,
|
|
12
|
+
* along with metadata about the fetch status and caching.
|
|
13
|
+
*/
|
|
14
|
+
export type ConfigRegistryControllerState = {
|
|
15
|
+
/**
|
|
16
|
+
* Network configurations organized by chain ID.
|
|
17
|
+
* Stores the full API response including isFeatured, isTestnet, etc.
|
|
18
|
+
* Use selectors (e.g. selectFeaturedNetworks) to filter when needed.
|
|
19
|
+
*/
|
|
20
|
+
configs: {
|
|
21
|
+
networks: Record<string, RegistryNetworkConfig>;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Semantic version string of the configuration data from the API.
|
|
25
|
+
* Indicates the version/schema of the configuration structure itself
|
|
26
|
+
* (e.g., "v1.0.0", "1.0.0").
|
|
27
|
+
* This is different from `etag` which is used for HTTP cache validation.
|
|
28
|
+
*/
|
|
29
|
+
version: string | null;
|
|
30
|
+
/**
|
|
31
|
+
* Timestamp (milliseconds since epoch) of when the configuration
|
|
32
|
+
* was last successfully fetched from the API.
|
|
33
|
+
*/
|
|
34
|
+
lastFetched: number | null;
|
|
35
|
+
/**
|
|
36
|
+
* HTTP entity tag (ETag) used for cache validation.
|
|
37
|
+
* Sent as `If-None-Match` header in subsequent requests to check
|
|
38
|
+
* if the content has changed. If the server returns 304 Not Modified,
|
|
39
|
+
* the full response body is not downloaded, improving efficiency.
|
|
40
|
+
* This is different from `version` which is a semantic version string
|
|
41
|
+
* indicating the schema/version of the configuration data itself.
|
|
42
|
+
*/
|
|
43
|
+
etag: string | null;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Published when the state of {@link ConfigRegistryController} changes.
|
|
47
|
+
*/
|
|
48
|
+
export type ConfigRegistryControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, ConfigRegistryControllerState>;
|
|
49
|
+
/**
|
|
50
|
+
* Retrieves the state of the {@link ConfigRegistryController}.
|
|
51
|
+
*/
|
|
52
|
+
export type ConfigRegistryControllerGetStateAction = ControllerGetStateAction<typeof controllerName, ConfigRegistryControllerState>;
|
|
53
|
+
/**
|
|
54
|
+
* Starts polling the config registry API. Returns a polling token that can be
|
|
55
|
+
* used to stop this polling session.
|
|
56
|
+
*/
|
|
57
|
+
export type ConfigRegistryControllerStartPollingAction = {
|
|
58
|
+
type: `${typeof controllerName}:startPolling`;
|
|
59
|
+
handler: (input: null) => string;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Stops all config registry polling.
|
|
63
|
+
*/
|
|
64
|
+
export type ConfigRegistryControllerStopPollingAction = {
|
|
65
|
+
type: `${typeof controllerName}:stopPolling`;
|
|
66
|
+
handler: () => void;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Actions that {@link ConfigRegistryControllerMessenger} exposes to other consumers.
|
|
70
|
+
*/
|
|
71
|
+
export type ConfigRegistryControllerActions = ConfigRegistryControllerGetStateAction | ConfigRegistryControllerStartPollingAction | ConfigRegistryControllerStopPollingAction;
|
|
72
|
+
/**
|
|
73
|
+
* Actions from other messengers that {@link ConfigRegistryControllerMessenger}
|
|
74
|
+
* calls.
|
|
75
|
+
*/
|
|
76
|
+
type AllowedActions = RemoteFeatureFlagControllerGetStateAction | {
|
|
77
|
+
type: 'ConfigRegistryApiService:fetchConfig';
|
|
78
|
+
handler: (options?: FetchConfigOptions) => Promise<FetchConfigResult>;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Events that {@link ConfigRegistryControllerMessenger} exposes to other consumers.
|
|
82
|
+
*/
|
|
83
|
+
export type ConfigRegistryControllerEvents = ConfigRegistryControllerStateChangeEvent;
|
|
84
|
+
/**
|
|
85
|
+
* Events from other messengers that {@link ConfigRegistryControllerMessenger}
|
|
86
|
+
* subscribes to.
|
|
87
|
+
*/
|
|
88
|
+
type AllowedEvents = KeyringControllerUnlockEvent | KeyringControllerLockEvent;
|
|
89
|
+
/**
|
|
90
|
+
* The messenger restricted to actions and events accessed by
|
|
91
|
+
* {@link ConfigRegistryController}.
|
|
92
|
+
*/
|
|
93
|
+
export type ConfigRegistryControllerMessenger = Messenger<typeof controllerName, ConfigRegistryControllerActions | AllowedActions, ConfigRegistryControllerEvents | AllowedEvents>;
|
|
94
|
+
/** @deprecated Use {@link ConfigRegistryControllerMessenger} instead. */
|
|
95
|
+
export type ConfigRegistryMessenger = ConfigRegistryControllerMessenger;
|
|
96
|
+
export type ConfigRegistryControllerOptions = {
|
|
97
|
+
messenger: ConfigRegistryMessenger;
|
|
98
|
+
state?: Partial<ConfigRegistryControllerState>;
|
|
99
|
+
pollingInterval?: number;
|
|
100
|
+
fallbackConfig?: Record<string, RegistryNetworkConfig>;
|
|
101
|
+
isConfigRegistryApiEnabled?: (messenger: ConfigRegistryMessenger) => boolean;
|
|
102
|
+
};
|
|
103
|
+
declare const ConfigRegistryController_base: (abstract new (...args: any[]) => {
|
|
104
|
+
readonly "__#15@#intervalIds": Record<string, NodeJS.Timeout>;
|
|
105
|
+
"__#15@#intervalLength": number | undefined;
|
|
106
|
+
setIntervalLength(intervalLength: number): void;
|
|
107
|
+
getIntervalLength(): number | undefined;
|
|
108
|
+
_startPolling(input: null): void;
|
|
109
|
+
_stopPollingByPollingTokenSetId(key: string): void;
|
|
110
|
+
readonly "__#3@#pollingTokenSets": Map<string, Set<string>>;
|
|
111
|
+
readonly "__#3@#callbacks": Map<string, Set<(input: null) => void>>;
|
|
112
|
+
_executePoll(input: null): Promise<void>;
|
|
113
|
+
startPolling(input: null): string;
|
|
114
|
+
stopAllPolling(): void;
|
|
115
|
+
stopPollingByPollingToken(pollingToken: string): void;
|
|
116
|
+
onPollingComplete(input: null, callback: (input: null) => void): void;
|
|
117
|
+
}) & typeof import("@metamask/base-controller").BaseController;
|
|
118
|
+
export declare class ConfigRegistryController extends ConfigRegistryController_base<typeof controllerName, ConfigRegistryControllerState, ConfigRegistryMessenger> {
|
|
119
|
+
#private;
|
|
120
|
+
/**
|
|
121
|
+
* @param options - The controller options.
|
|
122
|
+
* @param options.messenger - The controller messenger. Must have
|
|
123
|
+
* `ConfigRegistryApiService:fetchConfig` action handler registered.
|
|
124
|
+
* @param options.state - Initial state.
|
|
125
|
+
* @param options.pollingInterval - Polling interval in milliseconds.
|
|
126
|
+
* @param options.fallbackConfig - Fallback configuration.
|
|
127
|
+
* @param options.isConfigRegistryApiEnabled - Function to check if the config
|
|
128
|
+
* registry API is enabled. Defaults to checking the remote feature flag.
|
|
129
|
+
*/
|
|
130
|
+
constructor({ messenger, state, pollingInterval, fallbackConfig, isConfigRegistryApiEnabled, }: ConfigRegistryControllerOptions);
|
|
131
|
+
_executePoll(_input: null): Promise<void>;
|
|
132
|
+
}
|
|
133
|
+
export {};
|
|
134
|
+
//# sourceMappingURL=ConfigRegistryController.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigRegistryController.d.mts","sourceRoot":"","sources":["../src/ConfigRegistryController.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
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");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _ConfigRegistryController_isConfigRegistryApiEnabled;
|
|
13
|
+
import { StaticIntervalPollingController } from "@metamask/polling-controller";
|
|
14
|
+
import { Duration, inMilliseconds } from "@metamask/utils";
|
|
15
|
+
import { isConfigRegistryApiEnabled as defaultIsConfigRegistryApiEnabled } from "./utils/feature-flags.mjs";
|
|
16
|
+
const controllerName = 'ConfigRegistryController';
|
|
17
|
+
export const DEFAULT_POLLING_INTERVAL = inMilliseconds(1, Duration.Day);
|
|
18
|
+
const stateMetadata = {
|
|
19
|
+
configs: {
|
|
20
|
+
persist: true,
|
|
21
|
+
includeInStateLogs: false,
|
|
22
|
+
includeInDebugSnapshot: true,
|
|
23
|
+
usedInUi: true,
|
|
24
|
+
},
|
|
25
|
+
version: {
|
|
26
|
+
persist: true,
|
|
27
|
+
includeInStateLogs: true,
|
|
28
|
+
includeInDebugSnapshot: true,
|
|
29
|
+
usedInUi: false,
|
|
30
|
+
},
|
|
31
|
+
lastFetched: {
|
|
32
|
+
persist: true,
|
|
33
|
+
includeInStateLogs: true,
|
|
34
|
+
includeInDebugSnapshot: true,
|
|
35
|
+
usedInUi: false,
|
|
36
|
+
},
|
|
37
|
+
etag: {
|
|
38
|
+
persist: true,
|
|
39
|
+
includeInStateLogs: false,
|
|
40
|
+
includeInDebugSnapshot: false,
|
|
41
|
+
usedInUi: false,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Default fallback configuration when no configs are available.
|
|
46
|
+
*/
|
|
47
|
+
const DEFAULT_FALLBACK_CONFIG = {};
|
|
48
|
+
export class ConfigRegistryController extends StaticIntervalPollingController() {
|
|
49
|
+
/**
|
|
50
|
+
* @param options - The controller options.
|
|
51
|
+
* @param options.messenger - The controller messenger. Must have
|
|
52
|
+
* `ConfigRegistryApiService:fetchConfig` action handler registered.
|
|
53
|
+
* @param options.state - Initial state.
|
|
54
|
+
* @param options.pollingInterval - Polling interval in milliseconds.
|
|
55
|
+
* @param options.fallbackConfig - Fallback configuration.
|
|
56
|
+
* @param options.isConfigRegistryApiEnabled - Function to check if the config
|
|
57
|
+
* registry API is enabled. Defaults to checking the remote feature flag.
|
|
58
|
+
*/
|
|
59
|
+
constructor({ messenger, state = {}, pollingInterval = DEFAULT_POLLING_INTERVAL, fallbackConfig = DEFAULT_FALLBACK_CONFIG, isConfigRegistryApiEnabled = defaultIsConfigRegistryApiEnabled, }) {
|
|
60
|
+
super({
|
|
61
|
+
name: controllerName,
|
|
62
|
+
metadata: stateMetadata,
|
|
63
|
+
messenger,
|
|
64
|
+
state: {
|
|
65
|
+
configs: {
|
|
66
|
+
networks: state.configs?.networks ?? { ...fallbackConfig },
|
|
67
|
+
},
|
|
68
|
+
version: state.version ?? null,
|
|
69
|
+
lastFetched: state.lastFetched ?? null,
|
|
70
|
+
etag: state.etag ?? null,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
_ConfigRegistryController_isConfigRegistryApiEnabled.set(this, void 0);
|
|
74
|
+
this.setIntervalLength(pollingInterval);
|
|
75
|
+
__classPrivateFieldSet(this, _ConfigRegistryController_isConfigRegistryApiEnabled, isConfigRegistryApiEnabled, "f");
|
|
76
|
+
this.messenger.registerActionHandler(`${controllerName}:startPolling`, this.startPolling.bind(this));
|
|
77
|
+
this.messenger.registerActionHandler(`${controllerName}:stopPolling`, this.stopAllPolling.bind(this));
|
|
78
|
+
this.messenger.subscribe('KeyringController:unlock', () => this.startPolling(null));
|
|
79
|
+
this.messenger.subscribe('KeyringController:lock', () => this.stopAllPolling());
|
|
80
|
+
}
|
|
81
|
+
async _executePoll(_input) {
|
|
82
|
+
const isApiEnabled = __classPrivateFieldGet(this, _ConfigRegistryController_isConfigRegistryApiEnabled, "f").call(this, this.messenger);
|
|
83
|
+
// Skip fetch when API is disabled; client uses static config.
|
|
84
|
+
if (!isApiEnabled) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const interval = this.getIntervalLength() ?? DEFAULT_POLLING_INTERVAL;
|
|
88
|
+
if (this.state.lastFetched !== null &&
|
|
89
|
+
Date.now() - this.state.lastFetched < interval) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const result = await this.messenger.call('ConfigRegistryApiService:fetchConfig', {
|
|
94
|
+
etag: this.state.etag ?? undefined,
|
|
95
|
+
});
|
|
96
|
+
if (!result.modified) {
|
|
97
|
+
this.update((state) => {
|
|
98
|
+
state.lastFetched = Date.now();
|
|
99
|
+
if (result.etag !== undefined) {
|
|
100
|
+
state.etag = result.etag ?? null;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const apiChains = result.data.data.chains;
|
|
106
|
+
const newConfigs = {};
|
|
107
|
+
// duplicate chainIds from API response are not expected
|
|
108
|
+
apiChains.forEach((chainConfig) => {
|
|
109
|
+
const { chainId } = chainConfig;
|
|
110
|
+
newConfigs[chainId] = chainConfig;
|
|
111
|
+
});
|
|
112
|
+
this.update((state) => {
|
|
113
|
+
state.configs.networks = newConfigs;
|
|
114
|
+
state.version = result.data.data.version;
|
|
115
|
+
state.lastFetched = Date.now();
|
|
116
|
+
if (result.etag !== undefined) {
|
|
117
|
+
state.etag = result.etag;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
const errorInstance = error instanceof Error ? error : new Error(String(error));
|
|
123
|
+
this.messenger.captureException?.(errorInstance);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
_ConfigRegistryController_isConfigRegistryApiEnabled = new WeakMap();
|
|
128
|
+
//# sourceMappingURL=ConfigRegistryController.mjs.map
|