{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type {\n  ComposeCompilePartialData,\n  ComposeCompileRequest,\n  ComposeCompileResult,\n  ComposeCompileSuccessData,\n  ComposeManifest,\n} from '@lifi/compose-spec';\n\nimport type { GetZapPacksOptions, ZapPackOverview } from './discovery.js';\nimport { ComposeError, errorFromHttpResponse } from './errors.js';\n\n// __SDK_VERSION__ is a compile-time constant injected by tsup (via `define` in tsup.config.ts)\n// and by vitest (via `define` in vitest.config.ts). Both read the version from package.json\n// at build/test time and replace this identifier with the literal string value.\n// It is sent as the `x-lifi-composer-sdk` request header so the server can identify the caller.\n// Falls back to 'dev' when running via tsx without tsup substitution (e.g. the example harness).\ndeclare const __SDK_VERSION__: string;\n\nconst SDK_VERSION: string =\n  typeof __SDK_VERSION__ !== 'undefined' ? __SDK_VERSION__ : 'dev';\n\n/**\n * Configuration for creating a low-level Compose API client.\n */\nexport interface ComposeClientOptions {\n  /** Base URL of the Compose API. */\n  readonly baseUrl: string;\n  /** Optional custom `fetch` implementation. Defaults to `globalThis.fetch`. */\n  readonly fetch?: typeof globalThis.fetch;\n  /** Optional LI.FI API key. When set, sent as the `x-lifi-api-key` header on every request. */\n  readonly apiKey?: string;\n}\n\n/**\n * Low-level HTTP client for the Compose API.\n *\n * Handles request serialization, SDK version headers, and error mapping.\n * Prefer using {@link ComposeSdk} for the full builder experience. Use this\n * directly when you need to decouple request building from submission — e.g.\n * build via `sdk.request()` then submit via `client.compile()` with custom\n * retry logic or request inspection.\n */\nexport interface ComposeClient {\n  /**\n   * Fetches the server's operation manifest describing all supported operations,\n   * guards, materialisers, and preconditions.\n   * @returns The manifest document.\n   * @throws {@link ComposeError} on network, validation, or server errors.\n   */\n  readonly getManifest: () => Promise<ComposeManifest>;\n  /**\n   * Submits a compile request and returns the result.\n   *\n   * When the caller passes `simulationPolicy: 'allow-revert'` and the transaction\n   * reverts in simulation, the server responds with HTTP 206 and the SDK returns a\n   * partial result (`status: 'partial'`) instead of throwing. The partial result\n   * includes the transaction (without `gasLimit`) and revert diagnostics.\n   *\n   * @param request - The full compile request including flow and run inputs.\n   * @returns A discriminated result: `status: 'success'` or `status: 'partial'`.\n   * @throws {@link ComposeError} on network, validation, or server errors.\n   */\n  readonly compile: (\n    request: ComposeCompileRequest,\n  ) => Promise<ComposeCompileResult>;\n  /**\n   * Fetches the available routing edges grouped by protocol.\n   *\n   * The edge catalog is dynamic — it reflects the current state of the\n   * backend's routing snapshot (protocols, chains, token blacklists).\n   * Results are not cached by the SDK; callers should cache as appropriate.\n   *\n   * @param options - Optional filter to restrict results to specific protocols.\n   * @returns An array of {@link ZapPackOverview} objects, one per protocol.\n   * @throws {@link ComposeError} on network or server errors (503 when the\n   *   routing catalog is not yet initialized).\n   */\n  readonly getZapPacks: (\n    options?: GetZapPacksOptions,\n  ) => Promise<readonly ZapPackOverview[]>;\n}\n\nconst bigintReplacer = (_key: string, value: unknown): unknown =>\n  typeof value === 'bigint' ? value.toString() : value;\n\nconst isNonNullObject = (v: unknown): v is Record<string, unknown> =>\n  typeof v === 'object' && v !== null;\n\nconst parseBody = async <T>(res: Response, url: string): Promise<T> => {\n  const body = await res.json().catch((_) => null);\n  if (!isNonNullObject(body) || !('data' in body)) {\n    throw new ComposeError('UNKNOWN_ERROR', 'Unexpected response format', {\n      url,\n    });\n  }\n  return body.data as T;\n};\n\nconst parseCompileSuccessBody = async (\n  res: Response,\n  url: string,\n): Promise<ComposeCompileResult> => {\n  const data = await parseBody<ComposeCompileSuccessData>(res, url);\n  return { ...data, status: 'success' as const };\n};\n\nconst parsePartialBody = async (\n  res: Response,\n  url: string,\n): Promise<ComposeCompileResult> => {\n  const body = await res.json().catch((_) => null);\n  if (\n    !isNonNullObject(body) ||\n    !('data' in body) ||\n    !isNonNullObject(body.data) ||\n    !('error' in body) ||\n    !isNonNullObject(body.error)\n  ) {\n    throw new ComposeError(\n      'UNKNOWN_ERROR',\n      'Unexpected partial response format',\n      { url },\n    );\n  }\n  const data = body.data as unknown as ComposeCompilePartialData;\n  const error = body.error as unknown as { kind: string; message: string };\n  return { ...data, status: 'partial' as const, error };\n};\n\n/**\n * Creates a low-level Compose API client.\n *\n * @param options - Client configuration including the API base URL.\n * @returns A {@link ComposeClient} instance.\n */\nexport const createComposeClient = (\n  options: ComposeClientOptions,\n): ComposeClient => {\n  if (!options.baseUrl || !/^https?:\\/\\//i.test(options.baseUrl)) {\n    throw new ComposeError(\n      'VALIDATION_ERROR',\n      `Invalid baseUrl: expected an HTTP(S) URL, got \"${options.baseUrl}\"`,\n    );\n  }\n  const fetchFn = options.fetch ?? globalThis.fetch;\n  const base = options.baseUrl.replace(/\\/$/, '');\n\n  const trimmedApiKey = options.apiKey?.trim() || undefined;\n\n  const baseHeaders: Record<string, string> = {\n    Accept: 'application/json',\n    'x-lifi-composer-sdk': SDK_VERSION,\n    ...(trimmedApiKey ? { 'x-lifi-api-key': trimmedApiKey } : {}),\n  };\n\n  const getManifest = async (): Promise<ComposeManifest> => {\n    const url = `${base}/compose/manifest`;\n    let res: Response;\n    try {\n      res = await fetchFn(url, {\n        method: 'GET',\n        headers: { ...baseHeaders },\n      });\n    } catch (err) {\n      const message = err instanceof Error ? err.message : String(err);\n      throw new ComposeError('NETWORK_ERROR', message, { cause: err });\n    }\n    if (!res.ok) {\n      const body = await res.text();\n      throw errorFromHttpResponse(res.status, body, url);\n    }\n    return await parseBody<ComposeManifest>(res, url);\n  };\n\n  const compile = async (\n    request: ComposeCompileRequest,\n  ): Promise<ComposeCompileResult> => {\n    const url = `${base}/compose`;\n    let res: Response;\n    try {\n      res = await fetchFn(url, {\n        method: 'POST',\n        headers: { ...baseHeaders, 'Content-Type': 'application/json' },\n        body: JSON.stringify(request, bigintReplacer),\n      });\n    } catch (err) {\n      const message = err instanceof Error ? err.message : String(err);\n      throw new ComposeError('NETWORK_ERROR', message, { cause: err });\n    }\n    if (res.status === 206) {\n      return await parsePartialBody(res, url);\n    }\n    if (!res.ok) {\n      const body = await res.text();\n      throw errorFromHttpResponse(res.status, body, url);\n    }\n    return await parseCompileSuccessBody(res, url);\n  };\n\n  const getZapPacks = async (\n    options?: GetZapPacksOptions,\n  ): Promise<readonly ZapPackOverview[]> => {\n    const params = new URLSearchParams();\n    if (options?.protocols !== undefined) {\n      // Backend expects a single comma-separated value, not repeated keys.\n      const raw = options.protocols;\n      const list = typeof raw === 'string' ? raw : raw.join(',');\n      params.set('protocols', list);\n    }\n    const qs = params.toString();\n    const url = `${base}/compose/zap-packs${qs ? `?${qs}` : ''}`;\n    let res: Response;\n    try {\n      res = await fetchFn(url, {\n        method: 'GET',\n        headers: { ...baseHeaders },\n      });\n    } catch (err) {\n      const message = err instanceof Error ? err.message : String(err);\n      throw new ComposeError('NETWORK_ERROR', message, { cause: err });\n    }\n    if (!res.ok) {\n      const body = await res.text();\n      throw errorFromHttpResponse(res.status, body, url);\n    }\n    return await parseBody<readonly ZapPackOverview[]>(res, url);\n  };\n\n  return { getManifest, compile, getZapPacks };\n};\n"],"mappings":"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","names":["options"]}