UNPKG

@lifi/composer-sdk

Version:

Public Composer SDK for building and submitting flows

1 lines 11.5 kB
{"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"]}