@lifi/composer-sdk 0.6.2 → 0.7.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 +202 -189
- package/dist/authoring/FlowBuilderCore.cjs +16 -3
- package/dist/authoring/FlowBuilderCore.cjs.map +1 -1
- package/dist/authoring/FlowBuilderCore.d.cts +12 -4
- package/dist/authoring/FlowBuilderCore.d.ts +12 -4
- package/dist/authoring/FlowBuilderCore.js +19 -3
- package/dist/authoring/FlowBuilderCore.js.map +1 -1
- package/dist/authoring/handles.cjs.map +1 -1
- package/dist/authoring/handles.d.cts +35 -1
- package/dist/authoring/handles.d.ts +35 -1
- package/dist/authoring/handles.js.map +1 -1
- package/dist/client.cjs +1 -1
- package/dist/client.js +1 -1
- package/dist/generated/config.generated.cjs.map +1 -1
- package/dist/generated/config.generated.d.cts +500 -6
- package/dist/generated/config.generated.d.ts +500 -6
- package/dist/generated/operations.generated.cjs +7 -0
- package/dist/generated/operations.generated.cjs.map +1 -1
- package/dist/generated/operations.generated.d.cts +20 -5
- package/dist/generated/operations.generated.d.ts +20 -5
- package/dist/generated/operations.generated.js +7 -0
- package/dist/generated/operations.generated.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js.map +1 -1
- package/dist/sdk.cjs.map +1 -1
- package/dist/sdk.d.cts +5 -6
- package/dist/sdk.d.ts +5 -6
- package/dist/sdk.js.map +1 -1
- package/package.json +4 -3
- package/src/authoring/FlowBuilderCore.ts +55 -18
- package/src/authoring/handles.ts +67 -0
- package/src/examples/splitAndZap.ts +9 -8
- package/src/generated/config.generated.ts +516 -5
- package/src/generated/operations.generated.ts +52 -3
- package/src/index.ts +5 -1
- package/src/sdk.ts +5 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifi/composer-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Public Composer SDK for building and submitting flows",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -47,16 +47,17 @@
|
|
|
47
47
|
"example:staged": "tsx src/examples/run.ts --staged"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@lifi/compose-spec": "0.
|
|
50
|
+
"@lifi/compose-spec": "0.7.0"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"abitype": "^1.2.3",
|
|
54
54
|
"effect": "^3.21.0",
|
|
55
|
-
"zod": "^4.
|
|
55
|
+
"zod": "^4.6.5"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@eslint/compat": "^1.4.0",
|
|
59
59
|
"@lifi/compose-spec": "workspace:^",
|
|
60
|
+
"@lifi/vitest-config": "workspace:^",
|
|
60
61
|
"@types/node": "^25.6.0",
|
|
61
62
|
"@typescript-eslint/eslint-plugin": "^8.68.0",
|
|
62
63
|
"@typescript-eslint/parser": "^8.68.0",
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
AppliedGuard,
|
|
3
|
-
BindValue,
|
|
4
|
-
Call,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import {
|
|
2
|
+
type AppliedGuard,
|
|
3
|
+
type BindValue,
|
|
4
|
+
type Call,
|
|
5
|
+
familySize,
|
|
6
|
+
type Flow,
|
|
7
|
+
type FlowInput,
|
|
8
|
+
instancePortName,
|
|
9
|
+
type Ref,
|
|
10
|
+
type Resource,
|
|
11
|
+
type SolType,
|
|
10
12
|
} from '@lifi/compose-spec';
|
|
11
13
|
|
|
12
14
|
import type {
|
|
@@ -146,16 +148,34 @@ export interface FlowBuilderCore<T extends InputSchema = InputSchema> {
|
|
|
146
148
|
/** Maps output port names to their output kind for compile-time and runtime validation. */
|
|
147
149
|
export type OutputSpec = Record<string, OutputKind>;
|
|
148
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Maps each output family's port name to the config key that sizes it: an
|
|
153
|
+
* array (one instance per element) or a non-negative integer count.
|
|
154
|
+
* A family port is also listed in the {@link OutputSpec}, as `'resource'`.
|
|
155
|
+
*/
|
|
156
|
+
export type FamilySpec = Readonly<Record<string, string>>;
|
|
157
|
+
|
|
158
|
+
/** The handles `call` returns for an {@link OutputSpec} and its families. */
|
|
159
|
+
export type OutputHandles<O extends OutputSpec, F extends FamilySpec> = {
|
|
160
|
+
readonly [K in keyof O & string]: K extends keyof F
|
|
161
|
+
? readonly OutputHandle<'resource'>[]
|
|
162
|
+
: OutputHandle<O[K]>;
|
|
163
|
+
};
|
|
164
|
+
|
|
149
165
|
/** Internal interface exposed to bindGeneratedOps — not part of the public API. */
|
|
150
166
|
export interface FlowBuilderInternal<
|
|
151
167
|
T extends InputSchema = InputSchema,
|
|
152
168
|
> extends FlowBuilderCore<T> {
|
|
153
|
-
readonly call: <
|
|
169
|
+
readonly call: <
|
|
170
|
+
O extends OutputSpec,
|
|
171
|
+
F extends FamilySpec = Record<never, string>,
|
|
172
|
+
>(
|
|
154
173
|
nodeId: string,
|
|
155
174
|
op: string,
|
|
156
175
|
args: CallArgs,
|
|
157
176
|
outputs: O,
|
|
158
|
-
|
|
177
|
+
families?: F,
|
|
178
|
+
) => OutputHandles<O, F>;
|
|
159
179
|
}
|
|
160
180
|
|
|
161
181
|
const isResource = (decl: InputDecl): decl is Resource =>
|
|
@@ -194,12 +214,16 @@ export const createFlowBuilderCore = <T extends InputSchema>(
|
|
|
194
214
|
}
|
|
195
215
|
}
|
|
196
216
|
|
|
197
|
-
const call = <
|
|
217
|
+
const call = <
|
|
218
|
+
O extends OutputSpec,
|
|
219
|
+
F extends FamilySpec = Record<never, string>,
|
|
220
|
+
>(
|
|
198
221
|
nodeId: string,
|
|
199
222
|
op: string,
|
|
200
223
|
args: CallArgs,
|
|
201
224
|
outputs: O,
|
|
202
|
-
|
|
225
|
+
families?: F,
|
|
226
|
+
): OutputHandles<O, F> => {
|
|
203
227
|
if (nodeIds.has(nodeId)) throw new Error(`Duplicate node id: "${nodeId}"`);
|
|
204
228
|
nodeIds.add(nodeId);
|
|
205
229
|
|
|
@@ -208,18 +232,26 @@ export const createFlowBuilderCore = <T extends InputSchema>(
|
|
|
208
232
|
bind[key] = toBindValue(val);
|
|
209
233
|
}
|
|
210
234
|
|
|
235
|
+
const config = (args.config ?? {}) as Record<string, unknown>;
|
|
211
236
|
const node: Call = {
|
|
212
237
|
id: nodeId,
|
|
213
238
|
op,
|
|
214
239
|
bind,
|
|
215
|
-
config
|
|
240
|
+
config,
|
|
216
241
|
...(args.guards && args.guards.length > 0 && { guards: args.guards }),
|
|
217
242
|
};
|
|
218
243
|
nodes.push(node);
|
|
219
244
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
245
|
+
const outputHandle = (portName: string): OutputHandle => ({
|
|
246
|
+
_tag: 'output',
|
|
247
|
+
nodeId,
|
|
248
|
+
portName,
|
|
249
|
+
});
|
|
250
|
+
return new Proxy({} as OutputHandles<O, F>, {
|
|
251
|
+
get: (
|
|
252
|
+
_target,
|
|
253
|
+
prop,
|
|
254
|
+
): OutputHandle | readonly OutputHandle[] | undefined => {
|
|
223
255
|
if (typeof prop !== 'string' || prop === 'then') return undefined;
|
|
224
256
|
if (!(prop in outputs)) {
|
|
225
257
|
throw new Error(
|
|
@@ -228,7 +260,12 @@ export const createFlowBuilderCore = <T extends InputSchema>(
|
|
|
228
260
|
).join(', ')}`,
|
|
229
261
|
);
|
|
230
262
|
}
|
|
231
|
-
|
|
263
|
+
const countFrom = families?.[prop];
|
|
264
|
+
if (countFrom === undefined) return outputHandle(prop);
|
|
265
|
+
const size = familySize({ name: prop, countFrom }, config);
|
|
266
|
+
return Array.from({ length: size }, (_, index) =>
|
|
267
|
+
outputHandle(instancePortName(prop, index)),
|
|
268
|
+
);
|
|
232
269
|
},
|
|
233
270
|
});
|
|
234
271
|
};
|
package/src/authoring/handles.ts
CHANGED
|
@@ -39,6 +39,73 @@ export interface OutputHandle<T extends OutputKind = OutputKind> {
|
|
|
39
39
|
readonly __outputKind?: T;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* What an output family's sizing parameter accepts, given the config field the
|
|
44
|
+
* family counts from: an array of that field's elements, or a number.
|
|
45
|
+
*/
|
|
46
|
+
export type FamilySource<Field> = Field extends readonly (infer E)[]
|
|
47
|
+
? readonly E[]
|
|
48
|
+
: number;
|
|
49
|
+
|
|
50
|
+
/** Whether a number literal is a valid family count: a non-negative integer. */
|
|
51
|
+
type IsCountLiteral<N extends number> = `${N}` extends
|
|
52
|
+
`-${string}` | `${string}.${string}` | `${string}e${string}`
|
|
53
|
+
? false
|
|
54
|
+
: true;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* `N` resource handles as a tuple. A non-literal `number`, or a literal that is
|
|
58
|
+
* not a valid count, gives an open array; {@link FamilyCount} rejects the
|
|
59
|
+
* invalid literal at the call site.
|
|
60
|
+
*/
|
|
61
|
+
type HandleTuple<
|
|
62
|
+
N extends number,
|
|
63
|
+
Acc extends readonly OutputHandle<'resource'>[] = readonly [],
|
|
64
|
+
> = number extends N
|
|
65
|
+
? readonly OutputHandle<'resource'>[]
|
|
66
|
+
: IsCountLiteral<N> extends false
|
|
67
|
+
? readonly OutputHandle<'resource'>[]
|
|
68
|
+
: Acc['length'] extends N
|
|
69
|
+
? Acc
|
|
70
|
+
: HandleTuple<N, readonly [...Acc, OutputHandle<'resource'>]>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The handles of an output family: one resource {@link OutputHandle} per
|
|
74
|
+
* element of the config array the family counts from, or as many as the config
|
|
75
|
+
* count. A literal array or count yields a tuple of that length, so indexing
|
|
76
|
+
* past the last instance is a type error.
|
|
77
|
+
*/
|
|
78
|
+
export type OutputFamilyHandles<TCount extends readonly unknown[] | number> =
|
|
79
|
+
TCount extends readonly unknown[]
|
|
80
|
+
? { readonly [I in keyof TCount]: OutputHandle<'resource'> }
|
|
81
|
+
: TCount extends number
|
|
82
|
+
? HandleTuple<TCount>
|
|
83
|
+
: never;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Holds the config value an output family counts from to the op's own type. A
|
|
87
|
+
* runtime-sized array or count passes as is. A literal tuple must also satisfy
|
|
88
|
+
* `Shape`, so a tuple shorter than the op's `minItems` fails at the call site
|
|
89
|
+
* instead of at the server's bind step. A literal count must be a non-negative
|
|
90
|
+
* integer.
|
|
91
|
+
*/
|
|
92
|
+
export type FamilyCount<
|
|
93
|
+
TCount extends readonly unknown[] | number,
|
|
94
|
+
Shape,
|
|
95
|
+
> = TCount extends readonly unknown[]
|
|
96
|
+
? number extends TCount['length']
|
|
97
|
+
? unknown
|
|
98
|
+
: TCount extends Shape
|
|
99
|
+
? unknown
|
|
100
|
+
: { readonly 'output family config array has too few entries': never }
|
|
101
|
+
: TCount extends number
|
|
102
|
+
? IsCountLiteral<TCount> extends true
|
|
103
|
+
? unknown
|
|
104
|
+
: {
|
|
105
|
+
readonly 'output family count must be a non-negative integer': never;
|
|
106
|
+
}
|
|
107
|
+
: unknown;
|
|
108
|
+
|
|
42
109
|
/** Discriminated union of all handle types that can be bound to operation arguments. */
|
|
43
110
|
export type AnyHandle = InputHandle | OutputHandle;
|
|
44
111
|
|
|
@@ -18,8 +18,8 @@ const STEAKHOUSE_USDC = '0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB';
|
|
|
18
18
|
* Split USDC 60/40 and zap each portion into a different vault.
|
|
19
19
|
*
|
|
20
20
|
* Demonstrates:
|
|
21
|
-
* - core.
|
|
22
|
-
* -
|
|
21
|
+
* - core.partition to divide a resource into one part per basis-point share
|
|
22
|
+
* - Destructuring the typed `parts` tuple into two independent lifi.zap nodes
|
|
23
23
|
* - Slippage guards on both zap outputs
|
|
24
24
|
*/
|
|
25
25
|
export const buildSplitAndZapExample = (): {
|
|
@@ -35,15 +35,16 @@ export const buildSplitAndZapExample = (): {
|
|
|
35
35
|
},
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
//
|
|
39
|
-
|
|
38
|
+
// Partition USDC 60/40 — one part per `bps` entry. Every part but the last
|
|
39
|
+
// is rounded down; the last takes the remainder.
|
|
40
|
+
const [toAave, toMorpho] = builder.core.partition('split', {
|
|
40
41
|
bind: { source: builder.inputs.amountIn },
|
|
41
|
-
config: { bps: 6000 },
|
|
42
|
-
});
|
|
42
|
+
config: { bps: [6000, 4000] },
|
|
43
|
+
}).parts;
|
|
43
44
|
|
|
44
45
|
// Zap 60% into Aave v3 aEthUSDC.
|
|
45
46
|
builder.lifi.zap('zap-aave', {
|
|
46
|
-
bind: { amountIn:
|
|
47
|
+
bind: { amountIn: toAave },
|
|
47
48
|
config: {
|
|
48
49
|
resourceOut: resources.erc20(A_ETH_USDC, 1),
|
|
49
50
|
},
|
|
@@ -52,7 +53,7 @@ export const buildSplitAndZapExample = (): {
|
|
|
52
53
|
|
|
53
54
|
// Zap 40% into Steakhouse USDC (Morpho).
|
|
54
55
|
builder.lifi.zap('zap-morpho', {
|
|
55
|
-
bind: { amountIn:
|
|
56
|
+
bind: { amountIn: toMorpho },
|
|
56
57
|
config: {
|
|
57
58
|
resourceOut: resources.erc20(STEAKHOUSE_USDC, 1),
|
|
58
59
|
},
|