{"version":3,"file":"config-registry-api-service.cjs","sourceRoot":"","sources":["../../src/config-registry-api-service/config-registry-api-service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,iEAMoC;AAUvB,QAAA,oBAAoB,GAC/B,iDAAiD,CAAC;AAEvC,QAAA,qBAAqB,GAAG,kBAAkB,CAAC;AAE3C,QAAA,eAAe,GAAG,EAAE,GAAG,IAAI,CAAC;AAazC,MAAa,wBAAwB;IAanC;;;;;;;;;;;;OAYG;IACH,YAAY,EACV,UAAU,GAAG,4BAAoB,EACjC,YAAY,GAAG,6BAAqB,EACpC,OAAO,GAAG,uBAAe,EACzB,KAAK,EAAE,WAAW,GAAG,UAAU,CAAC,KAAK,EACrC,iBAAiB,GAAG,6CAA0B,EAC9C,OAAO,GAAG,sCAAmB,EAC7B,0BAA0B,GAAG,mDAAgC,EAC7D,oBAAoB,GAAG,iDAA8B,MAClB,EAAE;QAhC9B,mDAAuB;QAEvB,uDAAoB;QAEpB,yDAAsB;QAEtB,oDAAiB;QAEjB,kDAAqB;QAyB5B,uBAAA,IAAI,wCAAe,UAAU,MAAA,CAAC;QAC9B,uBAAA,IAAI,0CAAiB,YAAY,MAAA,CAAC;QAClC,uBAAA,IAAI,qCAAY,OAAO,MAAA,CAAC;QACxB,uBAAA,IAAI,mCAAU,WAAW,MAAA,CAAC;QAE1B,uBAAA,IAAI,oCAAW,IAAA,sCAAmB,EAAC;YACjC,UAAU,EAAE,OAAO;YACnB,sBAAsB,EAAE,0BAA0B;YAClD,oBAAoB;YACpB,iBAAiB;SAClB,CAAC,MAAA,CAAC;IACL,CAAC;IAED,OAAO,CACL,GAAG,IAA0C;QAE7C,OAAO,uBAAA,IAAI,wCAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,UAAU,CACR,GAAG,IAA6C;QAEhD,OAAO,uBAAA,IAAI,wCAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,WAAW,CACf,UAA8B,EAAE;QAEhC,MAAM,OAAO,GAAG,uBAAA,IAAI,4CAAY,CAAC,QAAQ,CAAC,GAAG,CAAC;YAC5C,CAAC,CAAC,uBAAA,IAAI,4CAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/B,CAAC,CAAC,uBAAA,IAAI,4CAAY,CAAC;QACrB,MAAM,YAAY,GAAG,uBAAA,IAAI,8CAAc,CAAC,UAAU,CAAC,GAAG,CAAC;YACrD,CAAC,CAAC,uBAAA,IAAI,8CAAc;YACpB,CAAC,CAAC,IAAI,uBAAA,IAAI,8CAAc,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,OAAO,GAAG,YAAY,EAAE,CAAC,CAAC;QAEjD,MAAM,OAAO,GAAgB;YAC3B,eAAe,EAAE,UAAU;SAC5B,CAAC;QAEF,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,OAAO,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;QAC1C,CAAC;QAED,MAAM,gBAAgB,GAAG,KAAK,IAAuB,EAAE;YACrD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;YACzC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,uBAAA,IAAI,yCAAS,CAAC,CAAC;YAEtE,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,uBAAA,IAAI,uCAAO,MAAX,IAAI,EAAQ,GAAG,CAAC,QAAQ,EAAE,EAAE;oBACjD,OAAO;oBACP,MAAM,EAAE,UAAU,CAAC,MAAM;iBAC1B,CAAC,CAAC;gBACH,YAAY,CAAC,SAAS,CAAC,CAAC;gBACxB,OAAO,QAAQ,CAAC;YAClB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,YAAY,CAAC,SAAS,CAAC,CAAC;gBACxB,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;oBAC1D,MAAM,IAAI,KAAK,CAAC,yBAAyB,uBAAA,IAAI,yCAAS,IAAI,CAAC,CAAC;gBAC9D,CAAC;gBACD,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,uBAAA,IAAI,wCAAQ,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YACrD,MAAM,GAAG,GAAG,MAAM,gBAAgB,EAAE,CAAC;YAErC,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACvB,OAAO;oBACL,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE,GAAG,CAAC,OAAO;iBACE,CAAC;YAC3B,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CACb,2BAA2B,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAC1D,CAAC;YACJ,CAAC;YAED,OAAO,GAAG,CAAC;QACb,CAAC,CAAC,CAAC;QAEH,IAAK,QAA2C,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAChE,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC;YACvD,OAAO;gBACL,WAAW,EAAE,IAAI;gBACjB,IAAI;aACL,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC;QACvD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA8B,CAAC;QAElE,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACzE,CAAC;QAED,OAAO;YACL,IAAI;YACJ,IAAI;YACJ,WAAW,EAAE,KAAK;SACnB,CAAC;IACJ,CAAC;CACF;AA5ID,4DA4IC","sourcesContent":["import {\n  createServicePolicy,\n  DEFAULT_CIRCUIT_BREAK_DURATION,\n  DEFAULT_DEGRADED_THRESHOLD,\n  DEFAULT_MAX_CONSECUTIVE_FAILURES,\n  DEFAULT_MAX_RETRIES,\n} from '@metamask/controller-utils';\nimport type { ServicePolicy } from '@metamask/controller-utils';\n\nimport type {\n  AbstractConfigRegistryApiService,\n  FetchConfigOptions,\n  FetchConfigResult,\n  RegistryConfigApiResponse,\n} from './abstract-config-registry-api-service';\n\nexport const DEFAULT_API_BASE_URL =\n  'https://client-config.uat-api.cx.metamask.io/v1';\n\nexport const DEFAULT_ENDPOINT_PATH = '/config/networks';\n\nexport const DEFAULT_TIMEOUT = 10 * 1000;\n\nexport type ConfigRegistryApiServiceOptions = {\n  apiBaseUrl?: string;\n  endpointPath?: string;\n  timeout?: number;\n  fetch?: typeof fetch;\n  degradedThreshold?: number;\n  retries?: number;\n  maximumConsecutiveFailures?: number;\n  circuitBreakDuration?: number;\n};\n\nexport class ConfigRegistryApiService\n  implements AbstractConfigRegistryApiService\n{\n  readonly #policy: ServicePolicy;\n\n  readonly #apiBaseUrl: string;\n\n  readonly #endpointPath: string;\n\n  readonly #timeout: number;\n\n  readonly #fetch: typeof fetch;\n\n  /**\n   * Construct a Config Registry API Service.\n   *\n   * @param options - The options for constructing the service.\n   * @param options.apiBaseUrl - The base URL for the API. Defaults to the UAT API URL.\n   * @param options.endpointPath - The endpoint path. Defaults to '/config/networks'.\n   * @param options.timeout - Timeout for HTTP requests in milliseconds. Defaults to 10 seconds.\n   * @param options.fetch - Custom fetch function for testing or custom implementations. Defaults to the global fetch.\n   * @param options.degradedThreshold - The length of time (in milliseconds) that governs when the service is regarded as degraded. Defaults to 5 seconds.\n   * @param options.retries - Number of retry attempts for each fetch request. Defaults to 3.\n   * @param options.maximumConsecutiveFailures - The maximum number of consecutive failures allowed before breaking the circuit. Defaults to 3.\n   * @param options.circuitBreakDuration - The amount of time to wait when the circuit breaks from too many consecutive failures. Defaults to 2 minutes.\n   */\n  constructor({\n    apiBaseUrl = DEFAULT_API_BASE_URL,\n    endpointPath = DEFAULT_ENDPOINT_PATH,\n    timeout = DEFAULT_TIMEOUT,\n    fetch: customFetch = globalThis.fetch,\n    degradedThreshold = DEFAULT_DEGRADED_THRESHOLD,\n    retries = DEFAULT_MAX_RETRIES,\n    maximumConsecutiveFailures = DEFAULT_MAX_CONSECUTIVE_FAILURES,\n    circuitBreakDuration = DEFAULT_CIRCUIT_BREAK_DURATION,\n  }: ConfigRegistryApiServiceOptions = {}) {\n    this.#apiBaseUrl = apiBaseUrl;\n    this.#endpointPath = endpointPath;\n    this.#timeout = timeout;\n    this.#fetch = customFetch;\n\n    this.#policy = createServicePolicy({\n      maxRetries: retries,\n      maxConsecutiveFailures: maximumConsecutiveFailures,\n      circuitBreakDuration,\n      degradedThreshold,\n    });\n  }\n\n  onBreak(\n    ...args: Parameters<ServicePolicy['onBreak']>\n  ): ReturnType<ServicePolicy['onBreak']> {\n    return this.#policy.onBreak(...args);\n  }\n\n  onDegraded(\n    ...args: Parameters<ServicePolicy['onDegraded']>\n  ): ReturnType<ServicePolicy['onDegraded']> {\n    return this.#policy.onDegraded(...args);\n  }\n\n  async fetchConfig(\n    options: FetchConfigOptions = {},\n  ): Promise<FetchConfigResult> {\n    const baseUrl = this.#apiBaseUrl.endsWith('/')\n      ? this.#apiBaseUrl.slice(0, -1)\n      : this.#apiBaseUrl;\n    const endpointPath = this.#endpointPath.startsWith('/')\n      ? this.#endpointPath\n      : `/${this.#endpointPath}`;\n    const url = new URL(`${baseUrl}${endpointPath}`);\n\n    const headers: HeadersInit = {\n      'Cache-Control': 'no-cache',\n    };\n\n    if (options.etag) {\n      headers['If-None-Match'] = options.etag;\n    }\n\n    const fetchWithTimeout = async (): Promise<Response> => {\n      const controller = new AbortController();\n      const timeoutId = setTimeout(() => controller.abort(), this.#timeout);\n\n      try {\n        const response = await this.#fetch(url.toString(), {\n          headers,\n          signal: controller.signal,\n        });\n        clearTimeout(timeoutId);\n        return response;\n      } catch (error) {\n        clearTimeout(timeoutId);\n        if (error instanceof Error && error.name === 'AbortError') {\n          throw new Error(`Request timeout after ${this.#timeout}ms`);\n        }\n        throw error;\n      }\n    };\n\n    const response = await this.#policy.execute(async () => {\n      const res = await fetchWithTimeout();\n\n      if (res.status === 304) {\n        return {\n          status: 304,\n          headers: res.headers,\n        } as unknown as Response;\n      }\n\n      if (!res.ok) {\n        throw new Error(\n          `Failed to fetch config: ${res.status} ${res.statusText}`,\n        );\n      }\n\n      return res;\n    });\n\n    if ((response as unknown as { status?: number }).status === 304) {\n      const etag = response.headers.get('ETag') ?? undefined;\n      return {\n        notModified: true,\n        etag,\n      };\n    }\n\n    const etag = response.headers.get('ETag') ?? undefined;\n    const data = (await response.json()) as RegistryConfigApiResponse;\n\n    if (!data?.data || !Array.isArray(data.data.networks)) {\n      throw new Error('Invalid response structure from config registry API');\n    }\n\n    return {\n      data,\n      etag,\n      notModified: false,\n    };\n  }\n}\n"]}