@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 +21 -0
- package/dist/client.cjs +1 -1
- package/dist/client.js +1 -1
- package/dist/errors.cjs +26 -3
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +46 -2
- package/dist/errors.d.ts +46 -2
- package/dist/errors.js +24 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/responseSchemas.cjs +10 -1
- package/dist/responseSchemas.cjs.map +1 -1
- package/dist/responseSchemas.d.cts +15 -1
- package/dist/responseSchemas.d.ts +15 -1
- package/dist/responseSchemas.js +10 -1
- package/dist/responseSchemas.js.map +1 -1
- package/package.json +2 -2
- package/src/errors.ts +67 -1
- package/src/index.ts +7 -2
- package/src/responseSchemas.ts +29 -0
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.
|
|
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.
|
|
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
|
package/dist/errors.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts"],"sourcesContent":["import type {
|
|
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
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts"],"sourcesContent":["import type {
|
|
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,
|
package/dist/index.cjs.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 {
|
|
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 {
|
|
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":[]}
|
package/dist/responseSchemas.cjs
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,iBAAc;AAkBd,MAAM,qBAAqB,WAAAA,QAAE,OAAO;AAAA,EAClC,OAAO,WAAAA,QAAE,OAAO;AAAA,EAChB,OAAO,WAAAA,QAAE,OAAO;AAAA,EAChB,QAAQ,WAAAA,QAAE,OAAO;AACnB,CAAC;AAED,MAAM,mBAAmB,WAAAA,QAAE,OAAO;AAAA,EAChC,QAAQ,WAAAA,QAAE,QAAQ,IAAI;AAAA,EACtB,OAAO,WAAAA,QAAE,OAAO;AAAA,EAChB,WAAW,WAAAA,QAAE,OAAO;AAAA,EACpB,gBAAgB,WAAAA,QAAE,MAAM,kBAAkB;AAAA,EAC1C,eAAe,WAAAA,QAAE,MAAM,kBAAkB;AAAA,EACzC,QAAQ,WAAAA,QAAE,MAAM,kBAAkB;AAAA,EAClC,SAAS,WAAAA,QAAE,OAAO;AACpB,CAAC;AAED,MAAM,mCAAmC,WAAAA,QAAE,OAAO;AAAA,EAChD,iBAAiB,WAAAA,QACd;AAAA,IACC,WAAAA,QAAE,OAAO;AAAA,MACP,uBAAuB,WAAAA,QAAE,OAAO;AAAA,MAChC,eAAe,WAAAA,QAAE,MAAM,WAAAA,QAAE,OAAO,CAAC;AAAA,IACnC,CAAC;AAAA,EACH,EACC,SAAS;AAAA,EACZ,OAAO,WAAAA,QAAE,OAAO,EAAE,SAAS;AAC7B,CAAC;AAED,MAAM,uBAAuB,WAAAA,QAAE,OAAO;AAAA,EACpC,QAAQ,WAAAA,QAAE,QAAQ,QAAQ;AAAA,EAC1B,OAAO,WAAAA,QAAE,OAAO;AAAA,EAChB,WAAW,WAAAA,QAAE,OAAO;AAAA,EACpB,cAAc,WAAAA,QAAE,OAAO,EAAE,SAAS;AAAA,EAClC,MAAM,WAAAA,QAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,eAAe,WAAAA,QAAE,OAAO,EAAE,SAAS;AAAA,EACnC,cAAc,iCAAiC,SAAS;AAC1D,CAAC;AAED,MAAM,2BAA2B,WAAAA,QAAE,OAAO;AAAA,EACxC,QAAQ,WAAAA,QAAE,QAAQ,OAAO;AAAA,EACzB,SAAS,WAAAA,QAAE,OAAO;AACpB,CAAC;AAED,MAAM,uBAAuB,WAAAA,QAAE,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,WAAAA,QAAE,OAAO;AAAA,EAC5C,MAAM,WAAAA,QAAE,OAAO,WAAAA,QAAE,OAAO,GAAG,WAAAA,QAAE,QAAQ,CAAC;AAAA,EACtC,OAAO,WAAAA,QAAE,OAAO,EAAE,MAAM,WAAAA,QAAE,OAAO,GAAG,SAAS,WAAAA,QAAE,OAAO,EAAE,CAAC;AAC3D,CAAC;AAGM,MAAM,8BAA8B,CACzC,SACkC;AAClC,QAAM,SAAS,6BAA6B,UAAU,IAAI;AAC1D,SAAO,OAAO,UAAU,OAAO,OAAO;AACxC;
|
|
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 };
|
package/dist/responseSchemas.js
CHANGED
|
@@ -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;
|
|
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":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifi/composer-sdk",
|
|
3
|
-
"version": "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.
|
|
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 {
|
|
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 {
|
|
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,
|
package/src/responseSchemas.ts
CHANGED
|
@@ -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
|
});
|