{"version":3,"file":"errors.mjs","names":[],"sources":["../../src/core/errors.ts"],"sourcesContent":["/** Explorers error hierarchy */\n\nimport { FetchError } from \"ofetch\";\n\n/**\n * Base class for failures surfaced through Explorers.\n *\n * Every message passes through `sanitizeUrl` here, so secret query params are redacted at one\n * boundary instead of at each construction site.\n */\nexport class ExplorerError extends Error {\n  constructor(\n    message: string,\n    public readonly provider?: string,\n  ) {\n    super(sanitizeUrl(message));\n    this.name = \"ExplorerError\";\n  }\n}\n\n/* Strip API keys from URLs and URL-bearing text for safe error messages */\nfunction sanitizeUrl(url: string): string {\n  return url.replaceAll(/([?&])(api[-_]?key|key|secret|token)=[^&#]*/gi, \"$1$2=REDACTED\");\n}\n\n/** HTTP failure with a redacted request URL in its message and a redacted response body. */\nexport class HTTPError extends ExplorerError {\n  /**\n   * Request URL with secret query params redacted. Non-enumerable to keep serialized errors\n   * compact.\n   */\n  public readonly rawUrl: string;\n\n  /** Response body, redacted in case the server echoes the request URL. */\n  public readonly body?: string;\n\n  constructor(\n    public readonly statusCode: number,\n    url: string,\n    body?: string,\n    provider?: string,\n  ) {\n    super(`HTTP ${statusCode} from ${url}`, provider);\n    if (body !== undefined) this.body = sanitizeUrl(body);\n    this.rawUrl = sanitizeUrl(url);\n    Object.defineProperty(this, \"rawUrl\", { enumerable: false });\n    this.name = \"HTTPError\";\n  }\n}\n\n/** Provider credentials were missing or rejected. */\nexport class AuthError extends ExplorerError {\n  constructor(provider: string, detail?: string) {\n    super(`Authentication failed for ${provider}${detail ? `: ${detail}` : \"\"}`, provider);\n    this.name = \"AuthError\";\n  }\n}\n\n/** Provider refused a request because its rate limit was reached. */\nexport class RateLimitError extends ExplorerError {\n  constructor(\n    provider: string,\n    public readonly retryAfter?: number,\n  ) {\n    super(\n      `Rate limited by ${provider}${retryAfter ? ` (retry after ${retryAfter}s)` : \"\"}`,\n      provider,\n    );\n    this.name = \"RateLimitError\";\n  }\n}\n\n/** Provider credentials are valid, but the current plan does not cover the requested read. */\nexport class PlanRestrictedError extends ExplorerError {\n  constructor(provider: string, detail?: string) {\n    super(`Plan restricted by ${provider}${detail ? `: ${detail}` : \"\"}`, provider);\n    this.name = \"PlanRestrictedError\";\n  }\n}\n\n/** Requested transaction, address, contract, or block was not found. */\nexport class NotFoundError extends ExplorerError {\n  constructor(resource: string, provider?: string) {\n    super(`Not found: ${resource}`, provider);\n    this.name = \"NotFoundError\";\n  }\n}\n\n/** Provider does not serve the requested chain. */\nexport class UnsupportedChainError extends ExplorerError {\n  constructor(chain: string, provider: string) {\n    super(`Chain \"${chain}\" not supported by ${provider}`, provider);\n    this.name = \"UnsupportedChainError\";\n  }\n}\n\n/** Explorer backend does not expose the requested operation. */\nexport class UnsupportedOperationError extends ExplorerError {\n  constructor(operation: string, provider: string) {\n    super(`Operation \"${operation}\" not supported by ${provider}`, provider);\n    this.name = \"UnsupportedOperationError\";\n  }\n}\n\n/** Registry does not contain the requested provider name. */\nexport class UnknownProviderError extends ExplorerError {\n  constructor(provider: string) {\n    super(`Unknown provider: ${provider}`, provider);\n    this.name = \"UnknownProviderError\";\n  }\n}\nfunction getFetchErrorUrl(error: FetchError): string | undefined {\n  const request = error.request;\n  if (typeof request === \"string\") return request;\n  if (request instanceof URL) return request.href;\n  if (typeof Request !== \"undefined\" && request instanceof Request) return request.url;\n  return undefined;\n}\n\nfunction getFetchErrorBody(error: FetchError): string | undefined {\n  if (typeof error.data === \"string\") return error.data;\n  if (error.data === undefined) return undefined;\n  try {\n    return JSON.stringify(error.data);\n  } catch {\n    return String(error.data);\n  }\n}\n\ninterface FailureContext {\n  readonly fetchError?: FetchError;\n  readonly lowerMessage: string;\n  readonly message: string;\n  readonly provider?: string;\n  readonly resource: string;\n  readonly status: number;\n  readonly url?: string;\n}\n\nfunction isAuthenticationFailure(context: FailureContext): boolean {\n  return (\n    context.status === 401 ||\n    context.status === 403 ||\n    context.lowerMessage.includes(\"unauthorized\")\n  );\n}\n\nfunction isTransportFailure(context: FailureContext): boolean {\n  if (context.status > 0 || context.url !== undefined) return true;\n  return [\"econnrefused\", \"etimedout\", \"timeouterror\"].some((fragment) =>\n    context.lowerMessage.includes(fragment),\n  );\n}\n\nfunction isNotFoundFailure(context: FailureContext): boolean {\n  return context.status === 404 || context.lowerMessage.includes(\"not found\");\n}\n\nfunction isRateLimitFailure(context: FailureContext): boolean {\n  return context.status === 429 || context.lowerMessage.includes(\"rate limit\");\n}\n\nfunction retryAfterSeconds(error: FetchError | undefined): number | undefined {\n  const header = error?.response?.headers?.get(\"retry-after\");\n  if (header === null || header === undefined) return undefined;\n  const trimmed = header.trim();\n  if (!/^\\d+$/.test(trimmed)) return undefined;\n  const parsed = Number.parseInt(trimmed, 10);\n  return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction authenticationError(context: FailureContext): AuthError {\n  const detail = context.url ? `HTTP ${context.status} from ${context.url}` : context.message;\n  return new AuthError(context.provider ?? \"unknown\", detail);\n}\n\nfunction transportError(context: FailureContext): HTTPError {\n  const body = context.fetchError ? getFetchErrorBody(context.fetchError) : undefined;\n  return new HTTPError(\n    context.status,\n    context.url ?? \"unknown\",\n    body ?? context.message,\n    context.provider,\n  );\n}\n\nfunction classifyFailure(context: FailureContext): ExplorerError | undefined {\n  if (isNotFoundFailure(context)) return new NotFoundError(context.resource, context.provider);\n  if (isRateLimitFailure(context)) {\n    return new RateLimitError(context.provider ?? \"unknown\", retryAfterSeconds(context.fetchError));\n  }\n  if (isAuthenticationFailure(context)) return authenticationError(context);\n  return isTransportFailure(context) ? transportError(context) : undefined;\n}\n\nfunction errorMessage(error: unknown): string {\n  return error instanceof Error ? error.message : String(error);\n}\n\nfunction errorStatus(error: FetchError | undefined, message: string): number {\n  const statusMatch = message.match(/HTTP (\\d{3})/i);\n  return error?.statusCode ?? Number(statusMatch?.[1] ?? 0);\n}\n\n/**\n * Turn an unknown provider or transport failure into the Explorers error hierarchy.\n *\n * Existing `ExplorerError` instances pass through unchanged. Structured HTTP failures retain their\n * status, response body, and redacted request URL.\n *\n * @param {unknown} error - The `error` value.\n * @param {string} provider - The `provider` value.\n * @param {string} requestUrl - The `requestUrl` value.\n * @returns {ExplorerError} The resulting value.\n */\nexport function normalizeError(\n  error: unknown,\n  provider?: string,\n  requestUrl?: string,\n): ExplorerError {\n  if (error instanceof ExplorerError) return error;\n\n  const message = errorMessage(error);\n  const lowerMessage = message.toLowerCase();\n  const fetchError = error instanceof FetchError ? error : undefined;\n  const status = errorStatus(fetchError, message);\n  const fetchUrl = fetchError ? getFetchErrorUrl(fetchError) : undefined;\n  const url = requestUrl ?? fetchUrl;\n  const context: FailureContext = {\n    fetchError,\n    lowerMessage,\n    message,\n    provider,\n    resource: url ?? message,\n    status,\n    url,\n  };\n\n  return classifyFailure(context) ?? new ExplorerError(message, provider);\n}\n"],"mappings":"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"}