{"version":3,"file":"base-client.cjs","sourceRoot":"","sources":["../../src/api/base-client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Base API Client - Shared HTTP functionality for all API clients.\n */\n\nimport { QueryClient } from '@tanstack/query-core';\nimport type { QueryKey } from '@tanstack/query-core';\n\nimport {\n  API_URLS,\n  STALE_TIMES,\n  GC_TIMES,\n  calculateRetryDelay,\n  shouldRetry,\n  HttpError,\n} from './shared-types';\nimport type { ApiPlatformClientOptions } from './shared-types';\n\n// Auth query keys - shared for token management across clients\nexport const authQueryKeys = {\n  bearerToken: (): QueryKey => ['auth', 'bearerToken'],\n} as const;\n\nexport type { ApiPlatformClientOptions };\n\n/**\n * Internal fetch options for HTTP requests.\n */\nexport type InternalFetchOptions = {\n  signal?: AbortSignal;\n  params?: Record<\n    string,\n    string | string[] | number | number[] | boolean | undefined\n  >;\n};\n\n/**\n * Base API Client with shared HTTP and caching functionality.\n * Extended by all specific API clients.\n */\nexport class BaseApiClient {\n  protected readonly clientProduct: string;\n\n  protected readonly clientVersion?: string;\n\n  protected readonly getBearerToken?: () => Promise<string | undefined>;\n\n  readonly #queryClientInstance: QueryClient;\n\n  /**\n   * Get the underlying QueryClient instance.\n   * Exposed for cache management operations.\n   *\n   * @returns The QueryClient instance.\n   */\n  get queryClient(): QueryClient {\n    return this.#queryClientInstance;\n  }\n\n  /**\n   * Invalidate the cached auth token.\n   * Call this when the user logs out or the token expires.\n   *\n   * Uses resetQueries() instead of invalidateQueries() to completely remove\n   * the cached value, ensuring the next request fetches a fresh token immediately.\n   */\n  async invalidateAuthToken(): Promise<void> {\n    await this.#queryClientInstance.resetQueries({\n      queryKey: authQueryKeys.bearerToken(),\n    });\n  }\n\n  constructor(options: ApiPlatformClientOptions) {\n    this.clientProduct = options.clientProduct;\n    this.clientVersion = options.clientVersion;\n    this.getBearerToken = options.getBearerToken;\n\n    this.#queryClientInstance =\n      options.queryClient ??\n      new QueryClient({\n        defaultOptions: {\n          queries: {\n            staleTime: STALE_TIMES.DEFAULT,\n            gcTime: GC_TIMES.DEFAULT,\n            retry: shouldRetry,\n            retryDelay: calculateRetryDelay,\n            refetchOnWindowFocus: false,\n            networkMode: 'always',\n          },\n        },\n      });\n  }\n\n  /**\n   * Internal HTTP fetch method with authentication and error handling.\n   *\n   * @param baseUrl - The base URL for the API.\n   * @param path - The API endpoint path.\n   * @param options - Optional fetch configuration.\n   * @returns The parsed JSON response.\n   */\n  protected async fetch<ResponseType>(\n    baseUrl: string,\n    path: string,\n    options?: InternalFetchOptions,\n  ): Promise<ResponseType> {\n    const url = new URL(path, baseUrl);\n\n    if (options?.params) {\n      for (const [key, value] of Object.entries(options.params)) {\n        if (value === undefined) {\n          continue;\n        }\n        if (Array.isArray(value)) {\n          // Convert array values (including number[]) to comma-separated string\n          url.searchParams.set(key, value.map(String).join(','));\n        } else {\n          url.searchParams.set(key, String(value));\n        }\n      }\n    }\n\n    const headers: Record<string, string> = {\n      'Content-Type': 'application/json',\n      'x-metamask-clientproduct': this.clientProduct,\n    };\n\n    if (this.clientVersion) {\n      headers['x-metamask-clientversion'] = this.clientVersion;\n    }\n\n    // Get bearer token using fetchQuery for automatic deduplication\n    if (this.getBearerToken) {\n      const queryKey = authQueryKeys.bearerToken();\n      const { getBearerToken } = this;\n\n      try {\n        // fetchQuery handles caching and deduplicates concurrent requests\n        const token = await this.#queryClientInstance.fetchQuery({\n          queryKey,\n          queryFn: async () => {\n            const result = await getBearerToken();\n            // Throw if no token - prevents caching null/undefined\n            // so subsequent requests can retry (e.g., after user logs in)\n            if (!result) {\n              throw new Error('No bearer token available');\n            }\n            return result;\n          },\n          staleTime: STALE_TIMES.AUTH_TOKEN,\n          retry: false, // Don't retry auth failures\n        });\n\n        headers.Authorization = `Bearer ${token}`;\n      } catch {\n        // No token available - continue without auth header\n      }\n    }\n\n    const response = await fetch(url.toString(), {\n      method: 'GET',\n      headers,\n      signal: options?.signal,\n    });\n\n    if (!response.ok) {\n      let body: unknown;\n      try {\n        body = await response.json();\n      } catch {\n        // Response body is not JSON or is empty, leave body as undefined\n      }\n      throw new HttpError(\n        `HTTP ${response.status}: ${response.statusText}`,\n        response.status,\n        response.statusText,\n        url.toString(),\n        body,\n      );\n    }\n\n    return response.json() as Promise<ResponseType>;\n  }\n}\n\n// Re-export constants for use by API clients\nexport { API_URLS, STALE_TIMES, GC_TIMES, HttpError };\n"]}