@lifi/composer-sdk 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -298,6 +298,27 @@ try {
298
298
  }
299
299
  ```
300
300
 
301
+ A `preparation_error` (HTTP 422) means some prepared ops failed while others succeeded — a mixed basket with an unroutable leg. Narrow with `isComposePreparationError` to read which ops failed, then drop those `callId`s from the flow and resubmit:
302
+
303
+ ```ts
304
+ import { isComposePreparationError } from '@lifi/composer-sdk';
305
+
306
+ try {
307
+ const result = await sdk.client.compile(request);
308
+ } catch (err) {
309
+ if (isComposePreparationError(err)) {
310
+ for (const op of err.failedOps) {
311
+ console.error(op.callId, op.op, op.kind, op.message);
312
+ }
313
+ // Prepared ops that succeeded. Both arrays list prepared ops only, so
314
+ // retry by removing the `failedOps` callIds from the original flow.
315
+ console.error('prepared ok:', err.succeededOps);
316
+ }
317
+ }
318
+ ```
319
+
320
+ See [`docs/sdk-interface.md`](docs/sdk-interface.md#per-op-preparation-failures) for the full error surface.
321
+
301
322
  ## Examples
302
323
 
303
324
  The `src/examples/` directory contains complete working examples:
package/dist/client.cjs CHANGED
@@ -23,7 +23,7 @@ __export(client_exports, {
23
23
  module.exports = __toCommonJS(client_exports);
24
24
  var import_errors = require("./errors.js");
25
25
  var import_responseSchemas = require("./responseSchemas.js");
26
- const SDK_VERSION = true ? "0.3.0" : "dev";
26
+ const SDK_VERSION = true ? "0.4.0" : "dev";
27
27
  const bigintReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value;
28
28
  const isNonNullObject = (v) => typeof v === "object" && v !== null;
29
29
  const parseBody = async (res, url) => {
package/dist/client.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  parseCompilePartialEnvelope,
4
4
  parseSimulateResult
5
5
  } from "./responseSchemas.js";
6
- const SDK_VERSION = true ? "0.3.0" : "dev";
6
+ const SDK_VERSION = true ? "0.4.0" : "dev";
7
7
  const bigintReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value;
8
8
  const isNonNullObject = (v) => typeof v === "object" && v !== null;
9
9
  const parseBody = async (res, url) => {
package/dist/errors.cjs CHANGED
@@ -20,7 +20,8 @@ var errors_exports = {};
20
20
  __export(errors_exports, {
21
21
  ComposeError: () => ComposeError,
22
22
  errorFromHttpResponse: () => errorFromHttpResponse,
23
- isComposeError: () => isComposeError
23
+ isComposeError: () => isComposeError,
24
+ isComposePreparationError: () => isComposePreparationError
24
25
  });
25
26
  module.exports = __toCommonJS(errors_exports);
26
27
  var import_responseSchemas = require("./responseSchemas.js");
@@ -42,6 +43,17 @@ class ComposeError extends Error {
42
43
  * backend can parse the revert reason.
43
44
  */
44
45
  details;
46
+ /**
47
+ * The prepared ops that failed, attached to `preparation_error` errors.
48
+ * Each entry carries the `callId` of the failing node so callers can drop
49
+ * the unroutable legs and resubmit a smaller flow.
50
+ */
51
+ failedOps;
52
+ /**
53
+ * The `callId`s of the prepared ops that succeeded, attached to
54
+ * `preparation_error` errors alongside {@link ComposeError.failedOps}.
55
+ */
56
+ succeededOps;
45
57
  constructor(code, message, options) {
46
58
  super(message, { cause: options?.cause });
47
59
  this.code = code;
@@ -50,9 +62,12 @@ class ComposeError extends Error {
50
62
  this.kind = options?.kind;
51
63
  this.path = options?.path;
52
64
  this.details = options?.details;
65
+ this.failedOps = options?.failedOps;
66
+ this.succeededOps = options?.succeededOps;
53
67
  }
54
68
  }
55
69
  const isComposeError = (e) => e instanceof ComposeError || e instanceof Error && e.name === "ComposeError" && "code" in e;
70
+ const isComposePreparationError = (e) => isComposeError(e) && e.kind === "preparation_error" && Array.isArray(e.failedOps) && Array.isArray(e.succeededOps);
56
71
  const STATUS_TO_CODE = /* @__PURE__ */ new Map([
57
72
  [400, "VALIDATION_ERROR"],
58
73
  [401, "UNAUTHENTICATED"],
@@ -81,7 +96,14 @@ const errorFromHttpResponse = (status, body, url) => {
81
96
  url,
82
97
  kind: serverError?.kind,
83
98
  path: serverError?.path,
84
- details: serverError?.details
99
+ details: serverError?.details,
100
+ // The wire schema types `kind` as a bare string; the server only ever
101
+ // reports generic kinds for individual ops.
102
+ failedOps: serverError?.failedOps?.map((op) => ({
103
+ ...op,
104
+ kind: op.kind
105
+ })),
106
+ succeededOps: serverError?.succeededOps
85
107
  }
86
108
  );
87
109
  };
@@ -89,6 +111,7 @@ const errorFromHttpResponse = (status, body, url) => {
89
111
  0 && (module.exports = {
90
112
  ComposeError,
91
113
  errorFromHttpResponse,
92
- isComposeError
114
+ isComposeError,
115
+ isComposePreparationError
93
116
  });
94
117
  //# sourceMappingURL=errors.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts"],"sourcesContent":["import type { ComposeErrorKind, SimulationRevert } 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 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 },\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 }\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\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 },\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,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,EAET,YACE,MACA,SACA,SAQA;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;AAAA,EAC1B;AACF;AAOO,MAAM,iBAAiB,CAAC,MAC7B,aAAa,gBACZ,aAAa,SAAS,EAAE,SAAS,kBAAkB,UAAU;AAEhE,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,IACxB;AAAA,EACF;AACF;","names":[]}
1
+ {"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":[]}
package/dist/errors.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { ComposeErrorKind, SimulationRevert } from '@lifi/compose-spec';
1
+ import { ComposeErrorKind, SimulationRevert, FailedPreparedOp } from '@lifi/compose-spec';
2
2
 
3
3
  /**
4
4
  * Machine-readable error codes returned by the SDK.
@@ -48,6 +48,17 @@ declare class ComposeError extends Error {
48
48
  * backend can parse the revert reason.
49
49
  */
50
50
  readonly details?: SimulationRevert;
51
+ /**
52
+ * The prepared ops that failed, attached to `preparation_error` errors.
53
+ * Each entry carries the `callId` of the failing node so callers can drop
54
+ * the unroutable legs and resubmit a smaller flow.
55
+ */
56
+ readonly failedOps?: readonly FailedPreparedOp[];
57
+ /**
58
+ * The `callId`s of the prepared ops that succeeded, attached to
59
+ * `preparation_error` errors alongside {@link ComposeError.failedOps}.
60
+ */
61
+ readonly succeededOps?: readonly string[];
51
62
  constructor(code: ComposeErrorCode, message: string, options?: {
52
63
  status?: number;
53
64
  url?: string;
@@ -55,14 +66,47 @@ declare class ComposeError extends Error {
55
66
  kind?: ComposeErrorKind;
56
67
  path?: string;
57
68
  details?: SimulationRevert;
69
+ failedOps?: readonly FailedPreparedOp[];
70
+ succeededOps?: readonly string[];
58
71
  });
59
72
  }
73
+ /**
74
+ * The per-op preparation diagnostics carried by `preparation_error` responses,
75
+ * with both arrays known to be present. Produced by narrowing with
76
+ * {@link isComposePreparationError}.
77
+ */
78
+ interface ComposePreparationOps {
79
+ readonly failedOps: readonly FailedPreparedOp[];
80
+ readonly succeededOps: readonly string[];
81
+ }
60
82
  /**
61
83
  * Type guard that narrows an unknown error to {@link ComposeError}.
62
84
  * @param e - The value to check.
63
85
  * @returns `true` if `e` is an instance of `ComposeError`.
64
86
  */
65
87
  declare const isComposeError: (e: unknown) => e is ComposeError;
88
+ /**
89
+ * Type guard for `preparation_error` failures (HTTP 422): a mixed basket where
90
+ * some prepared ops failed and others succeeded. Narrows both
91
+ * {@link ComposeError.failedOps} and {@link ComposeError.succeededOps} to
92
+ * present, so callers can read them without a cast or a truthiness check.
93
+ *
94
+ * @param e - The value to check.
95
+ * @returns `true` if `e` is a `ComposeError` carrying per-op preparation diagnostics.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * try {
100
+ * await builder.compile(run);
101
+ * } catch (e) {
102
+ * if (isComposePreparationError(e)) {
103
+ * console.error('Failed ops:', e.failedOps);
104
+ * console.error('Succeeded ops:', e.succeededOps);
105
+ * }
106
+ * }
107
+ * ```
108
+ */
109
+ declare const isComposePreparationError: (e: unknown) => e is ComposeError & ComposePreparationOps;
66
110
  /**
67
111
  * Constructs a {@link ComposeError} from an HTTP error response, extracting
68
112
  * structured error details from the response body when available.
@@ -74,4 +118,4 @@ declare const isComposeError: (e: unknown) => e is ComposeError;
74
118
  */
75
119
  declare const errorFromHttpResponse: (status: number, body: string, url: string) => ComposeError;
76
120
 
77
- export { ComposeError, type ComposeErrorCode, errorFromHttpResponse, isComposeError };
121
+ export { ComposeError, type ComposeErrorCode, type ComposePreparationOps, errorFromHttpResponse, isComposeError, isComposePreparationError };
package/dist/errors.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ComposeErrorKind, SimulationRevert } from '@lifi/compose-spec';
1
+ import { ComposeErrorKind, SimulationRevert, FailedPreparedOp } from '@lifi/compose-spec';
2
2
 
3
3
  /**
4
4
  * Machine-readable error codes returned by the SDK.
@@ -48,6 +48,17 @@ declare class ComposeError extends Error {
48
48
  * backend can parse the revert reason.
49
49
  */
50
50
  readonly details?: SimulationRevert;
51
+ /**
52
+ * The prepared ops that failed, attached to `preparation_error` errors.
53
+ * Each entry carries the `callId` of the failing node so callers can drop
54
+ * the unroutable legs and resubmit a smaller flow.
55
+ */
56
+ readonly failedOps?: readonly FailedPreparedOp[];
57
+ /**
58
+ * The `callId`s of the prepared ops that succeeded, attached to
59
+ * `preparation_error` errors alongside {@link ComposeError.failedOps}.
60
+ */
61
+ readonly succeededOps?: readonly string[];
51
62
  constructor(code: ComposeErrorCode, message: string, options?: {
52
63
  status?: number;
53
64
  url?: string;
@@ -55,14 +66,47 @@ declare class ComposeError extends Error {
55
66
  kind?: ComposeErrorKind;
56
67
  path?: string;
57
68
  details?: SimulationRevert;
69
+ failedOps?: readonly FailedPreparedOp[];
70
+ succeededOps?: readonly string[];
58
71
  });
59
72
  }
73
+ /**
74
+ * The per-op preparation diagnostics carried by `preparation_error` responses,
75
+ * with both arrays known to be present. Produced by narrowing with
76
+ * {@link isComposePreparationError}.
77
+ */
78
+ interface ComposePreparationOps {
79
+ readonly failedOps: readonly FailedPreparedOp[];
80
+ readonly succeededOps: readonly string[];
81
+ }
60
82
  /**
61
83
  * Type guard that narrows an unknown error to {@link ComposeError}.
62
84
  * @param e - The value to check.
63
85
  * @returns `true` if `e` is an instance of `ComposeError`.
64
86
  */
65
87
  declare const isComposeError: (e: unknown) => e is ComposeError;
88
+ /**
89
+ * Type guard for `preparation_error` failures (HTTP 422): a mixed basket where
90
+ * some prepared ops failed and others succeeded. Narrows both
91
+ * {@link ComposeError.failedOps} and {@link ComposeError.succeededOps} to
92
+ * present, so callers can read them without a cast or a truthiness check.
93
+ *
94
+ * @param e - The value to check.
95
+ * @returns `true` if `e` is a `ComposeError` carrying per-op preparation diagnostics.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * try {
100
+ * await builder.compile(run);
101
+ * } catch (e) {
102
+ * if (isComposePreparationError(e)) {
103
+ * console.error('Failed ops:', e.failedOps);
104
+ * console.error('Succeeded ops:', e.succeededOps);
105
+ * }
106
+ * }
107
+ * ```
108
+ */
109
+ declare const isComposePreparationError: (e: unknown) => e is ComposeError & ComposePreparationOps;
66
110
  /**
67
111
  * Constructs a {@link ComposeError} from an HTTP error response, extracting
68
112
  * structured error details from the response body when available.
@@ -74,4 +118,4 @@ declare const isComposeError: (e: unknown) => e is ComposeError;
74
118
  */
75
119
  declare const errorFromHttpResponse: (status: number, body: string, url: string) => ComposeError;
76
120
 
77
- export { ComposeError, type ComposeErrorCode, errorFromHttpResponse, isComposeError };
121
+ export { ComposeError, type ComposeErrorCode, type ComposePreparationOps, errorFromHttpResponse, isComposeError, isComposePreparationError };
package/dist/errors.js CHANGED
@@ -19,6 +19,17 @@ class ComposeError extends Error {
19
19
  * backend can parse the revert reason.
20
20
  */
21
21
  details;
22
+ /**
23
+ * The prepared ops that failed, attached to `preparation_error` errors.
24
+ * Each entry carries the `callId` of the failing node so callers can drop
25
+ * the unroutable legs and resubmit a smaller flow.
26
+ */
27
+ failedOps;
28
+ /**
29
+ * The `callId`s of the prepared ops that succeeded, attached to
30
+ * `preparation_error` errors alongside {@link ComposeError.failedOps}.
31
+ */
32
+ succeededOps;
22
33
  constructor(code, message, options) {
23
34
  super(message, { cause: options?.cause });
24
35
  this.code = code;
@@ -27,9 +38,12 @@ class ComposeError extends Error {
27
38
  this.kind = options?.kind;
28
39
  this.path = options?.path;
29
40
  this.details = options?.details;
41
+ this.failedOps = options?.failedOps;
42
+ this.succeededOps = options?.succeededOps;
30
43
  }
31
44
  }
32
45
  const isComposeError = (e) => e instanceof ComposeError || e instanceof Error && e.name === "ComposeError" && "code" in e;
46
+ const isComposePreparationError = (e) => isComposeError(e) && e.kind === "preparation_error" && Array.isArray(e.failedOps) && Array.isArray(e.succeededOps);
33
47
  const STATUS_TO_CODE = /* @__PURE__ */ new Map([
34
48
  [400, "VALIDATION_ERROR"],
35
49
  [401, "UNAUTHENTICATED"],
@@ -58,13 +72,21 @@ const errorFromHttpResponse = (status, body, url) => {
58
72
  url,
59
73
  kind: serverError?.kind,
60
74
  path: serverError?.path,
61
- details: serverError?.details
75
+ details: serverError?.details,
76
+ // The wire schema types `kind` as a bare string; the server only ever
77
+ // reports generic kinds for individual ops.
78
+ failedOps: serverError?.failedOps?.map((op) => ({
79
+ ...op,
80
+ kind: op.kind
81
+ })),
82
+ succeededOps: serverError?.succeededOps
62
83
  }
63
84
  );
64
85
  };
65
86
  export {
66
87
  ComposeError,
67
88
  errorFromHttpResponse,
68
- isComposeError
89
+ isComposeError,
90
+ isComposePreparationError
69
91
  };
70
92
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts"],"sourcesContent":["import type { ComposeErrorKind, SimulationRevert } 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 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 },\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 }\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\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 },\n );\n};\n"],"mappings":"AAEA;AAAA,EACE;AAAA,OAEK;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,EAET,YACE,MACA,SACA,SAQA;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;AAAA,EAC1B;AACF;AAOO,MAAM,iBAAiB,CAAC,MAC7B,aAAa,gBACZ,aAAa,SAAS,EAAE,SAAS,kBAAkB,UAAU;AAEhE,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,SAAO,qBAAqB,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,IACxB;AAAA,EACF;AACF;","names":[]}
1
+ {"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":"AAOA;AAAA,EACE;AAAA,OAEK;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,SAAO,qBAAqB,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":[]}
package/dist/index.cjs CHANGED
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  createComposeSdk: () => import_sdk.createComposeSdk,
36
36
  guards: () => guards,
37
37
  isComposeError: () => import_errors.isComposeError,
38
+ isComposePreparationError: () => import_errors.isComposePreparationError,
38
39
  isInputHandle: () => import_handles.isInputHandle,
39
40
  isOutputHandle: () => import_handles.isOutputHandle,
40
41
  materialiser: () => import_inputs.materialiser,
@@ -71,6 +72,7 @@ const raw = rawMod;
71
72
  createComposeSdk,
72
73
  guards,
73
74
  isComposeError,
75
+ isComposePreparationError,
74
76
  isInputHandle,
75
77
  isOutputHandle,
76
78
  materialiser,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@lifi/composer-sdk` — TypeScript SDK for building and compiling\n * Compose flows into executable EVM calldata.\n *\n * Start by creating an SDK instance with {@link createComposeSdk}, then use\n * {@link ComposeSdk.flow} to build flows and compile them.\n *\n * @packageDocumentation\n */\n\nexport type {\n AbiTypeToOutputKind,\n Address,\n InputDecl,\n InputSchema,\n InputSpecOf,\n IntegerString,\n IntegerStringInput,\n OutputKind,\n TypedGuard,\n TypedRef,\n} from './types.js';\n\nexport type {\n GetZapPacksOptions,\n ZapPackEdge,\n ZapPackOverview,\n} from './discovery.js';\nexport type { ComposeError, ComposeErrorCode } from './errors.js';\nexport type {\n AppliedGuard,\n ApprovalEntry,\n Call,\n ComposeCompilePartialData,\n ComposeCompileRequest,\n ComposeCompileResult,\n ComposeCompileSuccessData,\n ComposeContinuation,\n ComposeErrorKind,\n ComposeTransactionRequest,\n Flow,\n InputSpec,\n MaterialiserConfigOf,\n MaterialiserInput,\n Precondition,\n PreconditionConfigOf,\n PriceImpact,\n Ref,\n Resource,\n SimulationPolicy,\n SimulationRevert,\n SolType,\n SweepTo,\n SimulateRequest,\n SimulateResult,\n SimulateOkResult,\n SimulateRevertResult,\n SimulateRevertDecodeResult,\n SimulateSetupErrorResult,\n SimulateBalanceEntry,\n TrackedBalance,\n SlotFinderRequirement,\n Erc20BalanceRequirement,\n NativeBalanceRequirement,\n Erc20AllowanceRequirement,\n ContinuationIntent,\n ContinuationOutcome,\n Check,\n CheckComparator,\n Phase1ArtifactWire,\n ContinuationPlanWire,\n} from '@lifi/compose-spec';\nexport {\n SIMULATE_MAX_TRACKED_BALANCES,\n SIMULATE_MAX_REQUIREMENTS,\n} from '@lifi/compose-spec';\nexport { isComposeError } from './errors.js';\n\nexport type {\n Bindable,\n InputHandle,\n ResourceInputHandle,\n OutputHandle,\n AnyHandle,\n} from './authoring/handles.js';\nexport { isInputHandle, isOutputHandle } from './authoring/handles.js';\nexport { ref } from './authoring/raw.js';\n\nexport type { AnyBindable } from './authoring/FlowBuilderCore.js';\nexport type { TypedFlow, FlowOptions } from './authoring/FlowBuilderCore.js';\n\nexport type { ComposeRunInput } from './run/inputs.js';\nexport { materialiser } from './run/inputs.js';\n\nexport { buildSimulateRequest } from './run/simulate.js';\nexport type { BuildSimulateRequestInput } from './run/simulate.js';\n\nexport { createComposeSdk } from './sdk.js';\nexport type { ComposeSdk, ComposeSdkOptions, FlowBuilder } from './sdk.js';\n\nimport * as guardsMod from './generated/guards.generated.js';\nimport * as materialisersMod from './generated/materialisers.generated.js';\nimport * as preconditionsMod from './generated/preconditions.generated.js';\nimport * as rawMod from './raw.js';\nimport * as resourcesMod from './resources.js';\n\n/**\n * Guard factories for protecting operation outputs (e.g. slippage tolerance).\n * Pass guards to an operation's `guards` array.\n *\n * @example\n * ```ts\n * builder.lifi.swap('swap1', {\n * bind: { amountIn: builder.inputs.token },\n * config: { resourceOut: resources.native(1) },\n * guards: [guards.slippage({ port: 'amountOut', bps: 300 })],\n * });\n * ```\n */\nexport const guards: typeof guardsMod = guardsMod;\n/**\n * Materialiser factories that tell the runtime how to resolve token input\n * amounts on-chain (e.g. read a wallet balance, accept a direct deposit).\n *\n * @example\n * ```ts\n * await builder.compile({\n * inputs: { token: materialisers.balanceOf({}) },\n * signer: '0x...',\n * });\n * ```\n */\nexport const materialisers: typeof materialisersMod = materialisersMod;\n/**\n * Precondition factories for asserting on-chain state before a flow executes\n * (e.g. minimum token balances, ERC-20 allowances).\n *\n * @example\n * ```ts\n * await builder.compile({\n * inputs: { token: materialisers.balanceOf({}) },\n * preconditions: [\n * preconditions.erc20Balance({ wallet: '0x...', token: '0x...', balance: 1000n }),\n * ],\n * signer: '0x...',\n * });\n * ```\n */\nexport const preconditions: typeof preconditionsMod = preconditionsMod;\n/**\n * Resource declaration helpers for defining token inputs in a flow schema.\n * Use {@link resources.erc20} for ERC-20 tokens and {@link resources.native}\n * for the chain's native coin (ETH, MATIC, etc.).\n *\n * @example\n * ```ts\n * const builder = sdk.flow(1, {\n * inputs: {\n * usdc: resources.erc20('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 1),\n * eth: resources.native(1),\n * },\n * });\n * ```\n */\nexport const resources: typeof resourcesMod = resourcesMod;\nexport * from './generated/config.generated.js';\n\n/**\n * Low-level escape hatch for constructing flow nodes, guards, and materialiser\n * inputs without the typed builder API. Prefer the generated operation methods\n * on {@link FlowBuilder} and the typed `guards`/`materialisers` namespaces\n * for most use cases.\n */\nexport const raw: typeof rawMod = rawMod;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwEA,0BAGO;AACP,oBAA+B;AAS/B,qBAA8C;AAC9C,iBAAoB;AAMpB,oBAA6B;AAE7B,sBAAqC;AAGrC,iBAAiC;AAGjC,gBAA2B;AAC3B,uBAAkC;AAClC,uBAAkC;AAClC,aAAwB;AACxB,mBAA8B;AA6D9B,0BAAc,4CArKd;AAuHO,MAAM,SAA2B;AAajC,MAAM,gBAAyC;AAgB/C,MAAM,gBAAyC;AAgB/C,MAAM,YAAiC;AASvC,MAAM,MAAqB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@lifi/composer-sdk` — TypeScript SDK for building and compiling\n * Compose flows into executable EVM calldata.\n *\n * Start by creating an SDK instance with {@link createComposeSdk}, then use\n * {@link ComposeSdk.flow} to build flows and compile them.\n *\n * @packageDocumentation\n */\n\nexport type {\n AbiTypeToOutputKind,\n Address,\n InputDecl,\n InputSchema,\n InputSpecOf,\n IntegerString,\n IntegerStringInput,\n OutputKind,\n TypedGuard,\n TypedRef,\n} from './types.js';\n\nexport type {\n GetZapPacksOptions,\n ZapPackEdge,\n ZapPackOverview,\n} from './discovery.js';\nexport type {\n ComposeError,\n ComposeErrorCode,\n ComposePreparationOps,\n} from './errors.js';\nexport type {\n AppliedGuard,\n ApprovalEntry,\n Call,\n ComposeCompilePartialData,\n ComposeCompileRequest,\n ComposeCompileResult,\n ComposeCompileSuccessData,\n ComposeContinuation,\n ComposeErrorKind,\n ComposeTransactionRequest,\n FailedPreparedOp,\n Flow,\n InputSpec,\n MaterialiserConfigOf,\n MaterialiserInput,\n Precondition,\n PreconditionConfigOf,\n PriceImpact,\n Ref,\n Resource,\n SimulationPolicy,\n SimulationRevert,\n SolType,\n SweepTo,\n SimulateRequest,\n SimulateResult,\n SimulateOkResult,\n SimulateRevertResult,\n SimulateRevertDecodeResult,\n SimulateSetupErrorResult,\n SimulateBalanceEntry,\n TrackedBalance,\n SlotFinderRequirement,\n Erc20BalanceRequirement,\n NativeBalanceRequirement,\n Erc20AllowanceRequirement,\n ContinuationIntent,\n ContinuationOutcome,\n Check,\n CheckComparator,\n Phase1ArtifactWire,\n ContinuationPlanWire,\n} from '@lifi/compose-spec';\nexport {\n SIMULATE_MAX_TRACKED_BALANCES,\n SIMULATE_MAX_REQUIREMENTS,\n} from '@lifi/compose-spec';\nexport { isComposeError, isComposePreparationError } from './errors.js';\n\nexport type {\n Bindable,\n InputHandle,\n ResourceInputHandle,\n OutputHandle,\n AnyHandle,\n} from './authoring/handles.js';\nexport { isInputHandle, isOutputHandle } from './authoring/handles.js';\nexport { ref } from './authoring/raw.js';\n\nexport type { AnyBindable } from './authoring/FlowBuilderCore.js';\nexport type { TypedFlow, FlowOptions } from './authoring/FlowBuilderCore.js';\n\nexport type { ComposeRunInput } from './run/inputs.js';\nexport { materialiser } from './run/inputs.js';\n\nexport { buildSimulateRequest } from './run/simulate.js';\nexport type { BuildSimulateRequestInput } from './run/simulate.js';\n\nexport { createComposeSdk } from './sdk.js';\nexport type { ComposeSdk, ComposeSdkOptions, FlowBuilder } from './sdk.js';\n\nimport * as guardsMod from './generated/guards.generated.js';\nimport * as materialisersMod from './generated/materialisers.generated.js';\nimport * as preconditionsMod from './generated/preconditions.generated.js';\nimport * as rawMod from './raw.js';\nimport * as resourcesMod from './resources.js';\n\n/**\n * Guard factories for protecting operation outputs (e.g. slippage tolerance).\n * Pass guards to an operation's `guards` array.\n *\n * @example\n * ```ts\n * builder.lifi.swap('swap1', {\n * bind: { amountIn: builder.inputs.token },\n * config: { resourceOut: resources.native(1) },\n * guards: [guards.slippage({ port: 'amountOut', bps: 300 })],\n * });\n * ```\n */\nexport const guards: typeof guardsMod = guardsMod;\n/**\n * Materialiser factories that tell the runtime how to resolve token input\n * amounts on-chain (e.g. read a wallet balance, accept a direct deposit).\n *\n * @example\n * ```ts\n * await builder.compile({\n * inputs: { token: materialisers.balanceOf({}) },\n * signer: '0x...',\n * });\n * ```\n */\nexport const materialisers: typeof materialisersMod = materialisersMod;\n/**\n * Precondition factories for asserting on-chain state before a flow executes\n * (e.g. minimum token balances, ERC-20 allowances).\n *\n * @example\n * ```ts\n * await builder.compile({\n * inputs: { token: materialisers.balanceOf({}) },\n * preconditions: [\n * preconditions.erc20Balance({ wallet: '0x...', token: '0x...', balance: 1000n }),\n * ],\n * signer: '0x...',\n * });\n * ```\n */\nexport const preconditions: typeof preconditionsMod = preconditionsMod;\n/**\n * Resource declaration helpers for defining token inputs in a flow schema.\n * Use {@link resources.erc20} for ERC-20 tokens and {@link resources.native}\n * for the chain's native coin (ETH, MATIC, etc.).\n *\n * @example\n * ```ts\n * const builder = sdk.flow(1, {\n * inputs: {\n * usdc: resources.erc20('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 1),\n * eth: resources.native(1),\n * },\n * });\n * ```\n */\nexport const resources: typeof resourcesMod = resourcesMod;\nexport * from './generated/config.generated.js';\n\n/**\n * Low-level escape hatch for constructing flow nodes, guards, and materialiser\n * inputs without the typed builder API. Prefer the generated operation methods\n * on {@link FlowBuilder} and the typed `guards`/`materialisers` namespaces\n * for most use cases.\n */\nexport const raw: typeof rawMod = rawMod;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6EA,0BAGO;AACP,oBAA0D;AAS1D,qBAA8C;AAC9C,iBAAoB;AAMpB,oBAA6B;AAE7B,sBAAqC;AAGrC,iBAAiC;AAGjC,gBAA2B;AAC3B,uBAAkC;AAClC,uBAAkC;AAClC,aAAwB;AACxB,mBAA8B;AA6D9B,0BAAc,4CA1Kd;AA4HO,MAAM,SAA2B;AAajC,MAAM,gBAAyC;AAgB/C,MAAM,gBAAyC;AAgB/C,MAAM,YAAiC;AASvC,MAAM,MAAqB;","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { AbiTypeToOutputKind, Address, InputDecl, InputSchema, InputSpecOf, IntegerString, IntegerStringInput, OutputKind, TypedGuard, TypedRef } from './types.cjs';
2
2
  export { GetZapPacksOptions, ZapPackEdge, ZapPackOverview } from './discovery.cjs';
3
- export { ComposeError, ComposeErrorCode, isComposeError } from './errors.cjs';
4
- export { AppliedGuard, ApprovalEntry, Call, Check, CheckComparator, ComposeCompilePartialData, ComposeCompileRequest, ComposeCompileResult, ComposeCompileSuccessData, ComposeContinuation, ComposeErrorKind, ComposeTransactionRequest, ContinuationIntent, ContinuationOutcome, ContinuationPlanWire, Erc20AllowanceRequirement, Erc20BalanceRequirement, Flow, InputSpec, MaterialiserConfigOf, MaterialiserInput, NativeBalanceRequirement, Phase1ArtifactWire, Precondition, PreconditionConfigOf, PriceImpact, Ref, Resource, SIMULATE_MAX_REQUIREMENTS, SIMULATE_MAX_TRACKED_BALANCES, SimulateBalanceEntry, SimulateOkResult, SimulateRequest, SimulateResult, SimulateRevertDecodeResult, SimulateRevertResult, SimulateSetupErrorResult, SimulationPolicy, SimulationRevert, SlotFinderRequirement, SolType, SweepTo, TrackedBalance } from '@lifi/compose-spec';
3
+ export { ComposeError, ComposeErrorCode, ComposePreparationOps, isComposeError, isComposePreparationError } from './errors.cjs';
4
+ export { AppliedGuard, ApprovalEntry, Call, Check, CheckComparator, ComposeCompilePartialData, ComposeCompileRequest, ComposeCompileResult, ComposeCompileSuccessData, ComposeContinuation, ComposeErrorKind, ComposeTransactionRequest, ContinuationIntent, ContinuationOutcome, ContinuationPlanWire, Erc20AllowanceRequirement, Erc20BalanceRequirement, FailedPreparedOp, Flow, InputSpec, MaterialiserConfigOf, MaterialiserInput, NativeBalanceRequirement, Phase1ArtifactWire, Precondition, PreconditionConfigOf, PriceImpact, Ref, Resource, SIMULATE_MAX_REQUIREMENTS, SIMULATE_MAX_TRACKED_BALANCES, SimulateBalanceEntry, SimulateOkResult, SimulateRequest, SimulateResult, SimulateRevertDecodeResult, SimulateRevertResult, SimulateSetupErrorResult, SimulationPolicy, SimulationRevert, SlotFinderRequirement, SolType, SweepTo, TrackedBalance } from '@lifi/compose-spec';
5
5
  export { AnyHandle, Bindable, InputHandle, OutputHandle, ResourceInputHandle, isInputHandle, isOutputHandle } from './authoring/handles.cjs';
6
6
  export { ref } from './authoring/raw.cjs';
7
7
  export { AnyBindable, FlowOptions, TypedFlow } from './authoring/FlowBuilderCore.cjs';
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { AbiTypeToOutputKind, Address, InputDecl, InputSchema, InputSpecOf, IntegerString, IntegerStringInput, OutputKind, TypedGuard, TypedRef } from './types.js';
2
2
  export { GetZapPacksOptions, ZapPackEdge, ZapPackOverview } from './discovery.js';
3
- export { ComposeError, ComposeErrorCode, isComposeError } from './errors.js';
4
- export { AppliedGuard, ApprovalEntry, Call, Check, CheckComparator, ComposeCompilePartialData, ComposeCompileRequest, ComposeCompileResult, ComposeCompileSuccessData, ComposeContinuation, ComposeErrorKind, ComposeTransactionRequest, ContinuationIntent, ContinuationOutcome, ContinuationPlanWire, Erc20AllowanceRequirement, Erc20BalanceRequirement, Flow, InputSpec, MaterialiserConfigOf, MaterialiserInput, NativeBalanceRequirement, Phase1ArtifactWire, Precondition, PreconditionConfigOf, PriceImpact, Ref, Resource, SIMULATE_MAX_REQUIREMENTS, SIMULATE_MAX_TRACKED_BALANCES, SimulateBalanceEntry, SimulateOkResult, SimulateRequest, SimulateResult, SimulateRevertDecodeResult, SimulateRevertResult, SimulateSetupErrorResult, SimulationPolicy, SimulationRevert, SlotFinderRequirement, SolType, SweepTo, TrackedBalance } from '@lifi/compose-spec';
3
+ export { ComposeError, ComposeErrorCode, ComposePreparationOps, isComposeError, isComposePreparationError } from './errors.js';
4
+ export { AppliedGuard, ApprovalEntry, Call, Check, CheckComparator, ComposeCompilePartialData, ComposeCompileRequest, ComposeCompileResult, ComposeCompileSuccessData, ComposeContinuation, ComposeErrorKind, ComposeTransactionRequest, ContinuationIntent, ContinuationOutcome, ContinuationPlanWire, Erc20AllowanceRequirement, Erc20BalanceRequirement, FailedPreparedOp, Flow, InputSpec, MaterialiserConfigOf, MaterialiserInput, NativeBalanceRequirement, Phase1ArtifactWire, Precondition, PreconditionConfigOf, PriceImpact, Ref, Resource, SIMULATE_MAX_REQUIREMENTS, SIMULATE_MAX_TRACKED_BALANCES, SimulateBalanceEntry, SimulateOkResult, SimulateRequest, SimulateResult, SimulateRevertDecodeResult, SimulateRevertResult, SimulateSetupErrorResult, SimulationPolicy, SimulationRevert, SlotFinderRequirement, SolType, SweepTo, TrackedBalance } from '@lifi/compose-spec';
5
5
  export { AnyHandle, Bindable, InputHandle, OutputHandle, ResourceInputHandle, isInputHandle, isOutputHandle } from './authoring/handles.js';
6
6
  export { ref } from './authoring/raw.js';
7
7
  export { AnyBindable, FlowOptions, TypedFlow } from './authoring/FlowBuilderCore.js';
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  SIMULATE_MAX_TRACKED_BALANCES,
3
3
  SIMULATE_MAX_REQUIREMENTS
4
4
  } from "@lifi/compose-spec";
5
- import { isComposeError } from "./errors.js";
5
+ import { isComposeError, isComposePreparationError } from "./errors.js";
6
6
  import { isInputHandle, isOutputHandle } from "./authoring/handles.js";
7
7
  import { ref } from "./authoring/raw.js";
8
8
  import { materialiser } from "./run/inputs.js";
@@ -26,6 +26,7 @@ export {
26
26
  createComposeSdk,
27
27
  guards,
28
28
  isComposeError,
29
+ isComposePreparationError,
29
30
  isInputHandle,
30
31
  isOutputHandle,
31
32
  materialiser,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@lifi/composer-sdk` — TypeScript SDK for building and compiling\n * Compose flows into executable EVM calldata.\n *\n * Start by creating an SDK instance with {@link createComposeSdk}, then use\n * {@link ComposeSdk.flow} to build flows and compile them.\n *\n * @packageDocumentation\n */\n\nexport type {\n AbiTypeToOutputKind,\n Address,\n InputDecl,\n InputSchema,\n InputSpecOf,\n IntegerString,\n IntegerStringInput,\n OutputKind,\n TypedGuard,\n TypedRef,\n} from './types.js';\n\nexport type {\n GetZapPacksOptions,\n ZapPackEdge,\n ZapPackOverview,\n} from './discovery.js';\nexport type { ComposeError, ComposeErrorCode } from './errors.js';\nexport type {\n AppliedGuard,\n ApprovalEntry,\n Call,\n ComposeCompilePartialData,\n ComposeCompileRequest,\n ComposeCompileResult,\n ComposeCompileSuccessData,\n ComposeContinuation,\n ComposeErrorKind,\n ComposeTransactionRequest,\n Flow,\n InputSpec,\n MaterialiserConfigOf,\n MaterialiserInput,\n Precondition,\n PreconditionConfigOf,\n PriceImpact,\n Ref,\n Resource,\n SimulationPolicy,\n SimulationRevert,\n SolType,\n SweepTo,\n SimulateRequest,\n SimulateResult,\n SimulateOkResult,\n SimulateRevertResult,\n SimulateRevertDecodeResult,\n SimulateSetupErrorResult,\n SimulateBalanceEntry,\n TrackedBalance,\n SlotFinderRequirement,\n Erc20BalanceRequirement,\n NativeBalanceRequirement,\n Erc20AllowanceRequirement,\n ContinuationIntent,\n ContinuationOutcome,\n Check,\n CheckComparator,\n Phase1ArtifactWire,\n ContinuationPlanWire,\n} from '@lifi/compose-spec';\nexport {\n SIMULATE_MAX_TRACKED_BALANCES,\n SIMULATE_MAX_REQUIREMENTS,\n} from '@lifi/compose-spec';\nexport { isComposeError } from './errors.js';\n\nexport type {\n Bindable,\n InputHandle,\n ResourceInputHandle,\n OutputHandle,\n AnyHandle,\n} from './authoring/handles.js';\nexport { isInputHandle, isOutputHandle } from './authoring/handles.js';\nexport { ref } from './authoring/raw.js';\n\nexport type { AnyBindable } from './authoring/FlowBuilderCore.js';\nexport type { TypedFlow, FlowOptions } from './authoring/FlowBuilderCore.js';\n\nexport type { ComposeRunInput } from './run/inputs.js';\nexport { materialiser } from './run/inputs.js';\n\nexport { buildSimulateRequest } from './run/simulate.js';\nexport type { BuildSimulateRequestInput } from './run/simulate.js';\n\nexport { createComposeSdk } from './sdk.js';\nexport type { ComposeSdk, ComposeSdkOptions, FlowBuilder } from './sdk.js';\n\nimport * as guardsMod from './generated/guards.generated.js';\nimport * as materialisersMod from './generated/materialisers.generated.js';\nimport * as preconditionsMod from './generated/preconditions.generated.js';\nimport * as rawMod from './raw.js';\nimport * as resourcesMod from './resources.js';\n\n/**\n * Guard factories for protecting operation outputs (e.g. slippage tolerance).\n * Pass guards to an operation's `guards` array.\n *\n * @example\n * ```ts\n * builder.lifi.swap('swap1', {\n * bind: { amountIn: builder.inputs.token },\n * config: { resourceOut: resources.native(1) },\n * guards: [guards.slippage({ port: 'amountOut', bps: 300 })],\n * });\n * ```\n */\nexport const guards: typeof guardsMod = guardsMod;\n/**\n * Materialiser factories that tell the runtime how to resolve token input\n * amounts on-chain (e.g. read a wallet balance, accept a direct deposit).\n *\n * @example\n * ```ts\n * await builder.compile({\n * inputs: { token: materialisers.balanceOf({}) },\n * signer: '0x...',\n * });\n * ```\n */\nexport const materialisers: typeof materialisersMod = materialisersMod;\n/**\n * Precondition factories for asserting on-chain state before a flow executes\n * (e.g. minimum token balances, ERC-20 allowances).\n *\n * @example\n * ```ts\n * await builder.compile({\n * inputs: { token: materialisers.balanceOf({}) },\n * preconditions: [\n * preconditions.erc20Balance({ wallet: '0x...', token: '0x...', balance: 1000n }),\n * ],\n * signer: '0x...',\n * });\n * ```\n */\nexport const preconditions: typeof preconditionsMod = preconditionsMod;\n/**\n * Resource declaration helpers for defining token inputs in a flow schema.\n * Use {@link resources.erc20} for ERC-20 tokens and {@link resources.native}\n * for the chain's native coin (ETH, MATIC, etc.).\n *\n * @example\n * ```ts\n * const builder = sdk.flow(1, {\n * inputs: {\n * usdc: resources.erc20('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 1),\n * eth: resources.native(1),\n * },\n * });\n * ```\n */\nexport const resources: typeof resourcesMod = resourcesMod;\nexport * from './generated/config.generated.js';\n\n/**\n * Low-level escape hatch for constructing flow nodes, guards, and materialiser\n * inputs without the typed builder API. Prefer the generated operation methods\n * on {@link FlowBuilder} and the typed `guards`/`materialisers` namespaces\n * for most use cases.\n */\nexport const raw: typeof rawMod = rawMod;\n"],"mappings":"AAwEA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,sBAAsB;AAS/B,SAAS,eAAe,sBAAsB;AAC9C,SAAS,WAAW;AAMpB,SAAS,oBAAoB;AAE7B,SAAS,4BAA4B;AAGrC,SAAS,wBAAwB;AAGjC,YAAY,eAAe;AAC3B,YAAY,sBAAsB;AAClC,YAAY,sBAAsB;AAClC,YAAY,YAAY;AACxB,YAAY,kBAAkB;AAevB,MAAM,SAA2B;AAajC,MAAM,gBAAyC;AAgB/C,MAAM,gBAAyC;AAgB/C,MAAM,YAAiC;AAC9C,cAAc;AAQP,MAAM,MAAqB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@lifi/composer-sdk` — TypeScript SDK for building and compiling\n * Compose flows into executable EVM calldata.\n *\n * Start by creating an SDK instance with {@link createComposeSdk}, then use\n * {@link ComposeSdk.flow} to build flows and compile them.\n *\n * @packageDocumentation\n */\n\nexport type {\n AbiTypeToOutputKind,\n Address,\n InputDecl,\n InputSchema,\n InputSpecOf,\n IntegerString,\n IntegerStringInput,\n OutputKind,\n TypedGuard,\n TypedRef,\n} from './types.js';\n\nexport type {\n GetZapPacksOptions,\n ZapPackEdge,\n ZapPackOverview,\n} from './discovery.js';\nexport type {\n ComposeError,\n ComposeErrorCode,\n ComposePreparationOps,\n} from './errors.js';\nexport type {\n AppliedGuard,\n ApprovalEntry,\n Call,\n ComposeCompilePartialData,\n ComposeCompileRequest,\n ComposeCompileResult,\n ComposeCompileSuccessData,\n ComposeContinuation,\n ComposeErrorKind,\n ComposeTransactionRequest,\n FailedPreparedOp,\n Flow,\n InputSpec,\n MaterialiserConfigOf,\n MaterialiserInput,\n Precondition,\n PreconditionConfigOf,\n PriceImpact,\n Ref,\n Resource,\n SimulationPolicy,\n SimulationRevert,\n SolType,\n SweepTo,\n SimulateRequest,\n SimulateResult,\n SimulateOkResult,\n SimulateRevertResult,\n SimulateRevertDecodeResult,\n SimulateSetupErrorResult,\n SimulateBalanceEntry,\n TrackedBalance,\n SlotFinderRequirement,\n Erc20BalanceRequirement,\n NativeBalanceRequirement,\n Erc20AllowanceRequirement,\n ContinuationIntent,\n ContinuationOutcome,\n Check,\n CheckComparator,\n Phase1ArtifactWire,\n ContinuationPlanWire,\n} from '@lifi/compose-spec';\nexport {\n SIMULATE_MAX_TRACKED_BALANCES,\n SIMULATE_MAX_REQUIREMENTS,\n} from '@lifi/compose-spec';\nexport { isComposeError, isComposePreparationError } from './errors.js';\n\nexport type {\n Bindable,\n InputHandle,\n ResourceInputHandle,\n OutputHandle,\n AnyHandle,\n} from './authoring/handles.js';\nexport { isInputHandle, isOutputHandle } from './authoring/handles.js';\nexport { ref } from './authoring/raw.js';\n\nexport type { AnyBindable } from './authoring/FlowBuilderCore.js';\nexport type { TypedFlow, FlowOptions } from './authoring/FlowBuilderCore.js';\n\nexport type { ComposeRunInput } from './run/inputs.js';\nexport { materialiser } from './run/inputs.js';\n\nexport { buildSimulateRequest } from './run/simulate.js';\nexport type { BuildSimulateRequestInput } from './run/simulate.js';\n\nexport { createComposeSdk } from './sdk.js';\nexport type { ComposeSdk, ComposeSdkOptions, FlowBuilder } from './sdk.js';\n\nimport * as guardsMod from './generated/guards.generated.js';\nimport * as materialisersMod from './generated/materialisers.generated.js';\nimport * as preconditionsMod from './generated/preconditions.generated.js';\nimport * as rawMod from './raw.js';\nimport * as resourcesMod from './resources.js';\n\n/**\n * Guard factories for protecting operation outputs (e.g. slippage tolerance).\n * Pass guards to an operation's `guards` array.\n *\n * @example\n * ```ts\n * builder.lifi.swap('swap1', {\n * bind: { amountIn: builder.inputs.token },\n * config: { resourceOut: resources.native(1) },\n * guards: [guards.slippage({ port: 'amountOut', bps: 300 })],\n * });\n * ```\n */\nexport const guards: typeof guardsMod = guardsMod;\n/**\n * Materialiser factories that tell the runtime how to resolve token input\n * amounts on-chain (e.g. read a wallet balance, accept a direct deposit).\n *\n * @example\n * ```ts\n * await builder.compile({\n * inputs: { token: materialisers.balanceOf({}) },\n * signer: '0x...',\n * });\n * ```\n */\nexport const materialisers: typeof materialisersMod = materialisersMod;\n/**\n * Precondition factories for asserting on-chain state before a flow executes\n * (e.g. minimum token balances, ERC-20 allowances).\n *\n * @example\n * ```ts\n * await builder.compile({\n * inputs: { token: materialisers.balanceOf({}) },\n * preconditions: [\n * preconditions.erc20Balance({ wallet: '0x...', token: '0x...', balance: 1000n }),\n * ],\n * signer: '0x...',\n * });\n * ```\n */\nexport const preconditions: typeof preconditionsMod = preconditionsMod;\n/**\n * Resource declaration helpers for defining token inputs in a flow schema.\n * Use {@link resources.erc20} for ERC-20 tokens and {@link resources.native}\n * for the chain's native coin (ETH, MATIC, etc.).\n *\n * @example\n * ```ts\n * const builder = sdk.flow(1, {\n * inputs: {\n * usdc: resources.erc20('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 1),\n * eth: resources.native(1),\n * },\n * });\n * ```\n */\nexport const resources: typeof resourcesMod = resourcesMod;\nexport * from './generated/config.generated.js';\n\n/**\n * Low-level escape hatch for constructing flow nodes, guards, and materialiser\n * inputs without the typed builder API. Prefer the generated operation methods\n * on {@link FlowBuilder} and the typed `guards`/`materialisers` namespaces\n * for most use cases.\n */\nexport const raw: typeof rawMod = rawMod;\n"],"mappings":"AA6EA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,gBAAgB,iCAAiC;AAS1D,SAAS,eAAe,sBAAsB;AAC9C,SAAS,WAAW;AAMpB,SAAS,oBAAoB;AAE7B,SAAS,4BAA4B;AAGrC,SAAS,wBAAwB;AAGjC,YAAY,eAAe;AAC3B,YAAY,sBAAsB;AAClC,YAAY,sBAAsB;AAClC,YAAY,YAAY;AACxB,YAAY,kBAAkB;AAevB,MAAM,SAA2B;AAajC,MAAM,gBAAyC;AAgB/C,MAAM,gBAAyC;AAgB/C,MAAM,YAAiC;AAC9C,cAAc;AAQP,MAAM,MAAqB;","names":[]}
@@ -89,12 +89,21 @@ const parseCompilePartialEnvelope = (body) => {
89
89
  const parsed = compilePartialEnvelopeSchema.safeParse(body);
90
90
  return parsed.success ? parsed.data : null;
91
91
  };
92
+ const failedPreparedOpSchema = import_zod.default.object({
93
+ callId: import_zod.default.string(),
94
+ op: import_zod.default.string(),
95
+ kind: import_zod.default.string(),
96
+ message: import_zod.default.string(),
97
+ path: import_zod.default.string().optional()
98
+ });
92
99
  const serverErrorBodySchema = import_zod.default.object({
93
100
  error: import_zod.default.object({
94
101
  kind: import_zod.default.string().optional(),
95
102
  message: import_zod.default.string().optional(),
96
103
  path: import_zod.default.string().optional(),
97
- details: import_zod.default.custom().optional()
104
+ details: import_zod.default.custom().optional(),
105
+ failedOps: import_zod.default.array(failedPreparedOpSchema).readonly().optional(),
106
+ succeededOps: import_zod.default.array(import_zod.default.string()).readonly().optional()
98
107
  }).optional()
99
108
  });
100
109
  const parseServerErrorBody = (json) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/responseSchemas.ts"],"sourcesContent":["import type { SimulateResult, SimulationRevert } from '@lifi/compose-spec';\nimport z from 'zod';\n\n/**\n * Zod-backed parsers for Compose API response bodies.\n *\n * Server responses are untrusted input crossing into the SDK, so the shapes the\n * client reads back are validated here rather than asserted with `as` casts. The\n * schemas are module-private (the package builds with `--isolatedDeclarations`,\n * which forbids exporting un-annotated complex values); the public surface is\n * the typed `parse*` helpers, whose explicit return types double as the contract.\n *\n * The simulate union is the one response the SDK fully owns and parses itself\n * (it is un-enveloped, unlike `/compose`), so it gets a complete schema kept in\n * lockstep with the canonical `SimulateResult` in `@lifi/compose-spec` by the\n * compile-time assertion below — the same anchoring `@lifi/api-schemas` uses for\n * its server-side simulate schema, so the two cannot silently drift.\n */\n\nconst balanceEntrySchema = z.object({\n token: z.string(),\n owner: z.string(),\n amount: z.string(),\n});\n\nconst simulateOkSchema = z.object({\n status: z.literal('ok'),\n block: z.number(),\n timestamp: z.number(),\n balancesBefore: z.array(balanceEntrySchema),\n balancesAfter: z.array(balanceEntrySchema),\n deltas: z.array(balanceEntrySchema),\n gasUsed: z.string(),\n});\n\nconst simulateRevertDecodeResultSchema = z.object({\n errorCandidates: z\n .array(\n z.object({\n decodedErrorSignature: z.string(),\n decodedParams: z.array(z.string()),\n }),\n )\n .optional(),\n error: z.string().optional(),\n});\n\nconst simulateRevertSchema = z.object({\n status: z.literal('revert'),\n block: z.number(),\n timestamp: z.number(),\n revertReason: z.string().optional(),\n code: z.number().optional(),\n rawErrorBytes: z.string().optional(),\n decodeResult: simulateRevertDecodeResultSchema.optional(),\n});\n\nconst simulateSetupErrorSchema = z.object({\n status: z.literal('error'),\n message: z.string(),\n});\n\nconst simulateResultSchema = z.discriminatedUnion('status', [\n simulateOkSchema,\n simulateRevertSchema,\n simulateSetupErrorSchema,\n]);\n\n// The type the schema actually parses out. Kept private (referencing it from an\n// exported declaration would break `--isolatedDeclarations`) and pinned to the\n// canonical `SimulateResult` inside `parseSimulateResult` below.\ntype SimulateResultWire = z.infer<typeof simulateResultSchema>;\n\n/**\n * Parses a `POST /simulate` body into a {@link SimulateResult}. Returns `null`\n * when the body does not match the `ok`/`revert`/`error` union, letting the\n * caller raise a transport-level error.\n */\nexport const parseSimulateResult = (body: unknown): SimulateResult | null => {\n const parsed = simulateResultSchema.safeParse(body);\n if (!parsed.success) return null;\n // Pin the schema to the canonical contract in both directions, so it cannot\n // silently drift (mirroring the conformance assertion `@lifi/api-schemas` runs\n // on its server-side simulate schema). Assigning the inferred wire value to a\n // `SimulateResult` checks wire→spec; `satisfies SimulateResultWire` checks\n // spec→wire. A missing or mistyped field on either side fails to compile.\n const result: SimulateResult = parsed.data;\n return result satisfies SimulateResultWire;\n};\n\n/**\n * The validated members of an HTTP 206 partial compile envelope. `data` is\n * verified to be an object but kept untyped — compose-spec owns its full shape\n * (`ComposeCompilePartialData`) as a hand-authored type, so the caller narrows\n * it. `error` is fully validated.\n */\nexport interface CompilePartialEnvelope {\n readonly data: Record<string, unknown>;\n readonly error: { readonly kind: string; readonly message: string };\n}\n\nconst compilePartialEnvelopeSchema = z.object({\n data: z.record(z.string(), z.unknown()),\n error: z.object({ kind: z.string(), message: z.string() }),\n});\n\n/** Parses an HTTP 206 partial compile envelope; `null` when it does not match. */\nexport const parseCompilePartialEnvelope = (\n body: unknown,\n): CompilePartialEnvelope | null => {\n const parsed = compilePartialEnvelopeSchema.safeParse(body);\n return parsed.success ? parsed.data : null;\n};\n\n/** Error envelope returned on non-2xx responses. */\nexport interface ServerErrorBody {\n readonly error?: {\n readonly kind?: string;\n readonly message?: string;\n readonly path?: string;\n readonly details?: SimulationRevert;\n };\n}\n\n// `details` is passed through (`z.custom`) rather than parsed: the SDK does not\n// own the `SimulationRevert` schema and forwards the field verbatim onto\n// `ComposeError`.\nconst serverErrorBodySchema = z.object({\n error: z\n .object({\n kind: z.string().optional(),\n message: z.string().optional(),\n path: z.string().optional(),\n details: z.custom<SimulationRevert>().optional(),\n })\n .optional(),\n});\n\n/** Validates an already-parsed error envelope; `null` when it does not match. */\nexport const parseServerErrorBody = (json: unknown): ServerErrorBody | null => {\n const parsed = serverErrorBodySchema.safeParse(json);\n return parsed.success ? parsed.data : null;\n};\n"],"mappings":"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","names":["z"]}
1
+ {"version":3,"sources":["../src/responseSchemas.ts"],"sourcesContent":["import type { SimulateResult, SimulationRevert } from '@lifi/compose-spec';\nimport z from 'zod';\n\n/**\n * Zod-backed parsers for Compose API response bodies.\n *\n * Server responses are untrusted input crossing into the SDK, so the shapes the\n * client reads back are validated here rather than asserted with `as` casts. The\n * schemas are module-private (the package builds with `--isolatedDeclarations`,\n * which forbids exporting un-annotated complex values); the public surface is\n * the typed `parse*` helpers, whose explicit return types double as the contract.\n *\n * The simulate union is the one response the SDK fully owns and parses itself\n * (it is un-enveloped, unlike `/compose`), so it gets a complete schema kept in\n * lockstep with the canonical `SimulateResult` in `@lifi/compose-spec` by the\n * compile-time assertion below — the same anchoring `@lifi/api-schemas` uses for\n * its server-side simulate schema, so the two cannot silently drift.\n */\n\nconst balanceEntrySchema = z.object({\n token: z.string(),\n owner: z.string(),\n amount: z.string(),\n});\n\nconst simulateOkSchema = z.object({\n status: z.literal('ok'),\n block: z.number(),\n timestamp: z.number(),\n balancesBefore: z.array(balanceEntrySchema),\n balancesAfter: z.array(balanceEntrySchema),\n deltas: z.array(balanceEntrySchema),\n gasUsed: z.string(),\n});\n\nconst simulateRevertDecodeResultSchema = z.object({\n errorCandidates: z\n .array(\n z.object({\n decodedErrorSignature: z.string(),\n decodedParams: z.array(z.string()),\n }),\n )\n .optional(),\n error: z.string().optional(),\n});\n\nconst simulateRevertSchema = z.object({\n status: z.literal('revert'),\n block: z.number(),\n timestamp: z.number(),\n revertReason: z.string().optional(),\n code: z.number().optional(),\n rawErrorBytes: z.string().optional(),\n decodeResult: simulateRevertDecodeResultSchema.optional(),\n});\n\nconst simulateSetupErrorSchema = z.object({\n status: z.literal('error'),\n message: z.string(),\n});\n\nconst simulateResultSchema = z.discriminatedUnion('status', [\n simulateOkSchema,\n simulateRevertSchema,\n simulateSetupErrorSchema,\n]);\n\n// The type the schema actually parses out. Kept private (referencing it from an\n// exported declaration would break `--isolatedDeclarations`) and pinned to the\n// canonical `SimulateResult` inside `parseSimulateResult` below.\ntype SimulateResultWire = z.infer<typeof simulateResultSchema>;\n\n/**\n * Parses a `POST /simulate` body into a {@link SimulateResult}. Returns `null`\n * when the body does not match the `ok`/`revert`/`error` union, letting the\n * caller raise a transport-level error.\n */\nexport const parseSimulateResult = (body: unknown): SimulateResult | null => {\n const parsed = simulateResultSchema.safeParse(body);\n if (!parsed.success) return null;\n // Pin the schema to the canonical contract in both directions, so it cannot\n // silently drift (mirroring the conformance assertion `@lifi/api-schemas` runs\n // on its server-side simulate schema). Assigning the inferred wire value to a\n // `SimulateResult` checks wire→spec; `satisfies SimulateResultWire` checks\n // spec→wire. A missing or mistyped field on either side fails to compile.\n const result: SimulateResult = parsed.data;\n return result satisfies SimulateResultWire;\n};\n\n/**\n * The validated members of an HTTP 206 partial compile envelope. `data` is\n * verified to be an object but kept untyped — compose-spec owns its full shape\n * (`ComposeCompilePartialData`) as a hand-authored type, so the caller narrows\n * it. `error` is fully validated.\n */\nexport interface CompilePartialEnvelope {\n readonly data: Record<string, unknown>;\n readonly error: { readonly kind: string; readonly message: string };\n}\n\nconst compilePartialEnvelopeSchema = z.object({\n data: z.record(z.string(), z.unknown()),\n error: z.object({ kind: z.string(), message: z.string() }),\n});\n\n/** Parses an HTTP 206 partial compile envelope; `null` when it does not match. */\nexport const parseCompilePartialEnvelope = (\n body: unknown,\n): CompilePartialEnvelope | null => {\n const parsed = compilePartialEnvelopeSchema.safeParse(body);\n return parsed.success ? parsed.data : null;\n};\n\n/**\n * A single prepared-op failure inside a `preparation_error` envelope. `kind` is\n * kept as `string` here — the narrowing to `GenericComposeErrorKind` happens in\n * `errorFromHttpResponse`, where the value is lifted onto `ComposeError`.\n */\nexport interface ServerFailedPreparedOp {\n readonly callId: string;\n readonly op: string;\n readonly kind: string;\n readonly message: string;\n readonly path?: string;\n}\n\n/** Error envelope returned on non-2xx responses. */\nexport interface ServerErrorBody {\n readonly error?: {\n readonly kind?: string;\n readonly message?: string;\n readonly path?: string;\n readonly details?: SimulationRevert;\n readonly failedOps?: readonly ServerFailedPreparedOp[];\n readonly succeededOps?: readonly string[];\n };\n}\n\nconst failedPreparedOpSchema = z.object({\n callId: z.string(),\n op: z.string(),\n kind: z.string(),\n message: z.string(),\n path: z.string().optional(),\n});\n\n// `details` is passed through (`z.custom`) rather than parsed: the SDK does not\n// own the `SimulationRevert` schema and forwards the field verbatim onto\n// `ComposeError`.\n//\n// `failedOps`/`succeededOps` are optional here because every error kind shares\n// this envelope; they are only populated for `preparation_error` (422). Zod\n// strips undeclared keys, so omitting them would silently drop them.\nconst serverErrorBodySchema = z.object({\n error: z\n .object({\n kind: z.string().optional(),\n message: z.string().optional(),\n path: z.string().optional(),\n details: z.custom<SimulationRevert>().optional(),\n failedOps: z.array(failedPreparedOpSchema).readonly().optional(),\n succeededOps: z.array(z.string()).readonly().optional(),\n })\n .optional(),\n});\n\n/** Validates an already-parsed error envelope; `null` when it does not match. */\nexport const parseServerErrorBody = (json: unknown): ServerErrorBody | null => {\n const parsed = serverErrorBodySchema.safeParse(json);\n return parsed.success ? parsed.data : null;\n};\n"],"mappings":"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","names":["z"]}
@@ -21,6 +21,18 @@ interface CompilePartialEnvelope {
21
21
  }
22
22
  /** Parses an HTTP 206 partial compile envelope; `null` when it does not match. */
23
23
  declare const parseCompilePartialEnvelope: (body: unknown) => CompilePartialEnvelope | null;
24
+ /**
25
+ * A single prepared-op failure inside a `preparation_error` envelope. `kind` is
26
+ * kept as `string` here — the narrowing to `GenericComposeErrorKind` happens in
27
+ * `errorFromHttpResponse`, where the value is lifted onto `ComposeError`.
28
+ */
29
+ interface ServerFailedPreparedOp {
30
+ readonly callId: string;
31
+ readonly op: string;
32
+ readonly kind: string;
33
+ readonly message: string;
34
+ readonly path?: string;
35
+ }
24
36
  /** Error envelope returned on non-2xx responses. */
25
37
  interface ServerErrorBody {
26
38
  readonly error?: {
@@ -28,9 +40,11 @@ interface ServerErrorBody {
28
40
  readonly message?: string;
29
41
  readonly path?: string;
30
42
  readonly details?: SimulationRevert;
43
+ readonly failedOps?: readonly ServerFailedPreparedOp[];
44
+ readonly succeededOps?: readonly string[];
31
45
  };
32
46
  }
33
47
  /** Validates an already-parsed error envelope; `null` when it does not match. */
34
48
  declare const parseServerErrorBody: (json: unknown) => ServerErrorBody | null;
35
49
 
36
- export { type CompilePartialEnvelope, type ServerErrorBody, parseCompilePartialEnvelope, parseServerErrorBody, parseSimulateResult };
50
+ export { type CompilePartialEnvelope, type ServerErrorBody, type ServerFailedPreparedOp, parseCompilePartialEnvelope, parseServerErrorBody, parseSimulateResult };
@@ -21,6 +21,18 @@ interface CompilePartialEnvelope {
21
21
  }
22
22
  /** Parses an HTTP 206 partial compile envelope; `null` when it does not match. */
23
23
  declare const parseCompilePartialEnvelope: (body: unknown) => CompilePartialEnvelope | null;
24
+ /**
25
+ * A single prepared-op failure inside a `preparation_error` envelope. `kind` is
26
+ * kept as `string` here — the narrowing to `GenericComposeErrorKind` happens in
27
+ * `errorFromHttpResponse`, where the value is lifted onto `ComposeError`.
28
+ */
29
+ interface ServerFailedPreparedOp {
30
+ readonly callId: string;
31
+ readonly op: string;
32
+ readonly kind: string;
33
+ readonly message: string;
34
+ readonly path?: string;
35
+ }
24
36
  /** Error envelope returned on non-2xx responses. */
25
37
  interface ServerErrorBody {
26
38
  readonly error?: {
@@ -28,9 +40,11 @@ interface ServerErrorBody {
28
40
  readonly message?: string;
29
41
  readonly path?: string;
30
42
  readonly details?: SimulationRevert;
43
+ readonly failedOps?: readonly ServerFailedPreparedOp[];
44
+ readonly succeededOps?: readonly string[];
31
45
  };
32
46
  }
33
47
  /** Validates an already-parsed error envelope; `null` when it does not match. */
34
48
  declare const parseServerErrorBody: (json: unknown) => ServerErrorBody | null;
35
49
 
36
- export { type CompilePartialEnvelope, type ServerErrorBody, parseCompilePartialEnvelope, parseServerErrorBody, parseSimulateResult };
50
+ export { type CompilePartialEnvelope, type ServerErrorBody, type ServerFailedPreparedOp, parseCompilePartialEnvelope, parseServerErrorBody, parseSimulateResult };
@@ -54,12 +54,21 @@ const parseCompilePartialEnvelope = (body) => {
54
54
  const parsed = compilePartialEnvelopeSchema.safeParse(body);
55
55
  return parsed.success ? parsed.data : null;
56
56
  };
57
+ const failedPreparedOpSchema = z.object({
58
+ callId: z.string(),
59
+ op: z.string(),
60
+ kind: z.string(),
61
+ message: z.string(),
62
+ path: z.string().optional()
63
+ });
57
64
  const serverErrorBodySchema = z.object({
58
65
  error: z.object({
59
66
  kind: z.string().optional(),
60
67
  message: z.string().optional(),
61
68
  path: z.string().optional(),
62
- details: z.custom().optional()
69
+ details: z.custom().optional(),
70
+ failedOps: z.array(failedPreparedOpSchema).readonly().optional(),
71
+ succeededOps: z.array(z.string()).readonly().optional()
63
72
  }).optional()
64
73
  });
65
74
  const parseServerErrorBody = (json) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/responseSchemas.ts"],"sourcesContent":["import type { SimulateResult, SimulationRevert } from '@lifi/compose-spec';\nimport z from 'zod';\n\n/**\n * Zod-backed parsers for Compose API response bodies.\n *\n * Server responses are untrusted input crossing into the SDK, so the shapes the\n * client reads back are validated here rather than asserted with `as` casts. The\n * schemas are module-private (the package builds with `--isolatedDeclarations`,\n * which forbids exporting un-annotated complex values); the public surface is\n * the typed `parse*` helpers, whose explicit return types double as the contract.\n *\n * The simulate union is the one response the SDK fully owns and parses itself\n * (it is un-enveloped, unlike `/compose`), so it gets a complete schema kept in\n * lockstep with the canonical `SimulateResult` in `@lifi/compose-spec` by the\n * compile-time assertion below — the same anchoring `@lifi/api-schemas` uses for\n * its server-side simulate schema, so the two cannot silently drift.\n */\n\nconst balanceEntrySchema = z.object({\n token: z.string(),\n owner: z.string(),\n amount: z.string(),\n});\n\nconst simulateOkSchema = z.object({\n status: z.literal('ok'),\n block: z.number(),\n timestamp: z.number(),\n balancesBefore: z.array(balanceEntrySchema),\n balancesAfter: z.array(balanceEntrySchema),\n deltas: z.array(balanceEntrySchema),\n gasUsed: z.string(),\n});\n\nconst simulateRevertDecodeResultSchema = z.object({\n errorCandidates: z\n .array(\n z.object({\n decodedErrorSignature: z.string(),\n decodedParams: z.array(z.string()),\n }),\n )\n .optional(),\n error: z.string().optional(),\n});\n\nconst simulateRevertSchema = z.object({\n status: z.literal('revert'),\n block: z.number(),\n timestamp: z.number(),\n revertReason: z.string().optional(),\n code: z.number().optional(),\n rawErrorBytes: z.string().optional(),\n decodeResult: simulateRevertDecodeResultSchema.optional(),\n});\n\nconst simulateSetupErrorSchema = z.object({\n status: z.literal('error'),\n message: z.string(),\n});\n\nconst simulateResultSchema = z.discriminatedUnion('status', [\n simulateOkSchema,\n simulateRevertSchema,\n simulateSetupErrorSchema,\n]);\n\n// The type the schema actually parses out. Kept private (referencing it from an\n// exported declaration would break `--isolatedDeclarations`) and pinned to the\n// canonical `SimulateResult` inside `parseSimulateResult` below.\ntype SimulateResultWire = z.infer<typeof simulateResultSchema>;\n\n/**\n * Parses a `POST /simulate` body into a {@link SimulateResult}. Returns `null`\n * when the body does not match the `ok`/`revert`/`error` union, letting the\n * caller raise a transport-level error.\n */\nexport const parseSimulateResult = (body: unknown): SimulateResult | null => {\n const parsed = simulateResultSchema.safeParse(body);\n if (!parsed.success) return null;\n // Pin the schema to the canonical contract in both directions, so it cannot\n // silently drift (mirroring the conformance assertion `@lifi/api-schemas` runs\n // on its server-side simulate schema). Assigning the inferred wire value to a\n // `SimulateResult` checks wire→spec; `satisfies SimulateResultWire` checks\n // spec→wire. A missing or mistyped field on either side fails to compile.\n const result: SimulateResult = parsed.data;\n return result satisfies SimulateResultWire;\n};\n\n/**\n * The validated members of an HTTP 206 partial compile envelope. `data` is\n * verified to be an object but kept untyped — compose-spec owns its full shape\n * (`ComposeCompilePartialData`) as a hand-authored type, so the caller narrows\n * it. `error` is fully validated.\n */\nexport interface CompilePartialEnvelope {\n readonly data: Record<string, unknown>;\n readonly error: { readonly kind: string; readonly message: string };\n}\n\nconst compilePartialEnvelopeSchema = z.object({\n data: z.record(z.string(), z.unknown()),\n error: z.object({ kind: z.string(), message: z.string() }),\n});\n\n/** Parses an HTTP 206 partial compile envelope; `null` when it does not match. */\nexport const parseCompilePartialEnvelope = (\n body: unknown,\n): CompilePartialEnvelope | null => {\n const parsed = compilePartialEnvelopeSchema.safeParse(body);\n return parsed.success ? parsed.data : null;\n};\n\n/** Error envelope returned on non-2xx responses. */\nexport interface ServerErrorBody {\n readonly error?: {\n readonly kind?: string;\n readonly message?: string;\n readonly path?: string;\n readonly details?: SimulationRevert;\n };\n}\n\n// `details` is passed through (`z.custom`) rather than parsed: the SDK does not\n// own the `SimulationRevert` schema and forwards the field verbatim onto\n// `ComposeError`.\nconst serverErrorBodySchema = z.object({\n error: z\n .object({\n kind: z.string().optional(),\n message: z.string().optional(),\n path: z.string().optional(),\n details: z.custom<SimulationRevert>().optional(),\n })\n .optional(),\n});\n\n/** Validates an already-parsed error envelope; `null` when it does not match. */\nexport const parseServerErrorBody = (json: unknown): ServerErrorBody | null => {\n const parsed = serverErrorBodySchema.safeParse(json);\n return parsed.success ? parsed.data : null;\n};\n"],"mappings":"AACA,OAAO,OAAO;AAkBd,MAAM,qBAAqB,EAAE,OAAO;AAAA,EAClC,OAAO,EAAE,OAAO;AAAA,EAChB,OAAO,EAAE,OAAO;AAAA,EAChB,QAAQ,EAAE,OAAO;AACnB,CAAC;AAED,MAAM,mBAAmB,EAAE,OAAO;AAAA,EAChC,QAAQ,EAAE,QAAQ,IAAI;AAAA,EACtB,OAAO,EAAE,OAAO;AAAA,EAChB,WAAW,EAAE,OAAO;AAAA,EACpB,gBAAgB,EAAE,MAAM,kBAAkB;AAAA,EAC1C,eAAe,EAAE,MAAM,kBAAkB;AAAA,EACzC,QAAQ,EAAE,MAAM,kBAAkB;AAAA,EAClC,SAAS,EAAE,OAAO;AACpB,CAAC;AAED,MAAM,mCAAmC,EAAE,OAAO;AAAA,EAChD,iBAAiB,EACd;AAAA,IACC,EAAE,OAAO;AAAA,MACP,uBAAuB,EAAE,OAAO;AAAA,MAChC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,IACnC,CAAC;AAAA,EACH,EACC,SAAS;AAAA,EACZ,OAAO,EAAE,OAAO,EAAE,SAAS;AAC7B,CAAC;AAED,MAAM,uBAAuB,EAAE,OAAO;AAAA,EACpC,QAAQ,EAAE,QAAQ,QAAQ;AAAA,EAC1B,OAAO,EAAE,OAAO;AAAA,EAChB,WAAW,EAAE,OAAO;AAAA,EACpB,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,cAAc,iCAAiC,SAAS;AAC1D,CAAC;AAED,MAAM,2BAA2B,EAAE,OAAO;AAAA,EACxC,QAAQ,EAAE,QAAQ,OAAO;AAAA,EACzB,SAAS,EAAE,OAAO;AACpB,CAAC;AAED,MAAM,uBAAuB,EAAE,mBAAmB,UAAU;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAYM,MAAM,sBAAsB,CAAC,SAAyC;AAC3E,QAAM,SAAS,qBAAqB,UAAU,IAAI;AAClD,MAAI,CAAC,OAAO,QAAS,QAAO;AAM5B,QAAM,SAAyB,OAAO;AACtC,SAAO;AACT;AAaA,MAAM,+BAA+B,EAAE,OAAO;AAAA,EAC5C,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,EACtC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,OAAO,EAAE,CAAC;AAC3D,CAAC;AAGM,MAAM,8BAA8B,CACzC,SACkC;AAClC,QAAM,SAAS,6BAA6B,UAAU,IAAI;AAC1D,SAAO,OAAO,UAAU,OAAO,OAAO;AACxC;AAeA,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,OAAO,EACJ,OAAO;AAAA,IACN,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,IAC1B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,IAC7B,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,IAC1B,SAAS,EAAE,OAAyB,EAAE,SAAS;AAAA,EACjD,CAAC,EACA,SAAS;AACd,CAAC;AAGM,MAAM,uBAAuB,CAAC,SAA0C;AAC7E,QAAM,SAAS,sBAAsB,UAAU,IAAI;AACnD,SAAO,OAAO,UAAU,OAAO,OAAO;AACxC;","names":[]}
1
+ {"version":3,"sources":["../src/responseSchemas.ts"],"sourcesContent":["import type { SimulateResult, SimulationRevert } from '@lifi/compose-spec';\nimport z from 'zod';\n\n/**\n * Zod-backed parsers for Compose API response bodies.\n *\n * Server responses are untrusted input crossing into the SDK, so the shapes the\n * client reads back are validated here rather than asserted with `as` casts. The\n * schemas are module-private (the package builds with `--isolatedDeclarations`,\n * which forbids exporting un-annotated complex values); the public surface is\n * the typed `parse*` helpers, whose explicit return types double as the contract.\n *\n * The simulate union is the one response the SDK fully owns and parses itself\n * (it is un-enveloped, unlike `/compose`), so it gets a complete schema kept in\n * lockstep with the canonical `SimulateResult` in `@lifi/compose-spec` by the\n * compile-time assertion below — the same anchoring `@lifi/api-schemas` uses for\n * its server-side simulate schema, so the two cannot silently drift.\n */\n\nconst balanceEntrySchema = z.object({\n token: z.string(),\n owner: z.string(),\n amount: z.string(),\n});\n\nconst simulateOkSchema = z.object({\n status: z.literal('ok'),\n block: z.number(),\n timestamp: z.number(),\n balancesBefore: z.array(balanceEntrySchema),\n balancesAfter: z.array(balanceEntrySchema),\n deltas: z.array(balanceEntrySchema),\n gasUsed: z.string(),\n});\n\nconst simulateRevertDecodeResultSchema = z.object({\n errorCandidates: z\n .array(\n z.object({\n decodedErrorSignature: z.string(),\n decodedParams: z.array(z.string()),\n }),\n )\n .optional(),\n error: z.string().optional(),\n});\n\nconst simulateRevertSchema = z.object({\n status: z.literal('revert'),\n block: z.number(),\n timestamp: z.number(),\n revertReason: z.string().optional(),\n code: z.number().optional(),\n rawErrorBytes: z.string().optional(),\n decodeResult: simulateRevertDecodeResultSchema.optional(),\n});\n\nconst simulateSetupErrorSchema = z.object({\n status: z.literal('error'),\n message: z.string(),\n});\n\nconst simulateResultSchema = z.discriminatedUnion('status', [\n simulateOkSchema,\n simulateRevertSchema,\n simulateSetupErrorSchema,\n]);\n\n// The type the schema actually parses out. Kept private (referencing it from an\n// exported declaration would break `--isolatedDeclarations`) and pinned to the\n// canonical `SimulateResult` inside `parseSimulateResult` below.\ntype SimulateResultWire = z.infer<typeof simulateResultSchema>;\n\n/**\n * Parses a `POST /simulate` body into a {@link SimulateResult}. Returns `null`\n * when the body does not match the `ok`/`revert`/`error` union, letting the\n * caller raise a transport-level error.\n */\nexport const parseSimulateResult = (body: unknown): SimulateResult | null => {\n const parsed = simulateResultSchema.safeParse(body);\n if (!parsed.success) return null;\n // Pin the schema to the canonical contract in both directions, so it cannot\n // silently drift (mirroring the conformance assertion `@lifi/api-schemas` runs\n // on its server-side simulate schema). Assigning the inferred wire value to a\n // `SimulateResult` checks wire→spec; `satisfies SimulateResultWire` checks\n // spec→wire. A missing or mistyped field on either side fails to compile.\n const result: SimulateResult = parsed.data;\n return result satisfies SimulateResultWire;\n};\n\n/**\n * The validated members of an HTTP 206 partial compile envelope. `data` is\n * verified to be an object but kept untyped — compose-spec owns its full shape\n * (`ComposeCompilePartialData`) as a hand-authored type, so the caller narrows\n * it. `error` is fully validated.\n */\nexport interface CompilePartialEnvelope {\n readonly data: Record<string, unknown>;\n readonly error: { readonly kind: string; readonly message: string };\n}\n\nconst compilePartialEnvelopeSchema = z.object({\n data: z.record(z.string(), z.unknown()),\n error: z.object({ kind: z.string(), message: z.string() }),\n});\n\n/** Parses an HTTP 206 partial compile envelope; `null` when it does not match. */\nexport const parseCompilePartialEnvelope = (\n body: unknown,\n): CompilePartialEnvelope | null => {\n const parsed = compilePartialEnvelopeSchema.safeParse(body);\n return parsed.success ? parsed.data : null;\n};\n\n/**\n * A single prepared-op failure inside a `preparation_error` envelope. `kind` is\n * kept as `string` here — the narrowing to `GenericComposeErrorKind` happens in\n * `errorFromHttpResponse`, where the value is lifted onto `ComposeError`.\n */\nexport interface ServerFailedPreparedOp {\n readonly callId: string;\n readonly op: string;\n readonly kind: string;\n readonly message: string;\n readonly path?: string;\n}\n\n/** Error envelope returned on non-2xx responses. */\nexport interface ServerErrorBody {\n readonly error?: {\n readonly kind?: string;\n readonly message?: string;\n readonly path?: string;\n readonly details?: SimulationRevert;\n readonly failedOps?: readonly ServerFailedPreparedOp[];\n readonly succeededOps?: readonly string[];\n };\n}\n\nconst failedPreparedOpSchema = z.object({\n callId: z.string(),\n op: z.string(),\n kind: z.string(),\n message: z.string(),\n path: z.string().optional(),\n});\n\n// `details` is passed through (`z.custom`) rather than parsed: the SDK does not\n// own the `SimulationRevert` schema and forwards the field verbatim onto\n// `ComposeError`.\n//\n// `failedOps`/`succeededOps` are optional here because every error kind shares\n// this envelope; they are only populated for `preparation_error` (422). Zod\n// strips undeclared keys, so omitting them would silently drop them.\nconst serverErrorBodySchema = z.object({\n error: z\n .object({\n kind: z.string().optional(),\n message: z.string().optional(),\n path: z.string().optional(),\n details: z.custom<SimulationRevert>().optional(),\n failedOps: z.array(failedPreparedOpSchema).readonly().optional(),\n succeededOps: z.array(z.string()).readonly().optional(),\n })\n .optional(),\n});\n\n/** Validates an already-parsed error envelope; `null` when it does not match. */\nexport const parseServerErrorBody = (json: unknown): ServerErrorBody | null => {\n const parsed = serverErrorBodySchema.safeParse(json);\n return parsed.success ? parsed.data : null;\n};\n"],"mappings":"AACA,OAAO,OAAO;AAkBd,MAAM,qBAAqB,EAAE,OAAO;AAAA,EAClC,OAAO,EAAE,OAAO;AAAA,EAChB,OAAO,EAAE,OAAO;AAAA,EAChB,QAAQ,EAAE,OAAO;AACnB,CAAC;AAED,MAAM,mBAAmB,EAAE,OAAO;AAAA,EAChC,QAAQ,EAAE,QAAQ,IAAI;AAAA,EACtB,OAAO,EAAE,OAAO;AAAA,EAChB,WAAW,EAAE,OAAO;AAAA,EACpB,gBAAgB,EAAE,MAAM,kBAAkB;AAAA,EAC1C,eAAe,EAAE,MAAM,kBAAkB;AAAA,EACzC,QAAQ,EAAE,MAAM,kBAAkB;AAAA,EAClC,SAAS,EAAE,OAAO;AACpB,CAAC;AAED,MAAM,mCAAmC,EAAE,OAAO;AAAA,EAChD,iBAAiB,EACd;AAAA,IACC,EAAE,OAAO;AAAA,MACP,uBAAuB,EAAE,OAAO;AAAA,MAChC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,IACnC,CAAC;AAAA,EACH,EACC,SAAS;AAAA,EACZ,OAAO,EAAE,OAAO,EAAE,SAAS;AAC7B,CAAC;AAED,MAAM,uBAAuB,EAAE,OAAO;AAAA,EACpC,QAAQ,EAAE,QAAQ,QAAQ;AAAA,EAC1B,OAAO,EAAE,OAAO;AAAA,EAChB,WAAW,EAAE,OAAO;AAAA,EACpB,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,cAAc,iCAAiC,SAAS;AAC1D,CAAC;AAED,MAAM,2BAA2B,EAAE,OAAO;AAAA,EACxC,QAAQ,EAAE,QAAQ,OAAO;AAAA,EACzB,SAAS,EAAE,OAAO;AACpB,CAAC;AAED,MAAM,uBAAuB,EAAE,mBAAmB,UAAU;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAYM,MAAM,sBAAsB,CAAC,SAAyC;AAC3E,QAAM,SAAS,qBAAqB,UAAU,IAAI;AAClD,MAAI,CAAC,OAAO,QAAS,QAAO;AAM5B,QAAM,SAAyB,OAAO;AACtC,SAAO;AACT;AAaA,MAAM,+BAA+B,EAAE,OAAO;AAAA,EAC5C,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,EACtC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,OAAO,EAAE,CAAC;AAC3D,CAAC;AAGM,MAAM,8BAA8B,CACzC,SACkC;AAClC,QAAM,SAAS,6BAA6B,UAAU,IAAI;AAC1D,SAAO,OAAO,UAAU,OAAO,OAAO;AACxC;AA2BA,MAAM,yBAAyB,EAAE,OAAO;AAAA,EACtC,QAAQ,EAAE,OAAO;AAAA,EACjB,IAAI,EAAE,OAAO;AAAA,EACb,MAAM,EAAE,OAAO;AAAA,EACf,SAAS,EAAE,OAAO;AAAA,EAClB,MAAM,EAAE,OAAO,EAAE,SAAS;AAC5B,CAAC;AASD,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,OAAO,EACJ,OAAO;AAAA,IACN,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,IAC1B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,IAC7B,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,IAC1B,SAAS,EAAE,OAAyB,EAAE,SAAS;AAAA,IAC/C,WAAW,EAAE,MAAM,sBAAsB,EAAE,SAAS,EAAE,SAAS;AAAA,IAC/D,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EACxD,CAAC,EACA,SAAS;AACd,CAAC;AAGM,MAAM,uBAAuB,CAAC,SAA0C;AAC7E,QAAM,SAAS,sBAAsB,UAAU,IAAI;AACnD,SAAO,OAAO,UAAU,OAAO,OAAO;AACxC;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/composer-sdk",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Public Composer SDK for building and submitting flows",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -51,7 +51,7 @@
51
51
  "example:staged": "tsx src/examples/run.ts --staged"
52
52
  },
53
53
  "peerDependencies": {
54
- "@lifi/compose-spec": "0.3.0"
54
+ "@lifi/compose-spec": "0.4.0"
55
55
  },
56
56
  "dependencies": {
57
57
  "abitype": "^1.2.3",
package/src/errors.ts CHANGED
@@ -1,4 +1,9 @@
1
- import type { ComposeErrorKind, SimulationRevert } from '@lifi/compose-spec';
1
+ import type {
2
+ ComposeErrorKind,
3
+ FailedPreparedOp,
4
+ GenericComposeErrorKind,
5
+ SimulationRevert,
6
+ } from '@lifi/compose-spec';
2
7
 
3
8
  import {
4
9
  parseServerErrorBody,
@@ -62,6 +67,17 @@ export class ComposeError extends Error {
62
67
  * backend can parse the revert reason.
63
68
  */
64
69
  readonly details?: SimulationRevert;
70
+ /**
71
+ * The prepared ops that failed, attached to `preparation_error` errors.
72
+ * Each entry carries the `callId` of the failing node so callers can drop
73
+ * the unroutable legs and resubmit a smaller flow.
74
+ */
75
+ readonly failedOps?: readonly FailedPreparedOp[];
76
+ /**
77
+ * The `callId`s of the prepared ops that succeeded, attached to
78
+ * `preparation_error` errors alongside {@link ComposeError.failedOps}.
79
+ */
80
+ readonly succeededOps?: readonly string[];
65
81
 
66
82
  constructor(
67
83
  code: ComposeErrorCode,
@@ -73,6 +89,8 @@ export class ComposeError extends Error {
73
89
  kind?: ComposeErrorKind;
74
90
  path?: string;
75
91
  details?: SimulationRevert;
92
+ failedOps?: readonly FailedPreparedOp[];
93
+ succeededOps?: readonly string[];
76
94
  },
77
95
  ) {
78
96
  super(message, { cause: options?.cause });
@@ -82,9 +100,21 @@ export class ComposeError extends Error {
82
100
  this.kind = options?.kind;
83
101
  this.path = options?.path;
84
102
  this.details = options?.details;
103
+ this.failedOps = options?.failedOps;
104
+ this.succeededOps = options?.succeededOps;
85
105
  }
86
106
  }
87
107
 
108
+ /**
109
+ * The per-op preparation diagnostics carried by `preparation_error` responses,
110
+ * with both arrays known to be present. Produced by narrowing with
111
+ * {@link isComposePreparationError}.
112
+ */
113
+ export interface ComposePreparationOps {
114
+ readonly failedOps: readonly FailedPreparedOp[];
115
+ readonly succeededOps: readonly string[];
116
+ }
117
+
88
118
  /**
89
119
  * Type guard that narrows an unknown error to {@link ComposeError}.
90
120
  * @param e - The value to check.
@@ -94,6 +124,35 @@ export const isComposeError = (e: unknown): e is ComposeError =>
94
124
  e instanceof ComposeError ||
95
125
  (e instanceof Error && e.name === 'ComposeError' && 'code' in e);
96
126
 
127
+ /**
128
+ * Type guard for `preparation_error` failures (HTTP 422): a mixed basket where
129
+ * some prepared ops failed and others succeeded. Narrows both
130
+ * {@link ComposeError.failedOps} and {@link ComposeError.succeededOps} to
131
+ * present, so callers can read them without a cast or a truthiness check.
132
+ *
133
+ * @param e - The value to check.
134
+ * @returns `true` if `e` is a `ComposeError` carrying per-op preparation diagnostics.
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * try {
139
+ * await builder.compile(run);
140
+ * } catch (e) {
141
+ * if (isComposePreparationError(e)) {
142
+ * console.error('Failed ops:', e.failedOps);
143
+ * console.error('Succeeded ops:', e.succeededOps);
144
+ * }
145
+ * }
146
+ * ```
147
+ */
148
+ export const isComposePreparationError = (
149
+ e: unknown,
150
+ ): e is ComposeError & ComposePreparationOps =>
151
+ isComposeError(e) &&
152
+ e.kind === 'preparation_error' &&
153
+ Array.isArray(e.failedOps) &&
154
+ Array.isArray(e.succeededOps);
155
+
97
156
  const STATUS_TO_CODE: ReadonlyMap<number, ComposeErrorCode> = new Map<
98
157
  number,
99
158
  ComposeErrorCode
@@ -143,6 +202,13 @@ export const errorFromHttpResponse = (
143
202
  kind: serverError?.kind as ComposeErrorKind | undefined,
144
203
  path: serverError?.path,
145
204
  details: serverError?.details,
205
+ // The wire schema types `kind` as a bare string; the server only ever
206
+ // reports generic kinds for individual ops.
207
+ failedOps: serverError?.failedOps?.map((op) => ({
208
+ ...op,
209
+ kind: op.kind as GenericComposeErrorKind,
210
+ })),
211
+ succeededOps: serverError?.succeededOps,
146
212
  },
147
213
  );
148
214
  };
package/src/index.ts CHANGED
@@ -26,7 +26,11 @@ export type {
26
26
  ZapPackEdge,
27
27
  ZapPackOverview,
28
28
  } from './discovery.js';
29
- export type { ComposeError, ComposeErrorCode } from './errors.js';
29
+ export type {
30
+ ComposeError,
31
+ ComposeErrorCode,
32
+ ComposePreparationOps,
33
+ } from './errors.js';
30
34
  export type {
31
35
  AppliedGuard,
32
36
  ApprovalEntry,
@@ -38,6 +42,7 @@ export type {
38
42
  ComposeContinuation,
39
43
  ComposeErrorKind,
40
44
  ComposeTransactionRequest,
45
+ FailedPreparedOp,
41
46
  Flow,
42
47
  InputSpec,
43
48
  MaterialiserConfigOf,
@@ -74,7 +79,7 @@ export {
74
79
  SIMULATE_MAX_TRACKED_BALANCES,
75
80
  SIMULATE_MAX_REQUIREMENTS,
76
81
  } from '@lifi/compose-spec';
77
- export { isComposeError } from './errors.js';
82
+ export { isComposeError, isComposePreparationError } from './errors.js';
78
83
 
79
84
  export type {
80
85
  Bindable,
@@ -112,6 +112,19 @@ export const parseCompilePartialEnvelope = (
112
112
  return parsed.success ? parsed.data : null;
113
113
  };
114
114
 
115
+ /**
116
+ * A single prepared-op failure inside a `preparation_error` envelope. `kind` is
117
+ * kept as `string` here — the narrowing to `GenericComposeErrorKind` happens in
118
+ * `errorFromHttpResponse`, where the value is lifted onto `ComposeError`.
119
+ */
120
+ export interface ServerFailedPreparedOp {
121
+ readonly callId: string;
122
+ readonly op: string;
123
+ readonly kind: string;
124
+ readonly message: string;
125
+ readonly path?: string;
126
+ }
127
+
115
128
  /** Error envelope returned on non-2xx responses. */
116
129
  export interface ServerErrorBody {
117
130
  readonly error?: {
@@ -119,12 +132,26 @@ export interface ServerErrorBody {
119
132
  readonly message?: string;
120
133
  readonly path?: string;
121
134
  readonly details?: SimulationRevert;
135
+ readonly failedOps?: readonly ServerFailedPreparedOp[];
136
+ readonly succeededOps?: readonly string[];
122
137
  };
123
138
  }
124
139
 
140
+ const failedPreparedOpSchema = z.object({
141
+ callId: z.string(),
142
+ op: z.string(),
143
+ kind: z.string(),
144
+ message: z.string(),
145
+ path: z.string().optional(),
146
+ });
147
+
125
148
  // `details` is passed through (`z.custom`) rather than parsed: the SDK does not
126
149
  // own the `SimulationRevert` schema and forwards the field verbatim onto
127
150
  // `ComposeError`.
151
+ //
152
+ // `failedOps`/`succeededOps` are optional here because every error kind shares
153
+ // this envelope; they are only populated for `preparation_error` (422). Zod
154
+ // strips undeclared keys, so omitting them would silently drop them.
128
155
  const serverErrorBodySchema = z.object({
129
156
  error: z
130
157
  .object({
@@ -132,6 +159,8 @@ const serverErrorBodySchema = z.object({
132
159
  message: z.string().optional(),
133
160
  path: z.string().optional(),
134
161
  details: z.custom<SimulationRevert>().optional(),
162
+ failedOps: z.array(failedPreparedOpSchema).readonly().optional(),
163
+ succeededOps: z.array(z.string()).readonly().optional(),
135
164
  })
136
165
  .optional(),
137
166
  });