{"version":3,"file":"ConfigRegistryController.mjs","sourceRoot":"","sources":["../src/ConfigRegistryController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  ControllerGetStateAction,\n  ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { RemoteFeatureFlagControllerState } from '@metamask/remote-feature-flag-controller';\nimport type { Json } from '@metamask/utils';\n\nimport type {\n  AbstractConfigRegistryApiService,\n  FetchConfigResult,\n} from './config-registry-api-service';\nimport {\n  ConfigRegistryApiService,\n  filterNetworks,\n} from './config-registry-api-service';\nimport { isConfigRegistryApiEnabled } from './utils/feature-flags';\n\nconst controllerName = 'ConfigRegistryController';\n\nexport const DEFAULT_POLLING_INTERVAL = 24 * 60 * 60 * 1000;\n\nexport type RegistryConfigEntry = {\n  key: string;\n  value: Json;\n  metadata?: Json;\n};\n\nexport type ConfigRegistryState = {\n  configs: {\n    networks?: Record<string, RegistryConfigEntry>;\n  };\n  version: string | null;\n  lastFetched: number | null;\n  fetchError: string | null;\n  etag: string | null;\n};\n\nconst stateMetadata = {\n  configs: {\n    persist: true,\n    anonymous: false,\n    includeInStateLogs: false,\n    includeInDebugSnapshot: true,\n    usedInUi: true,\n  },\n  version: {\n    persist: true,\n    anonymous: false,\n    includeInStateLogs: true,\n    includeInDebugSnapshot: true,\n    usedInUi: false,\n  },\n  lastFetched: {\n    persist: true,\n    anonymous: false,\n    includeInStateLogs: true,\n    includeInDebugSnapshot: true,\n    usedInUi: false,\n  },\n  fetchError: {\n    persist: true,\n    anonymous: false,\n    includeInStateLogs: true,\n    includeInDebugSnapshot: true,\n    usedInUi: false,\n  },\n  etag: {\n    persist: true,\n    anonymous: false,\n    includeInStateLogs: false,\n    includeInDebugSnapshot: false,\n    usedInUi: false,\n  },\n};\n\nconst DEFAULT_FALLBACK_CONFIG: Record<string, RegistryConfigEntry> = {};\n\ntype ConfigRegistryPollingInput = Record<string, never>;\n\nexport type ConfigRegistryControllerStateChangeEvent =\n  ControllerStateChangeEvent<typeof controllerName, ConfigRegistryState>;\n\nexport type ConfigRegistryControllerGetStateAction = ControllerGetStateAction<\n  typeof controllerName,\n  ConfigRegistryState\n>;\n\nexport type ConfigRegistryControllerGetConfigAction = {\n  type: `${typeof controllerName}:getConfig`;\n  handler: ConfigRegistryController['getConfig'];\n};\n\nexport type ConfigRegistryControllerSetConfigAction = {\n  type: `${typeof controllerName}:setConfig`;\n  handler: ConfigRegistryController['setConfig'];\n};\n\nexport type ConfigRegistryControllerGetAllConfigsAction = {\n  type: `${typeof controllerName}:getAllConfigs`;\n  handler: ConfigRegistryController['getAllConfigs'];\n};\n\nexport type ConfigRegistryControllerStartPollingAction = {\n  type: `${typeof controllerName}:startPolling`;\n  handler: (input: ConfigRegistryPollingInput) => string;\n};\n\nexport type ConfigRegistryControllerStopPollingAction = {\n  type: `${typeof controllerName}:stopPolling`;\n  handler: () => void;\n};\n\nexport type ConfigRegistryControllerActions =\n  | ConfigRegistryControllerGetStateAction\n  | ConfigRegistryControllerGetConfigAction\n  | ConfigRegistryControllerSetConfigAction\n  | ConfigRegistryControllerGetAllConfigsAction\n  | ConfigRegistryControllerStartPollingAction\n  | ConfigRegistryControllerStopPollingAction\n  | {\n      type: 'RemoteFeatureFlagController:getState';\n      handler: () => RemoteFeatureFlagControllerState;\n    };\n\nexport type ConfigRegistryControllerEvents =\n  ConfigRegistryControllerStateChangeEvent;\n\nexport type ConfigRegistryMessenger = Messenger<\n  typeof controllerName,\n  ConfigRegistryControllerActions,\n  ConfigRegistryControllerEvents\n>;\n\nexport type ConfigRegistryControllerOptions = {\n  messenger: ConfigRegistryMessenger;\n  state?: Partial<ConfigRegistryState>;\n  pollingInterval?: number;\n  fallbackConfig?: Record<string, RegistryConfigEntry>;\n  apiService?: AbstractConfigRegistryApiService;\n};\n\nexport class ConfigRegistryController extends StaticIntervalPollingController<ConfigRegistryPollingInput>()<\n  typeof controllerName,\n  ConfigRegistryState,\n  ConfigRegistryMessenger\n> {\n  readonly #fallbackConfig: Record<string, RegistryConfigEntry>;\n\n  readonly #apiService: AbstractConfigRegistryApiService;\n\n  /**\n   * @param options - The controller options.\n   * @param options.messenger - The controller messenger.\n   * @param options.state - Initial state.\n   * @param options.pollingInterval - Polling interval in milliseconds.\n   * @param options.fallbackConfig - Fallback configuration.\n   * @param options.apiService - The API service.\n   */\n  constructor({\n    messenger,\n    state = {},\n    pollingInterval = DEFAULT_POLLING_INTERVAL,\n    fallbackConfig = DEFAULT_FALLBACK_CONFIG,\n    apiService = new ConfigRegistryApiService(),\n  }: ConfigRegistryControllerOptions) {\n    super({\n      name: controllerName,\n      metadata: stateMetadata,\n      messenger,\n      state: {\n        configs: { networks: {} },\n        version: null,\n        lastFetched: null,\n        fetchError: null,\n        etag: null,\n        ...state,\n      },\n    });\n\n    this.setIntervalLength(pollingInterval);\n    this.#fallbackConfig = fallbackConfig;\n    this.#apiService = apiService;\n\n    this.messenger.registerActionHandler(\n      `${controllerName}:getConfig`,\n      (key: string) => this.getConfig(key),\n    );\n\n    this.messenger.registerActionHandler(\n      `${controllerName}:setConfig`,\n      (key: string, value: Json, metadata?: Json) =>\n        this.setConfig(key, value, metadata),\n    );\n\n    this.messenger.registerActionHandler(\n      `${controllerName}:getAllConfigs`,\n      () => this.getAllConfigs(),\n    );\n\n    this.messenger.registerActionHandler(\n      `${controllerName}:startPolling`,\n      (input: ConfigRegistryPollingInput) => this.startPolling(input),\n    );\n\n    this.messenger.registerActionHandler(`${controllerName}:stopPolling`, () =>\n      this.stopPolling(),\n    );\n  }\n\n  async _executePoll(_input: ConfigRegistryPollingInput): Promise<void> {\n    const isApiEnabled = isConfigRegistryApiEnabled(this.messenger);\n\n    if (!isApiEnabled) {\n      this.#useFallbackConfig(\n        'Feature flag disabled - using fallback configuration',\n      );\n      return;\n    }\n\n    try {\n      const result: FetchConfigResult = await this.#apiService.fetchConfig({\n        etag: this.state.etag ?? undefined,\n      });\n\n      if (result.notModified) {\n        this.update((state) => {\n          state.fetchError = null;\n        });\n        return;\n      }\n\n      // Filter networks: only featured, active, non-testnet networks\n      const filteredNetworks = filterNetworks(result.data.data.networks, {\n        isFeatured: true,\n        isActive: true,\n        isTestnet: false,\n      });\n\n      const newConfigs: Record<string, RegistryConfigEntry> = {};\n      for (const network of filteredNetworks) {\n        newConfigs[network.chainId] = {\n          key: network.chainId,\n          value: network as unknown as Json,\n        };\n      }\n\n      this.update((state) => {\n        // eslint-disable-next-line @typescript-eslint/no-explicit-any\n        (state.configs as any) = { networks: newConfigs };\n        state.version = result.data.data.version;\n        state.lastFetched = Date.now();\n        state.fetchError = null;\n        state.etag = result.etag ?? null;\n      });\n    } catch (error) {\n      this.#handleFetchError(error);\n    }\n  }\n\n  #useFallbackConfig(errorMessage?: string): void {\n    this.update((state) => {\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      (state.configs as any) = { networks: { ...this.#fallbackConfig } };\n      state.fetchError =\n        errorMessage ?? 'Using fallback configuration - API unavailable';\n      state.etag = null;\n    });\n  }\n\n  #handleFetchError(error: unknown): void {\n    const errorMessage =\n      error instanceof Error ? error.message : 'Unknown error occurred';\n\n    const hasNoConfigs =\n      Object.keys(this.state.configs?.networks ?? {}).length === 0;\n\n    if (hasNoConfigs) {\n      this.#useFallbackConfig(errorMessage);\n    } else {\n      this.update((state) => {\n        state.fetchError = errorMessage;\n      });\n    }\n  }\n\n  getConfig(key: string): RegistryConfigEntry | undefined {\n    return this.state.configs?.networks?.[key];\n  }\n\n  getAllConfigs(): Record<string, RegistryConfigEntry> {\n    return { ...(this.state.configs?.networks ?? {}) };\n  }\n\n  getConfigValue<TValue = Json>(key: string): TValue | undefined {\n    const entry = this.state.configs?.networks?.[key];\n    return entry?.value as TValue | undefined;\n  }\n\n  setConfig(key: string, value: Json, metadata?: Json): void {\n    this.update((state) => {\n      if (!state.configs) {\n        // eslint-disable-next-line @typescript-eslint/no-explicit-any\n        (state.configs as any) = { networks: {} };\n      }\n      if (!state.configs.networks) {\n        // eslint-disable-next-line @typescript-eslint/no-explicit-any\n        (state.configs.networks as any) = {};\n      }\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      (state.configs.networks as any)[key] = {\n        key,\n        value,\n        metadata,\n      };\n    });\n  }\n\n  removeConfig(key: string): void {\n    this.update((state) => {\n      if (state.configs?.networks) {\n        delete state.configs.networks[key];\n      }\n    });\n  }\n\n  clearConfigs(): void {\n    this.update((state) => {\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      (state.configs as any) = { networks: {} };\n    });\n  }\n\n  startPolling(input: ConfigRegistryPollingInput = {}): string {\n    return super.startPolling(input);\n  }\n\n  stopPolling(): void {\n    super.stopAllPolling();\n  }\n}\n"]}