UNPKG

@lifi/composer-sdk

Version:

Public Composer SDK for building and submitting flows

248 lines 9.29 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var client_exports = {}; __export(client_exports, { bigintReplacer: () => bigintReplacer, createComposeClient: () => createComposeClient }); module.exports = __toCommonJS(client_exports); var import_compose_spec = require("@lifi/compose-spec"); var import_errors = require("./errors.js"); var import_responseSchemas = require("./responseSchemas.js"); const SDK_VERSION = true ? "0.6.0" : "dev"; const SDK_VERSION_TRIPLE = (0, import_compose_spec.parseVersion)(SDK_VERSION); const bigintReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value; const isNonNullObject = (v) => typeof v === "object" && v !== null; const INTEGER_STRING = /^\d+$/; const decodeAmount = (value, path, url) => { if (typeof value !== "string" || !INTEGER_STRING.test(value)) { throw new import_errors.ComposeError( "UNKNOWN_ERROR", `Malformed compose response: ${path} is ${value === void 0 ? "absent" : "not an integer string"}`, { url } ); } return BigInt(value); }; const malformed = (path, what, url) => new import_errors.ComposeError( "UNKNOWN_ERROR", `Malformed compose response: ${path} ${what}`, { url } ); const decodeAmountLeaf = (amount, field, path, url) => { const leaf = amount[field]; if (leaf === void 0) return; if (!isNonNullObject(leaf)) { throw malformed(`${path}.${field}`, "is not an object", url); } leaf.value = decodeAmount(leaf.value, `${path}.${field}.value`, url); }; const decodeOutputs = (data, url) => { const outputs = data.outputs; if (!isNonNullObject(outputs)) { throw malformed( "outputs", outputs === void 0 ? "is absent" : "is not an object", url ); } for (const key of Object.keys(outputs)) { const entry = outputs[key]; if (!isNonNullObject(entry)) { throw malformed(`outputs.${key}`, "is not an object", url); } const amount = entry.amount; if (!isNonNullObject(amount)) { throw malformed( `outputs.${key}.amount`, amount === void 0 ? "is absent" : "is not an object", url ); } decodeAmountLeaf(amount, "estimate", `outputs.${key}.amount`, url); decodeAmountLeaf(amount, "minimum", `outputs.${key}.amount`, url); } }; const decodeWireAmounts = (data, url) => { if (!isNonNullObject(data)) return; decodeOutputs(data, url); }; const parseBody = async (res, url) => { const body = await res.json().catch((_) => null); if (!isNonNullObject(body) || !("data" in body)) { throw new import_errors.ComposeError("UNKNOWN_ERROR", "Unexpected response format", { url }); } return body.data; }; const parseCompileSuccessBody = async (res, url) => { const data = await parseBody(res, url); decodeWireAmounts(data, url); return { ...data, status: "success" }; }; const parsePartialBody = async (res, url) => { const body = await res.json().catch((_) => null); const envelope = (0, import_responseSchemas.parseCompilePartialEnvelope)(body); if (envelope === null) { throw new import_errors.ComposeError( "UNKNOWN_ERROR", "Unexpected partial response format", { url } ); } const data = envelope.data; decodeWireAmounts(data, url); return { ...data, status: "partial", error: envelope.error }; }; const parseSimulateBody = async (res, url) => { const body = await res.json().catch((_) => null); const result = (0, import_responseSchemas.parseSimulateResult)(body); if (result === null) { throw new import_errors.ComposeError( "UNKNOWN_ERROR", "Unexpected simulate response format", { url } ); } return result; }; const assertServerVersion = (res, url) => { const serverVersion = res.headers.get(import_compose_spec.COMPOSE_VERSION_HEADER); if (serverVersion === null || SDK_VERSION_TRIPLE === void 0) return; const serverTriple = (0, import_compose_spec.parseVersion)(serverVersion); if (serverTriple === void 0) return; const compatibility = (0, import_compose_spec.checkCompatibility)(SDK_VERSION_TRIPLE, serverTriple); if (compatibility === "compatible") return; const versions = { sdkVersion: SDK_VERSION, serverVersion }; if (compatibility === "sdk_outdated") { throw new import_errors.ComposeError( "VALIDATION_ERROR", `composer-sdk ${SDK_VERSION} is older than the compose contract served at ${url} (${serverVersion}); upgrade @lifi/composer-sdk and @lifi/compose-spec`, { status: res.status, url, kind: "sdk_outdated", sdkOutdated: { ...versions, minimumSdkVersion: `${serverTriple.major}.${serverTriple.minor}.0` } } ); } throw new import_errors.ComposeError( "VALIDATION_ERROR", `composer-sdk ${SDK_VERSION} targets a newer compose contract than the server at ${url} serves (${serverVersion}); pin @lifi/composer-sdk and @lifi/compose-spec to ${serverVersion} or wait for the server rollout`, { status: res.status, url, kind: "server_outdated", serverOutdated: versions } ); }; const requestInit = (baseHeaders, init) => init.method === "GET" ? { method: "GET", headers: { ...baseHeaders } } : { method: "POST", headers: { ...baseHeaders, "Content-Type": "application/json" }, body: JSON.stringify(init.body, bigintReplacer) }; const send = async ({ fetchFn, baseHeaders }, url, init) => { try { return await fetchFn(url, requestInit(baseHeaders, init)); } catch (err) { const message = err instanceof Error ? err.message : String(err); throw new import_errors.ComposeError("NETWORK_ERROR", message, { cause: err }); } }; const postCompile = async (transport, url, request) => { const res = await send(transport, url, { method: "POST", body: request }); assertServerVersion(res, url); if (res.status === 206) { return await parsePartialBody(res, url); } if (!res.ok) { const body = await res.text(); throw (0, import_errors.errorFromHttpResponse)(res.status, body, url); } return await parseCompileSuccessBody(res, url); }; const getJson = async (transport, url) => { const res = await send(transport, url, { method: "GET" }); if (!res.ok) { const body = await res.text(); throw (0, import_errors.errorFromHttpResponse)(res.status, body, url); } return await parseBody(res, url); }; const createComposeClient = (options) => { if (!options.baseUrl || !/^https?:\/\//i.test(options.baseUrl)) { throw new import_errors.ComposeError( "VALIDATION_ERROR", `Invalid baseUrl: expected an HTTP(S) URL, got "${options.baseUrl}"` ); } const trimmedApiKey = options.apiKey?.trim() || void 0; if (!trimmedApiKey) { throw new import_errors.ComposeError( "VALIDATION_ERROR", "apiKey is required: pass a LI.FI API key to createComposeSdk()." ); } const fetchFn = options.fetch ?? globalThis.fetch; const base = options.baseUrl.replace(/\/$/, ""); const baseHeaders = { Accept: "application/json", [import_compose_spec.COMPOSER_SDK_VERSION_HEADER]: SDK_VERSION, "x-lifi-api-key": trimmedApiKey }; const transport = { fetchFn, baseHeaders }; const getManifest = async () => getJson(transport, `${base}/compose/manifest`); const getChains = async () => getJson(transport, `${base}/chains`); const compile = async (request) => postCompile(transport, `${base}/compose`, request); const route = async (request) => postCompile(transport, `${base}/compose/route`, request); const getZapPacks = async (options2) => { const params = new URLSearchParams(); if (options2?.protocols !== void 0) { const raw = options2.protocols; const list = typeof raw === "string" ? raw : raw.join(","); params.set("protocols", list); } const qs = params.toString(); return getJson( transport, `${base}/compose/zap-packs${qs ? `?${qs}` : ""}` ); }; const simulate = async (request) => { const url = `${base}/simulate`; const res = await send(transport, url, { method: "POST", body: request }); if (res.status === 200 || res.status === 422) { return await parseSimulateBody(res, url); } const body = await res.text(); throw (0, import_errors.errorFromHttpResponse)(res.status, body, url); }; return { getManifest, getChains, compile, route, getZapPacks, simulate }; }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { bigintReplacer, createComposeClient }); //# sourceMappingURL=client.cjs.map