{"version":3,"sources":["../src/errors.ts"],"sourcesContent":["import type {\n  ComposeErrorKind,\n  FailedPreparedOp,\n  GenericComposeErrorKind,\n  SimulationRevert,\n} from '@lifi/compose-spec';\n\nimport {\n  parseServerErrorBody,\n  type ServerErrorBody,\n} from './responseSchemas.js';\n\n/**\n * Machine-readable error codes returned by the SDK.\n *\n * - `NETWORK_ERROR` — The HTTP request failed (DNS, timeout, connection refused).\n * - `VALIDATION_ERROR` — The server rejected the request (HTTP 400/422).\n * - `UNAUTHENTICATED` — The request lacks valid authentication credentials (HTTP 401).\n * - `FORBIDDEN` — The server understood the request but refuses to authorise it (HTTP 403).\n * - `SERVER_ERROR` — The server returned a 5xx status.\n * - `RATE_LIMITED` — The server returned HTTP 429.\n * - `NOT_FOUND` — The requested resource does not exist (HTTP 404).\n * - `UNKNOWN_ERROR` — An unexpected error that doesn't fit other categories.\n */\nexport type ComposeErrorCode =\n  | 'NETWORK_ERROR'\n  | 'VALIDATION_ERROR'\n  | 'UNAUTHENTICATED'\n  | 'FORBIDDEN'\n  | 'SERVER_ERROR'\n  | 'RATE_LIMITED'\n  | 'NOT_FOUND'\n  | 'UNKNOWN_ERROR';\n\n/**\n * Error class for all failures originating from the Compose SDK or API.\n *\n * Includes structured metadata beyond the error message to support\n * programmatic error handling.\n *\n * @example\n * ```ts\n * try {\n *   await builder.compile(run);\n * } catch (e) {\n *   if (isComposeError(e) && e.code === 'VALIDATION_ERROR') {\n *     console.error('Invalid request:', e.message, e.path);\n *   }\n * }\n * ```\n */\nexport class ComposeError extends Error {\n  override readonly name = 'ComposeError';\n  /** Machine-readable error category. */\n  readonly code: ComposeErrorCode;\n  /** HTTP status code, when the error originated from an HTTP response. */\n  readonly status?: number;\n  /** The request URL that produced the error. */\n  readonly url?: string;\n  /** Server-provided error kind for finer-grained classification. */\n  readonly kind?: ComposeErrorKind;\n  /** JSON-pointer path to the field that caused a validation error. */\n  readonly path?: string;\n  /**\n   * Simulation revert diagnostics attached to `simulation_revert` errors.\n   * Contains the raw error bytes and decoded error candidates when the\n   * backend can parse the revert reason.\n   */\n  readonly details?: SimulationRevert;\n  /**\n   * The prepared ops that failed, attached to `preparation_error` errors.\n   * Each entry carries the `callId` of the failing node so callers can drop\n   * the unroutable legs and resubmit a smaller flow.\n   */\n  readonly failedOps?: readonly FailedPreparedOp[];\n  /**\n   * The `callId`s of the prepared ops that succeeded, attached to\n   * `preparation_error` errors alongside {@link ComposeError.failedOps}.\n   */\n  readonly succeededOps?: readonly string[];\n\n  constructor(\n    code: ComposeErrorCode,\n    message: string,\n    options?: {\n      status?: number;\n      url?: string;\n      cause?: unknown;\n      kind?: ComposeErrorKind;\n      path?: string;\n      details?: SimulationRevert;\n      failedOps?: readonly FailedPreparedOp[];\n      succeededOps?: readonly string[];\n    },\n  ) {\n    super(message, { cause: options?.cause });\n    this.code = code;\n    this.status = options?.status;\n    this.url = options?.url;\n    this.kind = options?.kind;\n    this.path = options?.path;\n    this.details = options?.details;\n    this.failedOps = options?.failedOps;\n    this.succeededOps = options?.succeededOps;\n  }\n}\n\n/**\n * The per-op preparation diagnostics carried by `preparation_error` responses,\n * with both arrays known to be present. Produced by narrowing with\n * {@link isComposePreparationError}.\n */\nexport interface ComposePreparationOps {\n  readonly failedOps: readonly FailedPreparedOp[];\n  readonly succeededOps: readonly string[];\n}\n\n/**\n * Type guard that narrows an unknown error to {@link ComposeError}.\n * @param e - The value to check.\n * @returns `true` if `e` is an instance of `ComposeError`.\n */\nexport const isComposeError = (e: unknown): e is ComposeError =>\n  e instanceof ComposeError ||\n  (e instanceof Error && e.name === 'ComposeError' && 'code' in e);\n\n/**\n * Type guard for `preparation_error` failures (HTTP 422): a mixed basket where\n * some prepared ops failed and others succeeded. Narrows both\n * {@link ComposeError.failedOps} and {@link ComposeError.succeededOps} to\n * present, so callers can read them without a cast or a truthiness check.\n *\n * @param e - The value to check.\n * @returns `true` if `e` is a `ComposeError` carrying per-op preparation diagnostics.\n *\n * @example\n * ```ts\n * try {\n *   await builder.compile(run);\n * } catch (e) {\n *   if (isComposePreparationError(e)) {\n *     console.error('Failed ops:', e.failedOps);\n *     console.error('Succeeded ops:', e.succeededOps);\n *   }\n * }\n * ```\n */\nexport const isComposePreparationError = (\n  e: unknown,\n): e is ComposeError & ComposePreparationOps =>\n  isComposeError(e) &&\n  e.kind === 'preparation_error' &&\n  Array.isArray(e.failedOps) &&\n  Array.isArray(e.succeededOps);\n\nconst STATUS_TO_CODE: ReadonlyMap<number, ComposeErrorCode> = new Map<\n  number,\n  ComposeErrorCode\n>([\n  [400, 'VALIDATION_ERROR'],\n  [401, 'UNAUTHENTICATED'],\n  [403, 'FORBIDDEN'],\n  [404, 'NOT_FOUND'],\n  [422, 'VALIDATION_ERROR'],\n  [429, 'RATE_LIMITED'],\n]);\n\nconst tryParseErrorBody = (body: string): ServerErrorBody | null => {\n  let json: unknown;\n  try {\n    json = JSON.parse(body);\n  } catch {\n    return null;\n  }\n  return parseServerErrorBody(json);\n};\n\n/**\n * Constructs a {@link ComposeError} from an HTTP error response, extracting\n * structured error details from the response body when available.\n *\n * @param status - The HTTP status code.\n * @param body - The raw response body text.\n * @param url - The request URL that produced the error.\n * @returns A `ComposeError` with the appropriate error code and metadata.\n */\nexport const errorFromHttpResponse = (\n  status: number,\n  body: string,\n  url: string,\n): ComposeError => {\n  const parsed = tryParseErrorBody(body);\n  const serverError = parsed?.error;\n\n  return new ComposeError(\n    STATUS_TO_CODE.get(status) ??\n      (status >= 500 ? 'SERVER_ERROR' : 'UNKNOWN_ERROR'),\n    (serverError?.message ?? body) || `HTTP ${status}`,\n    {\n      status,\n      url,\n      kind: serverError?.kind as ComposeErrorKind | undefined,\n      path: serverError?.path,\n      details: serverError?.details,\n      // The wire schema types `kind` as a bare string; the server only ever\n      // reports generic kinds for individual ops.\n      failedOps: serverError?.failedOps?.map((op) => ({\n        ...op,\n        kind: op.kind as GenericComposeErrorKind,\n      })),\n      succeededOps: serverError?.succeededOps,\n    },\n  );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,6BAGO;AAyCA,MAAM,qBAAqB,MAAM;AAAA,EACpB,OAAO;AAAA;AAAA,EAEhB;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,EAET,YACE,MACA,SACA,SAUA;AACA,UAAM,SAAS,EAAE,OAAO,SAAS,MAAM,CAAC;AACxC,SAAK,OAAO;AACZ,SAAK,SAAS,SAAS;AACvB,SAAK,MAAM,SAAS;AACpB,SAAK,OAAO,SAAS;AACrB,SAAK,OAAO,SAAS;AACrB,SAAK,UAAU,SAAS;AACxB,SAAK,YAAY,SAAS;AAC1B,SAAK,eAAe,SAAS;AAAA,EAC/B;AACF;AAiBO,MAAM,iBAAiB,CAAC,MAC7B,aAAa,gBACZ,aAAa,SAAS,EAAE,SAAS,kBAAkB,UAAU;AAuBzD,MAAM,4BAA4B,CACvC,MAEA,eAAe,CAAC,KAChB,EAAE,SAAS,uBACX,MAAM,QAAQ,EAAE,SAAS,KACzB,MAAM,QAAQ,EAAE,YAAY;AAE9B,MAAM,iBAAwD,oBAAI,IAGhE;AAAA,EACA,CAAC,KAAK,kBAAkB;AAAA,EACxB,CAAC,KAAK,iBAAiB;AAAA,EACvB,CAAC,KAAK,WAAW;AAAA,EACjB,CAAC,KAAK,WAAW;AAAA,EACjB,CAAC,KAAK,kBAAkB;AAAA,EACxB,CAAC,KAAK,cAAc;AACtB,CAAC;AAED,MAAM,oBAAoB,CAAC,SAAyC;AAClE,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,aAAO,6CAAqB,IAAI;AAClC;AAWO,MAAM,wBAAwB,CACnC,QACA,MACA,QACiB;AACjB,QAAM,SAAS,kBAAkB,IAAI;AACrC,QAAM,cAAc,QAAQ;AAE5B,SAAO,IAAI;AAAA,IACT,eAAe,IAAI,MAAM,MACtB,UAAU,MAAM,iBAAiB;AAAA,KACnC,aAAa,WAAW,SAAS,QAAQ,MAAM;AAAA,IAChD;AAAA,MACE;AAAA,MACA;AAAA,MACA,MAAM,aAAa;AAAA,MACnB,MAAM,aAAa;AAAA,MACnB,SAAS,aAAa;AAAA;AAAA;AAAA,MAGtB,WAAW,aAAa,WAAW,IAAI,CAAC,QAAQ;AAAA,QAC9C,GAAG;AAAA,QACH,MAAM,GAAG;AAAA,MACX,EAAE;AAAA,MACF,cAAc,aAAa;AAAA,IAC7B;AAAA,EACF;AACF;","names":[]}