@lifi/composer-sdk 0.4.1 → 0.5.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.
Files changed (92) hide show
  1. package/README.md +6 -3
  2. package/dist/authoring/FlowBuilderCore.cjs +1 -5
  3. package/dist/authoring/FlowBuilderCore.cjs.map +1 -1
  4. package/dist/authoring/FlowBuilderCore.d.cts +9 -7
  5. package/dist/authoring/FlowBuilderCore.d.ts +9 -7
  6. package/dist/authoring/FlowBuilderCore.js +2 -6
  7. package/dist/authoring/FlowBuilderCore.js.map +1 -1
  8. package/dist/authoring/handles.cjs +5 -2
  9. package/dist/authoring/handles.cjs.map +1 -1
  10. package/dist/authoring/handles.d.cts +25 -6
  11. package/dist/authoring/handles.d.ts +25 -6
  12. package/dist/authoring/handles.js +3 -1
  13. package/dist/authoring/handles.js.map +1 -1
  14. package/dist/authoring/raw.cjs +3 -0
  15. package/dist/authoring/raw.cjs.map +1 -1
  16. package/dist/authoring/raw.d.cts +33 -3
  17. package/dist/authoring/raw.d.ts +33 -3
  18. package/dist/authoring/raw.js +2 -0
  19. package/dist/authoring/raw.js.map +1 -1
  20. package/dist/authoring/signatureArgs.cjs.map +1 -1
  21. package/dist/authoring/signatureArgs.d.cts +16 -12
  22. package/dist/authoring/signatureArgs.d.ts +16 -12
  23. package/dist/authoring/signatureArgs.js.map +1 -1
  24. package/dist/client.cjs +70 -36
  25. package/dist/client.cjs.map +1 -1
  26. package/dist/client.d.cts +33 -4
  27. package/dist/client.d.ts +33 -4
  28. package/dist/client.js +70 -36
  29. package/dist/client.js.map +1 -1
  30. package/dist/discovery.cjs.map +1 -1
  31. package/dist/discovery.d.cts +18 -2
  32. package/dist/discovery.d.ts +18 -2
  33. package/dist/generated/config.generated.cjs.map +1 -1
  34. package/dist/generated/config.generated.d.cts +21 -1
  35. package/dist/generated/config.generated.d.ts +21 -1
  36. package/dist/generated/operations.generated.cjs +6 -0
  37. package/dist/generated/operations.generated.cjs.map +1 -1
  38. package/dist/generated/operations.generated.d.cts +18 -2
  39. package/dist/generated/operations.generated.d.ts +18 -2
  40. package/dist/generated/operations.generated.js +6 -0
  41. package/dist/generated/operations.generated.js.map +1 -1
  42. package/dist/index.cjs.map +1 -1
  43. package/dist/index.d.cts +6 -6
  44. package/dist/index.d.ts +6 -6
  45. package/dist/index.js.map +1 -1
  46. package/dist/{raw-IxCF_bSS.d.ts → raw-BvRghN3r.d.ts} +3 -2
  47. package/dist/{raw-Dj_2rBSQ.d.cts → raw-s1rer8X3.d.cts} +3 -2
  48. package/dist/raw.cjs +2 -0
  49. package/dist/raw.cjs.map +1 -1
  50. package/dist/raw.d.cts +1 -1
  51. package/dist/raw.d.ts +1 -1
  52. package/dist/raw.js +2 -1
  53. package/dist/raw.js.map +1 -1
  54. package/dist/run/inputs.cjs +14 -15
  55. package/dist/run/inputs.cjs.map +1 -1
  56. package/dist/run/inputs.d.cts +8 -0
  57. package/dist/run/inputs.d.ts +8 -0
  58. package/dist/run/inputs.js +14 -15
  59. package/dist/run/inputs.js.map +1 -1
  60. package/dist/run/simulate.cjs +5 -0
  61. package/dist/run/simulate.cjs.map +1 -1
  62. package/dist/run/simulate.d.cts +5 -2
  63. package/dist/run/simulate.d.ts +5 -2
  64. package/dist/run/simulate.js +5 -0
  65. package/dist/run/simulate.js.map +1 -1
  66. package/dist/sdk.cjs +2 -0
  67. package/dist/sdk.cjs.map +1 -1
  68. package/dist/sdk.d.cts +28 -1
  69. package/dist/sdk.d.ts +28 -1
  70. package/dist/sdk.js +2 -0
  71. package/dist/sdk.js.map +1 -1
  72. package/dist/types.cjs.map +1 -1
  73. package/dist/types.d.cts +18 -2
  74. package/dist/types.d.ts +18 -2
  75. package/package.json +3 -10
  76. package/src/authoring/FlowBuilderCore.ts +13 -15
  77. package/src/authoring/handles.ts +41 -5
  78. package/src/authoring/raw.ts +36 -2
  79. package/src/authoring/signatureArgs.ts +17 -13
  80. package/src/client.ts +133 -39
  81. package/src/discovery.ts +18 -1
  82. package/src/examples/flySwap.ts +167 -0
  83. package/src/examples/run.ts +29 -4
  84. package/src/examples/supportedChains.ts +122 -0
  85. package/src/generated/config.generated.ts +24 -0
  86. package/src/generated/operations.generated.ts +26 -0
  87. package/src/index.ts +10 -1
  88. package/src/raw.ts +1 -1
  89. package/src/run/inputs.ts +30 -11
  90. package/src/run/simulate.ts +14 -2
  91. package/src/sdk.ts +30 -0
  92. package/src/types.ts +20 -1
package/README.md CHANGED
@@ -137,7 +137,8 @@ if (result.status === 'success') {
137
137
 
138
138
  - `sdk.client.compile(request)` — sends the flow to the backend and returns a `ComposeCompileResult` (discriminated union: `status: 'success'` or `status: 'partial'`)
139
139
  - `sdk.client.getManifest()` — fetches the operation manifest
140
- - `sdk.client.getZapPacks(options?)` — fetches the available routing edges grouped by protocol, returning `ZapPackOverview[]`. The catalog is dynamic and is not cached by the SDK. Filter through `GetZapPacksOptions`. An edge with `recipient: 'required'` requires the zap's `recipient` binding, delivers the output externally, and produces an output that cannot be chained or swept. An edge with `availability: 'future'` produces an output that settles only after the initiating transaction; route it via `lifi.zapAsync` with a bound `recipient`. Availability governs custody only — a future output still reports `simulated.amountOut` (an in-transaction estimate; the delivered amount arrives after settlement), with `simulated.amountOutMin` present only when a minimum source exists.
140
+ - `sdk.client.getChains()` / `sdk.getSupportedChains()` — fetches the chains this deployment supports as `readonly ChainSummary[]` (`{ chainId, name }`), ascending by `chainId`. A chain is listed once the VM contract is confirmed deployed on it, which is the same condition `compile`, `route` and `simulate` validate against — so a chain missing from this list is rejected by those methods. The converse does not hold: a listed chain can still have no routing edge for a given pair, reported by `route` as `no_route_error`. `name` is a display label maintained by the deployment, so render it but key on `chainId`; a chain with no configured label reports `Chain <id>`. The list tracks the backend's chain registry, which refreshes periodically, and is not cached by the SDK.
141
+ - `sdk.client.getZapPacks(options?)` — fetches the available routing edges grouped by protocol, returning `ZapPackOverview[]`. The catalog is dynamic and is not cached by the SDK. Filter through `GetZapPacksOptions`. An edge with `recipient: 'required'` requires the zap's `recipient` binding, delivers the output externally, and produces an output that cannot be chained or swept. An edge with `availability: 'future'` produces an output that settles only after the initiating transaction; `route` compiles it for you, and on the authoring path it needs `lifi.zapAsync` with a bound `recipient`. Availability governs custody only — a future output still reports `simulated.amountOut` (an in-transaction estimate; the delivered amount arrives after settlement), with `simulated.amountOutMin` present only when a minimum source applied. The catalog publishes no settlement duration: a deferred output's ETA rides on the compile result as `producedResources[*].etaSeconds` (expected seconds until the proceeds land, present only alongside `availability: 'future'` and only when the protocol publishes an estimate; absence means unknown, not zero).
141
142
  - `sdk.client.simulate(request)` / `sdk.simulate(request)` — simulates a raw, pre-encoded transaction and returns a `SimulateResult` (discriminated union: `status: 'ok' | 'revert' | 'error'`). See [Simulating a raw transaction](#simulating-a-raw-transaction).
142
143
  - `sdk.client.route(request)` / `sdk.route(request)` — compiles a single from/to token pair on one chain via `POST /compose/route`, returning the same `ComposeCompileResult` as `compile()`. See [Routing a token pair](#routing-a-token-pair).
143
144
 
@@ -197,7 +198,7 @@ for (const pack of packs) {
197
198
  Every edge is one routable pair: `edge.in` and `edge.out` are the `fromToken` and `toToken` of a `route` call on `edge.in.chainId`, and `inAmountMin` / `inAmountMax` bound the amount when the protocol constrains it. Two edge fields change how you call `route`:
198
199
 
199
200
  - **`recipient: 'required'`** — the protocol mints to a named receiver, so set `sweepTo` when the output should not land with the signer.
200
- - **`availability: 'future'`** — the output only exists once the transaction settles. `route` cannot compile these; they need `lifi.zapAsync` on the authoring path.
201
+ - **`availability: 'future'`** — the output only exists once the transaction settles. `route` skips these unless you pass `allowAsyncRoutes: true`, so by default a pair reachable only this way is a `no_route_error` and every produced resource is `availability: 'now'`. Opt in and `sweepTo` names the future output's owner, no slippage floor is applied, and you must track settlement rather than treat the transaction as completing the job. A synchronous edge still wins whenever the pair has one, so read `producedResources[*].availability` on the result to know which representation you got.
201
202
 
202
203
  ### Amounts
203
204
 
@@ -220,10 +221,11 @@ Both accept a `bigint` or a decimal string. `bigint` values are serialised for y
220
221
  - **`checkOnChainAllowances`** — when `true`, the server reads current allowances and omits approvals that are already sufficient. Defaults to `false`.
221
222
  - **`simulationPolicy`** — `'strict'` (default) or `'allow-revert'`, exactly as on the authoring path. See [Simulation policy and partial results](#simulation-policy-and-partial-results).
222
223
  - **`simulateUserProgram`** — when `true`, additionally simulates the final user-facing transaction after the structured simulation succeeds. Defaults to `false`.
224
+ - **`allowAsyncRoutes`** — when `true`, accept a route whose output arrives only after the initiating transaction settles. Defaults to `false`, which leaves such a pair a `no_route_error` and guarantees every produced resource is `availability: 'now'`. Set it only if you track settlement and can live without a slippage floor.
223
225
 
224
226
  ### When to author a flow instead
225
227
 
226
- `route` gives you one server-authored step. Reach for `sdk.flow(...)` when you need anything it does not express: several operations chained together, splitting one input across several destinations, guards on intermediate outputs, explicit preconditions, arithmetic, funding from proxy-held balances, or an asynchronous position (`availability: 'future'`) as the destination. Swapping WETH to USDC and then depositing the USDC into Aave is a flow; depositing USDC straight into Aave is a route.
228
+ `route` gives you one server-authored step. Reach for `sdk.flow(...)` when you need anything it does not express: several operations chained together, splitting one input across several destinations, guards on intermediate outputs, explicit preconditions, arithmetic, or funding from proxy-held balances. Swapping WETH to USDC and then depositing the USDC into Aave is a flow; depositing USDC straight into Aave is a route. An asynchronous position (`availability: 'future'`) as the destination is a route once you pass `allowAsyncRoutes: true` — the server picks `lifi.zapAsync` for you — but authoring the flow yourself is still the way to combine it with other steps.
227
229
 
228
230
  ### Errors
229
231
 
@@ -436,6 +438,7 @@ The `src/examples/` directory contains complete working examples:
436
438
  - **aaveSetEMode** — Switch the proxy's Aave v3 eMode category
437
439
  - **untypedOpWithTypedRef** — Insert an untyped operation node via `untypedOp`, then bridge its output into typed operations using `raw.ref<T>()`
438
440
  - **routeTokenPair** — Compile a from/to token pair via `sdk.route(...)`, with both the `exact` and `all` amount variants
441
+ - **supportedChains** — Cache `sdk.getSupportedChains()`, then preflight a chain and label it from the response's `name`, so an unsupported one fails locally with a message naming the alternatives
439
442
 
440
443
  ## Staging channel
441
444
 
@@ -23,10 +23,6 @@ __export(FlowBuilderCore_exports, {
23
23
  module.exports = __toCommonJS(FlowBuilderCore_exports);
24
24
  var import_handles = require("./handles.js");
25
25
  var import_raw = require("./raw.js");
26
- const toRef = (bindable) => {
27
- if ("_tag" in bindable) return (0, import_handles.handleToRef)(bindable);
28
- return bindable;
29
- };
30
26
  const isResource = (decl) => typeof decl === "object" && decl !== null && "kind" in decl;
31
27
  const createFlowBuilderCore = (chainId, options) => {
32
28
  const id = options.name ?? crypto.randomUUID();
@@ -53,7 +49,7 @@ const createFlowBuilderCore = (chainId, options) => {
53
49
  nodeIds.add(nodeId);
54
50
  const bind = {};
55
51
  for (const [key, val] of Object.entries(args.bind)) {
56
- bind[key] = toRef(val);
52
+ bind[key] = (0, import_handles.toBindValue)(val);
57
53
  }
58
54
  const node = {
59
55
  id: nodeId,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/authoring/FlowBuilderCore.ts"],"sourcesContent":["import type {\n AppliedGuard,\n Call,\n Flow,\n FlowInput,\n Ref,\n Resource,\n SolType,\n} from '@lifi/compose-spec';\n\nimport type {\n InputDecl,\n InputSchema,\n OutputKind,\n TypedGuard,\n TypedRef,\n} from '../types.js';\n\nimport type {\n InputHandle,\n OutputHandle,\n ResourceInputHandle,\n} from './handles.js';\nimport { handleToRef } from './handles.js';\nimport { ref } from './raw.js';\n\n/**\n * Arguments for calling an operation node in a flow.\n */\nexport interface CallArgs {\n /** Maps parameter names to input/output handles or raw `$ref` pointers. */\n readonly bind: Record<string, AnyBindable>;\n /** Optional operation-specific configuration. */\n readonly config?: object;\n /** Optional guards (e.g. slippage checks) applied to this operation's outputs. */\n readonly guards?: readonly TypedGuard[];\n}\n\n/**\n * Any value that can be bound to an operation parameter: an input handle,\n * an output handle from a previous operation, or a typed `$ref` pointer.\n *\n * Use `raw.ref<T>()` to create a typed ref from a raw path string.\n */\nexport type AnyBindable = InputHandle | OutputHandle | TypedRef;\n\nconst toRef = (bindable: AnyBindable): Ref => {\n if ('_tag' in bindable) return handleToRef(bindable);\n return bindable;\n};\n\n/**\n * Provides references to runtime context values available inside a flow.\n * Access via `builder.context`.\n */\nexport interface ContextAccessor {\n /** A `$ref` pointing to the transaction sender (signer) address. */\n readonly sender: TypedRef<'address'>;\n /** A `$ref` pointing to the on-chain execution (proxy) address. */\n readonly executionAddress: TypedRef<'address'>;\n}\n\ntype InputHandleOf<D extends InputDecl> = D extends Resource\n ? ResourceInputHandle\n : D extends SolType\n ? InputHandle<D>\n : InputHandle;\n\n/**\n * A mapped type that converts an input schema into typed handles.\n * Resource inputs produce {@link ResourceInputHandle}; scalar inputs produce {@link InputHandle}.\n *\n * @typeParam T - The flow's input schema.\n */\nexport type InputHandles<T extends InputSchema> = {\n readonly [K in keyof T]: InputHandleOf<T[K]>;\n};\n\n/**\n * Options for creating a new flow builder.\n *\n * @typeParam T - The flow's input schema.\n */\nexport interface FlowOptions<T extends InputSchema> {\n /** Optional human-readable name for the flow. Defaults to a random UUID. */\n readonly name?: string;\n /** Input declarations mapping names to resource or scalar types. */\n readonly inputs: T;\n}\n\n/**\n * A Flow document branded with its input schema for type-safe request building.\n *\n * The `__inputs` field is a phantom type — it exists only at compile time for\n * TypeScript inference and is never present at runtime.\n */\nexport type TypedFlow<T extends InputSchema = InputSchema> = Flow & {\n readonly __inputs?: T;\n};\n\n/**\n * Core flow builder interface providing low-level access to flow construction.\n *\n * For most use cases, prefer the {@link FlowBuilder} type returned by `sdk.flow()`\n * which adds generated operation methods and a `compile` method.\n *\n * @typeParam T - The flow's input schema.\n */\nexport interface FlowBuilderCore<T extends InputSchema = InputSchema> {\n /** Runtime context references (sender address, execution address). */\n readonly context: ContextAccessor;\n /** Typed handles for each declared flow input, used to bind inputs to operations. */\n readonly inputs: InputHandles<T>;\n /**\n * Appends an untyped operation node to the flow. This is an escape hatch\n * for operations not yet covered by the generated methods.\n *\n * Use `raw.ref<T>()` to reference this node's outputs in subsequent typed\n * operations.\n *\n * @param id - Unique node identifier within the flow.\n * @param op - The operation name (e.g. `\"custom.vaultQuery\"`).\n * @param args - Bind map, config, and optional guards for the node.\n * @throws Error if a node with the same `id` already exists in the flow.\n */\n readonly untypedOp: (\n id: string,\n op: string,\n args: {\n readonly bind: Record<string, Ref>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly AppliedGuard[];\n },\n ) => void;\n /** Serialises the builder state into a {@link TypedFlow} document. */\n readonly build: () => TypedFlow<T>;\n}\n\n/** Maps output port names to their output kind for compile-time and runtime validation. */\nexport type OutputSpec = Record<string, OutputKind>;\n\n/** Internal interface exposed to bindGeneratedOps — not part of the public API. */\nexport interface FlowBuilderInternal<\n T extends InputSchema = InputSchema,\n> extends FlowBuilderCore<T> {\n readonly call: <O extends OutputSpec>(\n nodeId: string,\n op: string,\n args: CallArgs,\n outputs: O,\n ) => { readonly [K in keyof O & string]: OutputHandle<O[K]> };\n}\n\nconst isResource = (decl: InputDecl): decl is Resource =>\n typeof decl === 'object' && decl !== null && 'kind' in decl;\n\n/**\n * Creates a new flow builder targeting the given chain.\n *\n * @param chainId - The EVM chain ID (e.g. `1` for Ethereum mainnet).\n * @param options - Flow configuration including input declarations.\n * @returns A {@link FlowBuilderInternal} instance.\n * @internal\n */\nexport const createFlowBuilderCore = <T extends InputSchema>(\n chainId: number,\n options: FlowOptions<T>,\n): FlowBuilderInternal<T> => {\n const id = options.name ?? crypto.randomUUID();\n const flowInputs: FlowInput[] = [];\n const nodes: Call[] = [];\n const nodeIds = new Set<string>();\n\n const inputHandles = {} as Record<string, InputHandle | ResourceInputHandle>;\n for (const [name, decl] of Object.entries(options.inputs)) {\n if (isResource(decl)) {\n if (decl.chainId !== chainId) {\n throw new Error(\n `Input \"${name}\" has chainId ${decl.chainId} but flow targets chain ${chainId}`,\n );\n }\n flowInputs.push({ name, resource: decl });\n inputHandles[name] = { _tag: 'input', inputName: name, resource: decl };\n } else {\n flowInputs.push({ name, type: decl });\n inputHandles[name] = { _tag: 'input', inputName: name };\n }\n }\n\n const call = <O extends OutputSpec>(\n nodeId: string,\n op: string,\n args: CallArgs,\n outputs: O,\n ): { readonly [K in keyof O & string]: OutputHandle<O[K]> } => {\n if (nodeIds.has(nodeId)) throw new Error(`Duplicate node id: \"${nodeId}\"`);\n nodeIds.add(nodeId);\n\n const bind: Record<string, Ref> = {};\n for (const [key, val] of Object.entries(args.bind)) {\n bind[key] = toRef(val);\n }\n\n const node: Call = {\n id: nodeId,\n op,\n bind,\n config: (args.config ?? {}) as Record<string, unknown>,\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n nodes.push(node);\n\n type Result = { readonly [K in keyof O & string]: OutputHandle<O[K]> };\n return new Proxy({} as Result, {\n get: (_target, prop): OutputHandle | undefined => {\n if (typeof prop !== 'string' || prop === 'then') return undefined;\n if (!(prop in outputs)) {\n throw new Error(\n `Op \"${op}\" has no output port \"${prop}\". Valid ports: ${Object.keys(\n outputs,\n ).join(', ')}`,\n );\n }\n return { _tag: 'output', nodeId, portName: prop };\n },\n });\n };\n\n const untypedOp = (\n nodeId: string,\n op: string,\n args: {\n readonly bind: Record<string, Ref>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly AppliedGuard[];\n },\n ): void => {\n if (nodeIds.has(nodeId)) throw new Error(`Duplicate node id: \"${nodeId}\"`);\n nodeIds.add(nodeId);\n const node: Call = {\n id: nodeId,\n op,\n bind: args.bind,\n config: args.config,\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n nodes.push(node);\n };\n\n const build = (): TypedFlow<T> => ({\n version: 1,\n id,\n chainId,\n inputs: [...flowInputs],\n nodes: [...nodes],\n });\n\n const context: ContextAccessor = {\n sender: ref<'address'>('context.sender'),\n executionAddress: ref<'address'>('context.executionAddress'),\n };\n\n return {\n context,\n inputs: inputHandles as InputHandles<T>,\n call,\n untypedOp,\n build,\n };\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/authoring/FlowBuilderCore.ts"],"sourcesContent":["import type {\n AppliedGuard,\n BindValue,\n Call,\n Flow,\n FlowInput,\n Resource,\n SolType,\n} from '@lifi/compose-spec';\n\nimport type {\n InputDecl,\n InputSchema,\n OutputKind,\n TypedGuard,\n TypedLiteral,\n TypedRef,\n} from '../types.js';\n\nimport type {\n InputHandle,\n OutputHandle,\n ResourceInputHandle,\n} from './handles.js';\nimport { toBindValue } from './handles.js';\nimport { ref } from './raw.js';\n\n/**\n * Arguments for calling an operation node in a flow.\n */\nexport interface CallArgs {\n /** Maps parameter names to input/output handles or raw `$ref` pointers. */\n readonly bind: Record<string, AnyBindable>;\n /** Optional operation-specific configuration. */\n readonly config?: object;\n /** Optional guards (e.g. slippage checks) applied to this operation's outputs. */\n readonly guards?: readonly TypedGuard[];\n}\n\n/**\n * Any value that can be bound to an operation parameter: an input handle, an\n * output handle from a previous operation, a typed `$ref` pointer, or a typed\n * literal.\n *\n * Use `raw.ref<T>()` to create a typed ref from a raw path string, and\n * `raw.literal<T>()` to bake in a constant.\n */\nexport type AnyBindable = InputHandle | OutputHandle | TypedRef | TypedLiteral;\n\n/**\n * Provides references to runtime context values available inside a flow.\n * Access via `builder.context`.\n */\nexport interface ContextAccessor {\n /** A `$ref` pointing to the transaction sender (signer) address. */\n readonly sender: TypedRef<'address'>;\n /** A `$ref` pointing to the on-chain execution (proxy) address. */\n readonly executionAddress: TypedRef<'address'>;\n}\n\ntype InputHandleOf<D extends InputDecl> = D extends Resource\n ? ResourceInputHandle\n : D extends SolType\n ? InputHandle<D>\n : InputHandle;\n\n/**\n * A mapped type that converts an input schema into typed handles.\n * Resource inputs produce {@link ResourceInputHandle}; scalar inputs produce {@link InputHandle}.\n *\n * @typeParam T - The flow's input schema.\n */\nexport type InputHandles<T extends InputSchema> = {\n readonly [K in keyof T]: InputHandleOf<T[K]>;\n};\n\n/**\n * Options for creating a new flow builder.\n *\n * @typeParam T - The flow's input schema.\n */\nexport interface FlowOptions<T extends InputSchema> {\n /** Optional human-readable name for the flow. Defaults to a random UUID. */\n readonly name?: string;\n /** Input declarations mapping names to resource or scalar types. */\n readonly inputs: T;\n}\n\n/**\n * A Flow document branded with its input schema for type-safe request building.\n *\n * The `__inputs` field is a phantom type — it exists only at compile time for\n * TypeScript inference and is never present at runtime.\n */\nexport type TypedFlow<T extends InputSchema = InputSchema> = Flow & {\n readonly __inputs?: T;\n};\n\n/**\n * Core flow builder interface providing low-level access to flow construction.\n *\n * For most use cases, prefer the {@link FlowBuilder} type returned by `sdk.flow()`\n * which adds generated operation methods and a `compile` method.\n *\n * @typeParam T - The flow's input schema.\n */\nexport interface FlowBuilderCore<T extends InputSchema = InputSchema> {\n /** Runtime context references (sender address, execution address). */\n readonly context: ContextAccessor;\n /** Typed handles for each declared flow input, used to bind inputs to operations. */\n readonly inputs: InputHandles<T>;\n /**\n * Appends an untyped operation node to the flow. This is an escape hatch\n * for operations not yet covered by the generated methods.\n *\n * Use `raw.ref<T>()` to reference this node's outputs in subsequent typed\n * operations.\n *\n * @param id - Unique node identifier within the flow.\n * @param op - The operation name (e.g. `\"custom.vaultQuery\"`).\n * @param args - Bind map, config, and optional guards for the node.\n * @throws Error if a node with the same `id` already exists in the flow.\n */\n readonly untypedOp: (\n id: string,\n op: string,\n args: {\n readonly bind: Record<string, BindValue>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly AppliedGuard[];\n },\n ) => void;\n /** Serialises the builder state into a {@link TypedFlow} document. */\n readonly build: () => TypedFlow<T>;\n}\n\n/** Maps output port names to their output kind for compile-time and runtime validation. */\nexport type OutputSpec = Record<string, OutputKind>;\n\n/** Internal interface exposed to bindGeneratedOps — not part of the public API. */\nexport interface FlowBuilderInternal<\n T extends InputSchema = InputSchema,\n> extends FlowBuilderCore<T> {\n readonly call: <O extends OutputSpec>(\n nodeId: string,\n op: string,\n args: CallArgs,\n outputs: O,\n ) => { readonly [K in keyof O & string]: OutputHandle<O[K]> };\n}\n\nconst isResource = (decl: InputDecl): decl is Resource =>\n typeof decl === 'object' && decl !== null && 'kind' in decl;\n\n/**\n * Creates a new flow builder targeting the given chain.\n *\n * @param chainId - The EVM chain ID (e.g. `1` for Ethereum mainnet).\n * @param options - Flow configuration including input declarations.\n * @returns A {@link FlowBuilderInternal} instance.\n * @internal\n */\nexport const createFlowBuilderCore = <T extends InputSchema>(\n chainId: number,\n options: FlowOptions<T>,\n): FlowBuilderInternal<T> => {\n const id = options.name ?? crypto.randomUUID();\n const flowInputs: FlowInput[] = [];\n const nodes: Call[] = [];\n const nodeIds = new Set<string>();\n\n const inputHandles = {} as Record<string, InputHandle | ResourceInputHandle>;\n for (const [name, decl] of Object.entries(options.inputs)) {\n if (isResource(decl)) {\n if (decl.chainId !== chainId) {\n throw new Error(\n `Input \"${name}\" has chainId ${decl.chainId} but flow targets chain ${chainId}`,\n );\n }\n flowInputs.push({ name, resource: decl });\n inputHandles[name] = { _tag: 'input', inputName: name, resource: decl };\n } else {\n flowInputs.push({ name, type: decl });\n inputHandles[name] = { _tag: 'input', inputName: name };\n }\n }\n\n const call = <O extends OutputSpec>(\n nodeId: string,\n op: string,\n args: CallArgs,\n outputs: O,\n ): { readonly [K in keyof O & string]: OutputHandle<O[K]> } => {\n if (nodeIds.has(nodeId)) throw new Error(`Duplicate node id: \"${nodeId}\"`);\n nodeIds.add(nodeId);\n\n const bind: Record<string, BindValue> = {};\n for (const [key, val] of Object.entries(args.bind)) {\n bind[key] = toBindValue(val);\n }\n\n const node: Call = {\n id: nodeId,\n op,\n bind,\n config: (args.config ?? {}) as Record<string, unknown>,\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n nodes.push(node);\n\n type Result = { readonly [K in keyof O & string]: OutputHandle<O[K]> };\n return new Proxy({} as Result, {\n get: (_target, prop): OutputHandle | undefined => {\n if (typeof prop !== 'string' || prop === 'then') return undefined;\n if (!(prop in outputs)) {\n throw new Error(\n `Op \"${op}\" has no output port \"${prop}\". Valid ports: ${Object.keys(\n outputs,\n ).join(', ')}`,\n );\n }\n return { _tag: 'output', nodeId, portName: prop };\n },\n });\n };\n\n const untypedOp = (\n nodeId: string,\n op: string,\n args: {\n readonly bind: Record<string, BindValue>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly AppliedGuard[];\n },\n ): void => {\n if (nodeIds.has(nodeId)) throw new Error(`Duplicate node id: \"${nodeId}\"`);\n nodeIds.add(nodeId);\n const node: Call = {\n id: nodeId,\n op,\n bind: args.bind,\n config: args.config,\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n nodes.push(node);\n };\n\n const build = (): TypedFlow<T> => ({\n version: 1,\n id,\n chainId,\n inputs: [...flowInputs],\n nodes: [...nodes],\n });\n\n const context: ContextAccessor = {\n sender: ref<'address'>('context.sender'),\n executionAddress: ref<'address'>('context.executionAddress'),\n };\n\n return {\n context,\n inputs: inputHandles as InputHandles<T>,\n call,\n untypedOp,\n build,\n };\n};\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
- import { Resource, SolType, Ref, AppliedGuard, Flow } from '@lifi/compose-spec';
2
- import { TypedRef, InputSchema, InputDecl, TypedGuard, OutputKind } from '../types.cjs';
1
+ import { Resource, SolType, BindValue, AppliedGuard, Flow } from '@lifi/compose-spec';
2
+ import { TypedRef, TypedLiteral, InputSchema, InputDecl, TypedGuard, OutputKind } from '../types.cjs';
3
3
  import { InputHandle, OutputHandle, ResourceInputHandle } from './handles.cjs';
4
4
 
5
5
  /**
@@ -14,12 +14,14 @@ interface CallArgs {
14
14
  readonly guards?: readonly TypedGuard[];
15
15
  }
16
16
  /**
17
- * Any value that can be bound to an operation parameter: an input handle,
18
- * an output handle from a previous operation, or a typed `$ref` pointer.
17
+ * Any value that can be bound to an operation parameter: an input handle, an
18
+ * output handle from a previous operation, a typed `$ref` pointer, or a typed
19
+ * literal.
19
20
  *
20
- * Use `raw.ref<T>()` to create a typed ref from a raw path string.
21
+ * Use `raw.ref<T>()` to create a typed ref from a raw path string, and
22
+ * `raw.literal<T>()` to bake in a constant.
21
23
  */
22
- type AnyBindable = InputHandle | OutputHandle | TypedRef;
24
+ type AnyBindable = InputHandle | OutputHandle | TypedRef | TypedLiteral;
23
25
  /**
24
26
  * Provides references to runtime context values available inside a flow.
25
27
  * Access via `builder.context`.
@@ -86,7 +88,7 @@ interface FlowBuilderCore<T extends InputSchema = InputSchema> {
86
88
  * @throws Error if a node with the same `id` already exists in the flow.
87
89
  */
88
90
  readonly untypedOp: (id: string, op: string, args: {
89
- readonly bind: Record<string, Ref>;
91
+ readonly bind: Record<string, BindValue>;
90
92
  readonly config: Record<string, unknown>;
91
93
  readonly guards?: readonly AppliedGuard[];
92
94
  }) => void;
@@ -1,5 +1,5 @@
1
- import { Resource, SolType, Ref, AppliedGuard, Flow } from '@lifi/compose-spec';
2
- import { TypedRef, InputSchema, InputDecl, TypedGuard, OutputKind } from '../types.js';
1
+ import { Resource, SolType, BindValue, AppliedGuard, Flow } from '@lifi/compose-spec';
2
+ import { TypedRef, TypedLiteral, InputSchema, InputDecl, TypedGuard, OutputKind } from '../types.js';
3
3
  import { InputHandle, OutputHandle, ResourceInputHandle } from './handles.js';
4
4
 
5
5
  /**
@@ -14,12 +14,14 @@ interface CallArgs {
14
14
  readonly guards?: readonly TypedGuard[];
15
15
  }
16
16
  /**
17
- * Any value that can be bound to an operation parameter: an input handle,
18
- * an output handle from a previous operation, or a typed `$ref` pointer.
17
+ * Any value that can be bound to an operation parameter: an input handle, an
18
+ * output handle from a previous operation, a typed `$ref` pointer, or a typed
19
+ * literal.
19
20
  *
20
- * Use `raw.ref<T>()` to create a typed ref from a raw path string.
21
+ * Use `raw.ref<T>()` to create a typed ref from a raw path string, and
22
+ * `raw.literal<T>()` to bake in a constant.
21
23
  */
22
- type AnyBindable = InputHandle | OutputHandle | TypedRef;
24
+ type AnyBindable = InputHandle | OutputHandle | TypedRef | TypedLiteral;
23
25
  /**
24
26
  * Provides references to runtime context values available inside a flow.
25
27
  * Access via `builder.context`.
@@ -86,7 +88,7 @@ interface FlowBuilderCore<T extends InputSchema = InputSchema> {
86
88
  * @throws Error if a node with the same `id` already exists in the flow.
87
89
  */
88
90
  readonly untypedOp: (id: string, op: string, args: {
89
- readonly bind: Record<string, Ref>;
91
+ readonly bind: Record<string, BindValue>;
90
92
  readonly config: Record<string, unknown>;
91
93
  readonly guards?: readonly AppliedGuard[];
92
94
  }) => void;
@@ -1,9 +1,5 @@
1
- import { handleToRef } from "./handles.js";
1
+ import { toBindValue } from "./handles.js";
2
2
  import { ref } from "./raw.js";
3
- const toRef = (bindable) => {
4
- if ("_tag" in bindable) return handleToRef(bindable);
5
- return bindable;
6
- };
7
3
  const isResource = (decl) => typeof decl === "object" && decl !== null && "kind" in decl;
8
4
  const createFlowBuilderCore = (chainId, options) => {
9
5
  const id = options.name ?? crypto.randomUUID();
@@ -30,7 +26,7 @@ const createFlowBuilderCore = (chainId, options) => {
30
26
  nodeIds.add(nodeId);
31
27
  const bind = {};
32
28
  for (const [key, val] of Object.entries(args.bind)) {
33
- bind[key] = toRef(val);
29
+ bind[key] = toBindValue(val);
34
30
  }
35
31
  const node = {
36
32
  id: nodeId,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/authoring/FlowBuilderCore.ts"],"sourcesContent":["import type {\n AppliedGuard,\n Call,\n Flow,\n FlowInput,\n Ref,\n Resource,\n SolType,\n} from '@lifi/compose-spec';\n\nimport type {\n InputDecl,\n InputSchema,\n OutputKind,\n TypedGuard,\n TypedRef,\n} from '../types.js';\n\nimport type {\n InputHandle,\n OutputHandle,\n ResourceInputHandle,\n} from './handles.js';\nimport { handleToRef } from './handles.js';\nimport { ref } from './raw.js';\n\n/**\n * Arguments for calling an operation node in a flow.\n */\nexport interface CallArgs {\n /** Maps parameter names to input/output handles or raw `$ref` pointers. */\n readonly bind: Record<string, AnyBindable>;\n /** Optional operation-specific configuration. */\n readonly config?: object;\n /** Optional guards (e.g. slippage checks) applied to this operation's outputs. */\n readonly guards?: readonly TypedGuard[];\n}\n\n/**\n * Any value that can be bound to an operation parameter: an input handle,\n * an output handle from a previous operation, or a typed `$ref` pointer.\n *\n * Use `raw.ref<T>()` to create a typed ref from a raw path string.\n */\nexport type AnyBindable = InputHandle | OutputHandle | TypedRef;\n\nconst toRef = (bindable: AnyBindable): Ref => {\n if ('_tag' in bindable) return handleToRef(bindable);\n return bindable;\n};\n\n/**\n * Provides references to runtime context values available inside a flow.\n * Access via `builder.context`.\n */\nexport interface ContextAccessor {\n /** A `$ref` pointing to the transaction sender (signer) address. */\n readonly sender: TypedRef<'address'>;\n /** A `$ref` pointing to the on-chain execution (proxy) address. */\n readonly executionAddress: TypedRef<'address'>;\n}\n\ntype InputHandleOf<D extends InputDecl> = D extends Resource\n ? ResourceInputHandle\n : D extends SolType\n ? InputHandle<D>\n : InputHandle;\n\n/**\n * A mapped type that converts an input schema into typed handles.\n * Resource inputs produce {@link ResourceInputHandle}; scalar inputs produce {@link InputHandle}.\n *\n * @typeParam T - The flow's input schema.\n */\nexport type InputHandles<T extends InputSchema> = {\n readonly [K in keyof T]: InputHandleOf<T[K]>;\n};\n\n/**\n * Options for creating a new flow builder.\n *\n * @typeParam T - The flow's input schema.\n */\nexport interface FlowOptions<T extends InputSchema> {\n /** Optional human-readable name for the flow. Defaults to a random UUID. */\n readonly name?: string;\n /** Input declarations mapping names to resource or scalar types. */\n readonly inputs: T;\n}\n\n/**\n * A Flow document branded with its input schema for type-safe request building.\n *\n * The `__inputs` field is a phantom type — it exists only at compile time for\n * TypeScript inference and is never present at runtime.\n */\nexport type TypedFlow<T extends InputSchema = InputSchema> = Flow & {\n readonly __inputs?: T;\n};\n\n/**\n * Core flow builder interface providing low-level access to flow construction.\n *\n * For most use cases, prefer the {@link FlowBuilder} type returned by `sdk.flow()`\n * which adds generated operation methods and a `compile` method.\n *\n * @typeParam T - The flow's input schema.\n */\nexport interface FlowBuilderCore<T extends InputSchema = InputSchema> {\n /** Runtime context references (sender address, execution address). */\n readonly context: ContextAccessor;\n /** Typed handles for each declared flow input, used to bind inputs to operations. */\n readonly inputs: InputHandles<T>;\n /**\n * Appends an untyped operation node to the flow. This is an escape hatch\n * for operations not yet covered by the generated methods.\n *\n * Use `raw.ref<T>()` to reference this node's outputs in subsequent typed\n * operations.\n *\n * @param id - Unique node identifier within the flow.\n * @param op - The operation name (e.g. `\"custom.vaultQuery\"`).\n * @param args - Bind map, config, and optional guards for the node.\n * @throws Error if a node with the same `id` already exists in the flow.\n */\n readonly untypedOp: (\n id: string,\n op: string,\n args: {\n readonly bind: Record<string, Ref>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly AppliedGuard[];\n },\n ) => void;\n /** Serialises the builder state into a {@link TypedFlow} document. */\n readonly build: () => TypedFlow<T>;\n}\n\n/** Maps output port names to their output kind for compile-time and runtime validation. */\nexport type OutputSpec = Record<string, OutputKind>;\n\n/** Internal interface exposed to bindGeneratedOps — not part of the public API. */\nexport interface FlowBuilderInternal<\n T extends InputSchema = InputSchema,\n> extends FlowBuilderCore<T> {\n readonly call: <O extends OutputSpec>(\n nodeId: string,\n op: string,\n args: CallArgs,\n outputs: O,\n ) => { readonly [K in keyof O & string]: OutputHandle<O[K]> };\n}\n\nconst isResource = (decl: InputDecl): decl is Resource =>\n typeof decl === 'object' && decl !== null && 'kind' in decl;\n\n/**\n * Creates a new flow builder targeting the given chain.\n *\n * @param chainId - The EVM chain ID (e.g. `1` for Ethereum mainnet).\n * @param options - Flow configuration including input declarations.\n * @returns A {@link FlowBuilderInternal} instance.\n * @internal\n */\nexport const createFlowBuilderCore = <T extends InputSchema>(\n chainId: number,\n options: FlowOptions<T>,\n): FlowBuilderInternal<T> => {\n const id = options.name ?? crypto.randomUUID();\n const flowInputs: FlowInput[] = [];\n const nodes: Call[] = [];\n const nodeIds = new Set<string>();\n\n const inputHandles = {} as Record<string, InputHandle | ResourceInputHandle>;\n for (const [name, decl] of Object.entries(options.inputs)) {\n if (isResource(decl)) {\n if (decl.chainId !== chainId) {\n throw new Error(\n `Input \"${name}\" has chainId ${decl.chainId} but flow targets chain ${chainId}`,\n );\n }\n flowInputs.push({ name, resource: decl });\n inputHandles[name] = { _tag: 'input', inputName: name, resource: decl };\n } else {\n flowInputs.push({ name, type: decl });\n inputHandles[name] = { _tag: 'input', inputName: name };\n }\n }\n\n const call = <O extends OutputSpec>(\n nodeId: string,\n op: string,\n args: CallArgs,\n outputs: O,\n ): { readonly [K in keyof O & string]: OutputHandle<O[K]> } => {\n if (nodeIds.has(nodeId)) throw new Error(`Duplicate node id: \"${nodeId}\"`);\n nodeIds.add(nodeId);\n\n const bind: Record<string, Ref> = {};\n for (const [key, val] of Object.entries(args.bind)) {\n bind[key] = toRef(val);\n }\n\n const node: Call = {\n id: nodeId,\n op,\n bind,\n config: (args.config ?? {}) as Record<string, unknown>,\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n nodes.push(node);\n\n type Result = { readonly [K in keyof O & string]: OutputHandle<O[K]> };\n return new Proxy({} as Result, {\n get: (_target, prop): OutputHandle | undefined => {\n if (typeof prop !== 'string' || prop === 'then') return undefined;\n if (!(prop in outputs)) {\n throw new Error(\n `Op \"${op}\" has no output port \"${prop}\". Valid ports: ${Object.keys(\n outputs,\n ).join(', ')}`,\n );\n }\n return { _tag: 'output', nodeId, portName: prop };\n },\n });\n };\n\n const untypedOp = (\n nodeId: string,\n op: string,\n args: {\n readonly bind: Record<string, Ref>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly AppliedGuard[];\n },\n ): void => {\n if (nodeIds.has(nodeId)) throw new Error(`Duplicate node id: \"${nodeId}\"`);\n nodeIds.add(nodeId);\n const node: Call = {\n id: nodeId,\n op,\n bind: args.bind,\n config: args.config,\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n nodes.push(node);\n };\n\n const build = (): TypedFlow<T> => ({\n version: 1,\n id,\n chainId,\n inputs: [...flowInputs],\n nodes: [...nodes],\n });\n\n const context: ContextAccessor = {\n sender: ref<'address'>('context.sender'),\n executionAddress: ref<'address'>('context.executionAddress'),\n };\n\n return {\n context,\n inputs: inputHandles as InputHandles<T>,\n call,\n untypedOp,\n build,\n };\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/authoring/FlowBuilderCore.ts"],"sourcesContent":["import type {\n AppliedGuard,\n BindValue,\n Call,\n Flow,\n FlowInput,\n Resource,\n SolType,\n} from '@lifi/compose-spec';\n\nimport type {\n InputDecl,\n InputSchema,\n OutputKind,\n TypedGuard,\n TypedLiteral,\n TypedRef,\n} from '../types.js';\n\nimport type {\n InputHandle,\n OutputHandle,\n ResourceInputHandle,\n} from './handles.js';\nimport { toBindValue } from './handles.js';\nimport { ref } from './raw.js';\n\n/**\n * Arguments for calling an operation node in a flow.\n */\nexport interface CallArgs {\n /** Maps parameter names to input/output handles or raw `$ref` pointers. */\n readonly bind: Record<string, AnyBindable>;\n /** Optional operation-specific configuration. */\n readonly config?: object;\n /** Optional guards (e.g. slippage checks) applied to this operation's outputs. */\n readonly guards?: readonly TypedGuard[];\n}\n\n/**\n * Any value that can be bound to an operation parameter: an input handle, an\n * output handle from a previous operation, a typed `$ref` pointer, or a typed\n * literal.\n *\n * Use `raw.ref<T>()` to create a typed ref from a raw path string, and\n * `raw.literal<T>()` to bake in a constant.\n */\nexport type AnyBindable = InputHandle | OutputHandle | TypedRef | TypedLiteral;\n\n/**\n * Provides references to runtime context values available inside a flow.\n * Access via `builder.context`.\n */\nexport interface ContextAccessor {\n /** A `$ref` pointing to the transaction sender (signer) address. */\n readonly sender: TypedRef<'address'>;\n /** A `$ref` pointing to the on-chain execution (proxy) address. */\n readonly executionAddress: TypedRef<'address'>;\n}\n\ntype InputHandleOf<D extends InputDecl> = D extends Resource\n ? ResourceInputHandle\n : D extends SolType\n ? InputHandle<D>\n : InputHandle;\n\n/**\n * A mapped type that converts an input schema into typed handles.\n * Resource inputs produce {@link ResourceInputHandle}; scalar inputs produce {@link InputHandle}.\n *\n * @typeParam T - The flow's input schema.\n */\nexport type InputHandles<T extends InputSchema> = {\n readonly [K in keyof T]: InputHandleOf<T[K]>;\n};\n\n/**\n * Options for creating a new flow builder.\n *\n * @typeParam T - The flow's input schema.\n */\nexport interface FlowOptions<T extends InputSchema> {\n /** Optional human-readable name for the flow. Defaults to a random UUID. */\n readonly name?: string;\n /** Input declarations mapping names to resource or scalar types. */\n readonly inputs: T;\n}\n\n/**\n * A Flow document branded with its input schema for type-safe request building.\n *\n * The `__inputs` field is a phantom type — it exists only at compile time for\n * TypeScript inference and is never present at runtime.\n */\nexport type TypedFlow<T extends InputSchema = InputSchema> = Flow & {\n readonly __inputs?: T;\n};\n\n/**\n * Core flow builder interface providing low-level access to flow construction.\n *\n * For most use cases, prefer the {@link FlowBuilder} type returned by `sdk.flow()`\n * which adds generated operation methods and a `compile` method.\n *\n * @typeParam T - The flow's input schema.\n */\nexport interface FlowBuilderCore<T extends InputSchema = InputSchema> {\n /** Runtime context references (sender address, execution address). */\n readonly context: ContextAccessor;\n /** Typed handles for each declared flow input, used to bind inputs to operations. */\n readonly inputs: InputHandles<T>;\n /**\n * Appends an untyped operation node to the flow. This is an escape hatch\n * for operations not yet covered by the generated methods.\n *\n * Use `raw.ref<T>()` to reference this node's outputs in subsequent typed\n * operations.\n *\n * @param id - Unique node identifier within the flow.\n * @param op - The operation name (e.g. `\"custom.vaultQuery\"`).\n * @param args - Bind map, config, and optional guards for the node.\n * @throws Error if a node with the same `id` already exists in the flow.\n */\n readonly untypedOp: (\n id: string,\n op: string,\n args: {\n readonly bind: Record<string, BindValue>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly AppliedGuard[];\n },\n ) => void;\n /** Serialises the builder state into a {@link TypedFlow} document. */\n readonly build: () => TypedFlow<T>;\n}\n\n/** Maps output port names to their output kind for compile-time and runtime validation. */\nexport type OutputSpec = Record<string, OutputKind>;\n\n/** Internal interface exposed to bindGeneratedOps — not part of the public API. */\nexport interface FlowBuilderInternal<\n T extends InputSchema = InputSchema,\n> extends FlowBuilderCore<T> {\n readonly call: <O extends OutputSpec>(\n nodeId: string,\n op: string,\n args: CallArgs,\n outputs: O,\n ) => { readonly [K in keyof O & string]: OutputHandle<O[K]> };\n}\n\nconst isResource = (decl: InputDecl): decl is Resource =>\n typeof decl === 'object' && decl !== null && 'kind' in decl;\n\n/**\n * Creates a new flow builder targeting the given chain.\n *\n * @param chainId - The EVM chain ID (e.g. `1` for Ethereum mainnet).\n * @param options - Flow configuration including input declarations.\n * @returns A {@link FlowBuilderInternal} instance.\n * @internal\n */\nexport const createFlowBuilderCore = <T extends InputSchema>(\n chainId: number,\n options: FlowOptions<T>,\n): FlowBuilderInternal<T> => {\n const id = options.name ?? crypto.randomUUID();\n const flowInputs: FlowInput[] = [];\n const nodes: Call[] = [];\n const nodeIds = new Set<string>();\n\n const inputHandles = {} as Record<string, InputHandle | ResourceInputHandle>;\n for (const [name, decl] of Object.entries(options.inputs)) {\n if (isResource(decl)) {\n if (decl.chainId !== chainId) {\n throw new Error(\n `Input \"${name}\" has chainId ${decl.chainId} but flow targets chain ${chainId}`,\n );\n }\n flowInputs.push({ name, resource: decl });\n inputHandles[name] = { _tag: 'input', inputName: name, resource: decl };\n } else {\n flowInputs.push({ name, type: decl });\n inputHandles[name] = { _tag: 'input', inputName: name };\n }\n }\n\n const call = <O extends OutputSpec>(\n nodeId: string,\n op: string,\n args: CallArgs,\n outputs: O,\n ): { readonly [K in keyof O & string]: OutputHandle<O[K]> } => {\n if (nodeIds.has(nodeId)) throw new Error(`Duplicate node id: \"${nodeId}\"`);\n nodeIds.add(nodeId);\n\n const bind: Record<string, BindValue> = {};\n for (const [key, val] of Object.entries(args.bind)) {\n bind[key] = toBindValue(val);\n }\n\n const node: Call = {\n id: nodeId,\n op,\n bind,\n config: (args.config ?? {}) as Record<string, unknown>,\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n nodes.push(node);\n\n type Result = { readonly [K in keyof O & string]: OutputHandle<O[K]> };\n return new Proxy({} as Result, {\n get: (_target, prop): OutputHandle | undefined => {\n if (typeof prop !== 'string' || prop === 'then') return undefined;\n if (!(prop in outputs)) {\n throw new Error(\n `Op \"${op}\" has no output port \"${prop}\". Valid ports: ${Object.keys(\n outputs,\n ).join(', ')}`,\n );\n }\n return { _tag: 'output', nodeId, portName: prop };\n },\n });\n };\n\n const untypedOp = (\n nodeId: string,\n op: string,\n args: {\n readonly bind: Record<string, BindValue>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly AppliedGuard[];\n },\n ): void => {\n if (nodeIds.has(nodeId)) throw new Error(`Duplicate node id: \"${nodeId}\"`);\n nodeIds.add(nodeId);\n const node: Call = {\n id: nodeId,\n op,\n bind: args.bind,\n config: args.config,\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n nodes.push(node);\n };\n\n const build = (): TypedFlow<T> => ({\n version: 1,\n id,\n chainId,\n inputs: [...flowInputs],\n nodes: [...nodes],\n });\n\n const context: ContextAccessor = {\n sender: ref<'address'>('context.sender'),\n executionAddress: ref<'address'>('context.executionAddress'),\n };\n\n return {\n context,\n inputs: inputHandles as InputHandles<T>,\n call,\n untypedOp,\n build,\n };\n};\n"],"mappings":"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","names":[]}
@@ -20,7 +20,8 @@ var handles_exports = {};
20
20
  __export(handles_exports, {
21
21
  handleToRef: () => handleToRef,
22
22
  isInputHandle: () => isInputHandle,
23
- isOutputHandle: () => isOutputHandle
23
+ isOutputHandle: () => isOutputHandle,
24
+ toBindValue: () => toBindValue
24
25
  });
25
26
  module.exports = __toCommonJS(handles_exports);
26
27
  const isInputHandle = (h) => h._tag === "input";
@@ -34,10 +35,12 @@ const handleToRef = (h) => {
34
35
  if (!h.portName) throw new Error("OutputHandle has empty portName");
35
36
  return { $ref: `${h.nodeId}.${h.portName}` };
36
37
  };
38
+ const toBindValue = (bindable) => "_tag" in bindable ? handleToRef(bindable) : bindable;
37
39
  // Annotate the CommonJS export names for ESM import in node:
38
40
  0 && (module.exports = {
39
41
  handleToRef,
40
42
  isInputHandle,
41
- isOutputHandle
43
+ isOutputHandle,
44
+ toBindValue
42
45
  });
43
46
  //# sourceMappingURL=handles.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/authoring/handles.ts"],"sourcesContent":["import type { Resource, StaticSolType } from '@lifi/compose-spec';\n\nimport type { OutputKind, TypedRef } from '../types.js';\n\n/**\n * A handle referencing a flow input declared in the flow's input schema.\n * The phantom `__outputKind` field carries the type parameter through\n * TypeScript's structural type system without affecting runtime shape.\n */\nexport interface InputHandle<T extends OutputKind = OutputKind> {\n readonly _tag: 'input';\n readonly inputName: string;\n readonly __outputKind?: T;\n}\n\n/**\n * An input handle for a resource (token) input, carrying the resource\n * declaration alongside the reference.\n */\nexport interface ResourceInputHandle extends InputHandle<'resource'> {\n readonly resource: Resource;\n}\n\n/**\n * A handle referencing an output port of a previously declared operation node.\n * The phantom `__outputKind` field carries the type parameter through\n * TypeScript's structural type system without affecting runtime shape.\n */\nexport interface OutputHandle<T extends OutputKind = OutputKind> {\n readonly _tag: 'output';\n readonly nodeId: string;\n readonly portName: string;\n readonly __outputKind?: T;\n}\n\n/** Discriminated union of all handle types that can be bound to operation arguments. */\nexport type AnyHandle = InputHandle | OutputHandle;\n\n/** Type guard that narrows an {@link AnyHandle} to an {@link InputHandle}. */\nexport const isInputHandle = (h: AnyHandle): h is InputHandle =>\n h._tag === 'input';\n\n/** Type guard that narrows an {@link AnyHandle} to an {@link OutputHandle}. */\nexport const isOutputHandle = (h: AnyHandle): h is OutputHandle =>\n h._tag === 'output';\n\n/**\n * Converts a handle to a JSON `$ref` pointer used in the flow document.\n * Input handles become `\"input.<name>\"`; output handles become `\"<nodeId>.<port>\"`.\n */\nexport const handleToRef = (h: AnyHandle): { $ref: string } => {\n if (h._tag === 'input') {\n if (!h.inputName) throw new Error('InputHandle has empty inputName');\n return { $ref: `input.${h.inputName}` };\n }\n if (!h.nodeId) throw new Error('OutputHandle has empty nodeId');\n if (!h.portName) throw new Error('OutputHandle has empty portName');\n return { $ref: `${h.nodeId}.${h.portName}` };\n};\n\n/**\n * For a bind slot expecting type T, widen the set of accepted handle types:\n * - `'bytes32'` is the raw 32-byte word type: accept any static-32-byte handle\n * (`uint256`/`uintN`/`address`/`bool`/`bytes32`) plus `'resource'` (a uint256\n * amount). At the IR1 boundary every value is a type-erased word, so any\n * static-32-byte handle is a sound bind for a `bytes32` payload port.\n * - `'uint256'` keeps its existing widening to also accept `'resource'`-tagged\n * handles (resources are uint256 amounts).\n * - every other type keeps exact-SolType matching.\n */\ntype SlotAssignable<T extends OutputKind> = T extends 'bytes32'\n ? StaticSolType | 'resource'\n : T extends 'uint256'\n ? T | 'resource'\n : T;\n\n/**\n * The set of values accepted by a typed bind slot.\n * - `OutputHandle<T>` / `InputHandle<T>` (same type)\n * - For `'uint256'` slots, also `'resource'`-tagged handles\n * - For `'bytes32'` slots, any static-32-byte handle (and `'resource'`)\n * - `TypedRef<T>` (typed context refs, subject to `SlotAssignable`)\n *\n * Plain `{ $ref: '...' }` objects are **not** accepted — use `raw.ref<T>()`\n * to create a typed ref when you need to reference a raw path.\n */\nexport type Bindable<T extends OutputKind> =\n | OutputHandle<SlotAssignable<T>>\n | InputHandle<SlotAssignable<T>>\n | TypedRef<SlotAssignable<T>>;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuCO,MAAM,gBAAgB,CAAC,MAC5B,EAAE,SAAS;AAGN,MAAM,iBAAiB,CAAC,MAC7B,EAAE,SAAS;AAMN,MAAM,cAAc,CAAC,MAAmC;AAC7D,MAAI,EAAE,SAAS,SAAS;AACtB,QAAI,CAAC,EAAE,UAAW,OAAM,IAAI,MAAM,iCAAiC;AACnE,WAAO,EAAE,MAAM,SAAS,EAAE,SAAS,GAAG;AAAA,EACxC;AACA,MAAI,CAAC,EAAE,OAAQ,OAAM,IAAI,MAAM,+BAA+B;AAC9D,MAAI,CAAC,EAAE,SAAU,OAAM,IAAI,MAAM,iCAAiC;AAClE,SAAO,EAAE,MAAM,GAAG,EAAE,MAAM,IAAI,EAAE,QAAQ,GAAG;AAC7C;","names":[]}
1
+ {"version":3,"sources":["../../src/authoring/handles.ts"],"sourcesContent":["import type {\n BindValue,\n Resource,\n SolType,\n StaticSolType,\n} from '@lifi/compose-spec';\n\nimport type { OutputKind, TypedLiteral, TypedRef } from '../types.js';\n\n/**\n * A handle referencing a flow input declared in the flow's input schema.\n * The phantom `__outputKind` field carries the type parameter through\n * TypeScript's structural type system without affecting runtime shape.\n */\nexport interface InputHandle<T extends OutputKind = OutputKind> {\n readonly _tag: 'input';\n readonly inputName: string;\n readonly __outputKind?: T;\n}\n\n/**\n * An input handle for a resource (token) input, carrying the resource\n * declaration alongside the reference.\n */\nexport interface ResourceInputHandle extends InputHandle<'resource'> {\n readonly resource: Resource;\n}\n\n/**\n * A handle referencing an output port of a previously declared operation node.\n * The phantom `__outputKind` field carries the type parameter through\n * TypeScript's structural type system without affecting runtime shape.\n */\nexport interface OutputHandle<T extends OutputKind = OutputKind> {\n readonly _tag: 'output';\n readonly nodeId: string;\n readonly portName: string;\n readonly __outputKind?: T;\n}\n\n/** Discriminated union of all handle types that can be bound to operation arguments. */\nexport type AnyHandle = InputHandle | OutputHandle;\n\n/** Type guard that narrows an {@link AnyHandle} to an {@link InputHandle}. */\nexport const isInputHandle = (h: AnyHandle): h is InputHandle =>\n h._tag === 'input';\n\n/** Type guard that narrows an {@link AnyHandle} to an {@link OutputHandle}. */\nexport const isOutputHandle = (h: AnyHandle): h is OutputHandle =>\n h._tag === 'output';\n\n/**\n * Converts a handle to a JSON `$ref` pointer used in the flow document.\n * Input handles become `\"input.<name>\"`; output handles become `\"<nodeId>.<port>\"`.\n */\nexport const handleToRef = (h: AnyHandle): { $ref: string } => {\n if (h._tag === 'input') {\n if (!h.inputName) throw new Error('InputHandle has empty inputName');\n return { $ref: `input.${h.inputName}` };\n }\n if (!h.nodeId) throw new Error('OutputHandle has empty nodeId');\n if (!h.portName) throw new Error('OutputHandle has empty portName');\n return { $ref: `${h.nodeId}.${h.portName}` };\n};\n\n/**\n * For a bind slot expecting type T, widen the set of accepted handle types:\n * - `'bytes32'` is the raw 32-byte word type: accept any static-32-byte handle\n * (`uint256`/`uintN`/`address`/`bool`/`bytes32`) plus `'resource'` (a uint256\n * amount). At the IR1 boundary every value is a type-erased word, so any\n * static-32-byte handle is a sound bind for a `bytes32` payload port.\n * - `'uint256'` keeps its existing widening to also accept `'resource'`-tagged\n * handles (resources are uint256 amounts).\n * - every other type keeps exact-SolType matching.\n */\ntype SlotAssignable<T extends OutputKind> = T extends 'bytes32'\n ? StaticSolType | 'resource'\n : T extends 'uint256'\n ? T | 'resource'\n : T;\n\n/**\n * The values accepted by a typed slot that must resolve to a `$ref`:\n * - `OutputHandle<T>` / `InputHandle<T>` (same type)\n * - For `'uint256'` slots, also `'resource'`-tagged handles\n * - For `'bytes32'` slots, any static-32-byte handle (and `'resource'`)\n * - `TypedRef<T>` (typed context refs, subject to `SlotAssignable`)\n *\n * Plain `{ $ref: '...' }` objects are **not** accepted - use `raw.ref<T>()`\n * to create a typed ref when you need to reference a raw path.\n */\nexport type RefBindable<T extends OutputKind> =\n | OutputHandle<SlotAssignable<T>>\n | InputHandle<SlotAssignable<T>>\n | TypedRef<SlotAssignable<T>>;\n\n/**\n * Any ref-resolving bindable, with no per-slot type constraint.\n */\nexport type AnyRefBindable = InputHandle | OutputHandle | TypedRef;\n\n/**\n * The set of values accepted by a typed operation bind slot: everything\n * {@link RefBindable} accepts, plus a {@link TypedLiteral} constant baked\n * into the flow document. A `'resource'` slot rejects a literal: a token\n * resource is not a scalar constant.\n *\n * Use `raw.literal<T>()` to create one.\n */\nexport type Bindable<T extends OutputKind> =\n RefBindable<T> | LiteralBindable<SlotAssignable<T>>;\n\n// Distributes over the slot's assignable kinds and keeps the scalar ones: a\n// `'resource'` slot has no scalar arm, so its literal arm collapses to\n// `never` and a literal is rejected outright.\ntype LiteralBindable<K extends OutputKind> = K extends SolType\n ? TypedLiteral<K>\n : never;\n\n/**\n * Converts a bindable to the wire `bind` value it denotes: handles become\n * `$ref` pointers, refs and literals pass through as-is.\n */\nexport const toBindValue = (\n bindable: InputHandle | OutputHandle | TypedRef | TypedLiteral,\n): BindValue => ('_tag' in bindable ? handleToRef(bindable) : bindable);\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4CO,MAAM,gBAAgB,CAAC,MAC5B,EAAE,SAAS;AAGN,MAAM,iBAAiB,CAAC,MAC7B,EAAE,SAAS;AAMN,MAAM,cAAc,CAAC,MAAmC;AAC7D,MAAI,EAAE,SAAS,SAAS;AACtB,QAAI,CAAC,EAAE,UAAW,OAAM,IAAI,MAAM,iCAAiC;AACnE,WAAO,EAAE,MAAM,SAAS,EAAE,SAAS,GAAG;AAAA,EACxC;AACA,MAAI,CAAC,EAAE,OAAQ,OAAM,IAAI,MAAM,+BAA+B;AAC9D,MAAI,CAAC,EAAE,SAAU,OAAM,IAAI,MAAM,iCAAiC;AAClE,SAAO,EAAE,MAAM,GAAG,EAAE,MAAM,IAAI,EAAE,QAAQ,GAAG;AAC7C;AA4DO,MAAM,cAAc,CACzB,aACe,UAAU,WAAW,YAAY,QAAQ,IAAI;","names":[]}
@@ -1,5 +1,5 @@
1
- import { StaticSolType, Resource } from '@lifi/compose-spec';
2
- import { OutputKind, TypedRef } from '../types.cjs';
1
+ import { StaticSolType, SolType, Resource, BindValue } from '@lifi/compose-spec';
2
+ import { OutputKind, TypedRef, TypedLiteral } from '../types.cjs';
3
3
 
4
4
  /**
5
5
  * A handle referencing a flow input declared in the flow's input schema.
@@ -54,15 +54,34 @@ declare const handleToRef: (h: AnyHandle) => {
54
54
  */
55
55
  type SlotAssignable<T extends OutputKind> = T extends 'bytes32' ? StaticSolType | 'resource' : T extends 'uint256' ? T | 'resource' : T;
56
56
  /**
57
- * The set of values accepted by a typed bind slot.
57
+ * The values accepted by a typed slot that must resolve to a `$ref`:
58
58
  * - `OutputHandle<T>` / `InputHandle<T>` (same type)
59
59
  * - For `'uint256'` slots, also `'resource'`-tagged handles
60
60
  * - For `'bytes32'` slots, any static-32-byte handle (and `'resource'`)
61
61
  * - `TypedRef<T>` (typed context refs, subject to `SlotAssignable`)
62
62
  *
63
- * Plain `{ $ref: '...' }` objects are **not** accepted — use `raw.ref<T>()`
63
+ * Plain `{ $ref: '...' }` objects are **not** accepted - use `raw.ref<T>()`
64
64
  * to create a typed ref when you need to reference a raw path.
65
65
  */
66
- type Bindable<T extends OutputKind> = OutputHandle<SlotAssignable<T>> | InputHandle<SlotAssignable<T>> | TypedRef<SlotAssignable<T>>;
66
+ type RefBindable<T extends OutputKind> = OutputHandle<SlotAssignable<T>> | InputHandle<SlotAssignable<T>> | TypedRef<SlotAssignable<T>>;
67
+ /**
68
+ * Any ref-resolving bindable, with no per-slot type constraint.
69
+ */
70
+ type AnyRefBindable = InputHandle | OutputHandle | TypedRef;
71
+ /**
72
+ * The set of values accepted by a typed operation bind slot: everything
73
+ * {@link RefBindable} accepts, plus a {@link TypedLiteral} constant baked
74
+ * into the flow document. A `'resource'` slot rejects a literal: a token
75
+ * resource is not a scalar constant.
76
+ *
77
+ * Use `raw.literal<T>()` to create one.
78
+ */
79
+ type Bindable<T extends OutputKind> = RefBindable<T> | LiteralBindable<SlotAssignable<T>>;
80
+ type LiteralBindable<K extends OutputKind> = K extends SolType ? TypedLiteral<K> : never;
81
+ /**
82
+ * Converts a bindable to the wire `bind` value it denotes: handles become
83
+ * `$ref` pointers, refs and literals pass through as-is.
84
+ */
85
+ declare const toBindValue: (bindable: InputHandle | OutputHandle | TypedRef | TypedLiteral) => BindValue;
67
86
 
68
- export { type AnyHandle, type Bindable, type InputHandle, type OutputHandle, type ResourceInputHandle, handleToRef, isInputHandle, isOutputHandle };
87
+ export { type AnyHandle, type AnyRefBindable, type Bindable, type InputHandle, type OutputHandle, type RefBindable, type ResourceInputHandle, handleToRef, isInputHandle, isOutputHandle, toBindValue };
@@ -1,5 +1,5 @@
1
- import { StaticSolType, Resource } from '@lifi/compose-spec';
2
- import { OutputKind, TypedRef } from '../types.js';
1
+ import { StaticSolType, SolType, Resource, BindValue } from '@lifi/compose-spec';
2
+ import { OutputKind, TypedRef, TypedLiteral } from '../types.js';
3
3
 
4
4
  /**
5
5
  * A handle referencing a flow input declared in the flow's input schema.
@@ -54,15 +54,34 @@ declare const handleToRef: (h: AnyHandle) => {
54
54
  */
55
55
  type SlotAssignable<T extends OutputKind> = T extends 'bytes32' ? StaticSolType | 'resource' : T extends 'uint256' ? T | 'resource' : T;
56
56
  /**
57
- * The set of values accepted by a typed bind slot.
57
+ * The values accepted by a typed slot that must resolve to a `$ref`:
58
58
  * - `OutputHandle<T>` / `InputHandle<T>` (same type)
59
59
  * - For `'uint256'` slots, also `'resource'`-tagged handles
60
60
  * - For `'bytes32'` slots, any static-32-byte handle (and `'resource'`)
61
61
  * - `TypedRef<T>` (typed context refs, subject to `SlotAssignable`)
62
62
  *
63
- * Plain `{ $ref: '...' }` objects are **not** accepted — use `raw.ref<T>()`
63
+ * Plain `{ $ref: '...' }` objects are **not** accepted - use `raw.ref<T>()`
64
64
  * to create a typed ref when you need to reference a raw path.
65
65
  */
66
- type Bindable<T extends OutputKind> = OutputHandle<SlotAssignable<T>> | InputHandle<SlotAssignable<T>> | TypedRef<SlotAssignable<T>>;
66
+ type RefBindable<T extends OutputKind> = OutputHandle<SlotAssignable<T>> | InputHandle<SlotAssignable<T>> | TypedRef<SlotAssignable<T>>;
67
+ /**
68
+ * Any ref-resolving bindable, with no per-slot type constraint.
69
+ */
70
+ type AnyRefBindable = InputHandle | OutputHandle | TypedRef;
71
+ /**
72
+ * The set of values accepted by a typed operation bind slot: everything
73
+ * {@link RefBindable} accepts, plus a {@link TypedLiteral} constant baked
74
+ * into the flow document. A `'resource'` slot rejects a literal: a token
75
+ * resource is not a scalar constant.
76
+ *
77
+ * Use `raw.literal<T>()` to create one.
78
+ */
79
+ type Bindable<T extends OutputKind> = RefBindable<T> | LiteralBindable<SlotAssignable<T>>;
80
+ type LiteralBindable<K extends OutputKind> = K extends SolType ? TypedLiteral<K> : never;
81
+ /**
82
+ * Converts a bindable to the wire `bind` value it denotes: handles become
83
+ * `$ref` pointers, refs and literals pass through as-is.
84
+ */
85
+ declare const toBindValue: (bindable: InputHandle | OutputHandle | TypedRef | TypedLiteral) => BindValue;
67
86
 
68
- export { type AnyHandle, type Bindable, type InputHandle, type OutputHandle, type ResourceInputHandle, handleToRef, isInputHandle, isOutputHandle };
87
+ export { type AnyHandle, type AnyRefBindable, type Bindable, type InputHandle, type OutputHandle, type RefBindable, type ResourceInputHandle, handleToRef, isInputHandle, isOutputHandle, toBindValue };
@@ -9,9 +9,11 @@ const handleToRef = (h) => {
9
9
  if (!h.portName) throw new Error("OutputHandle has empty portName");
10
10
  return { $ref: `${h.nodeId}.${h.portName}` };
11
11
  };
12
+ const toBindValue = (bindable) => "_tag" in bindable ? handleToRef(bindable) : bindable;
12
13
  export {
13
14
  handleToRef,
14
15
  isInputHandle,
15
- isOutputHandle
16
+ isOutputHandle,
17
+ toBindValue
16
18
  };
17
19
  //# sourceMappingURL=handles.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/authoring/handles.ts"],"sourcesContent":["import type { Resource, StaticSolType } from '@lifi/compose-spec';\n\nimport type { OutputKind, TypedRef } from '../types.js';\n\n/**\n * A handle referencing a flow input declared in the flow's input schema.\n * The phantom `__outputKind` field carries the type parameter through\n * TypeScript's structural type system without affecting runtime shape.\n */\nexport interface InputHandle<T extends OutputKind = OutputKind> {\n readonly _tag: 'input';\n readonly inputName: string;\n readonly __outputKind?: T;\n}\n\n/**\n * An input handle for a resource (token) input, carrying the resource\n * declaration alongside the reference.\n */\nexport interface ResourceInputHandle extends InputHandle<'resource'> {\n readonly resource: Resource;\n}\n\n/**\n * A handle referencing an output port of a previously declared operation node.\n * The phantom `__outputKind` field carries the type parameter through\n * TypeScript's structural type system without affecting runtime shape.\n */\nexport interface OutputHandle<T extends OutputKind = OutputKind> {\n readonly _tag: 'output';\n readonly nodeId: string;\n readonly portName: string;\n readonly __outputKind?: T;\n}\n\n/** Discriminated union of all handle types that can be bound to operation arguments. */\nexport type AnyHandle = InputHandle | OutputHandle;\n\n/** Type guard that narrows an {@link AnyHandle} to an {@link InputHandle}. */\nexport const isInputHandle = (h: AnyHandle): h is InputHandle =>\n h._tag === 'input';\n\n/** Type guard that narrows an {@link AnyHandle} to an {@link OutputHandle}. */\nexport const isOutputHandle = (h: AnyHandle): h is OutputHandle =>\n h._tag === 'output';\n\n/**\n * Converts a handle to a JSON `$ref` pointer used in the flow document.\n * Input handles become `\"input.<name>\"`; output handles become `\"<nodeId>.<port>\"`.\n */\nexport const handleToRef = (h: AnyHandle): { $ref: string } => {\n if (h._tag === 'input') {\n if (!h.inputName) throw new Error('InputHandle has empty inputName');\n return { $ref: `input.${h.inputName}` };\n }\n if (!h.nodeId) throw new Error('OutputHandle has empty nodeId');\n if (!h.portName) throw new Error('OutputHandle has empty portName');\n return { $ref: `${h.nodeId}.${h.portName}` };\n};\n\n/**\n * For a bind slot expecting type T, widen the set of accepted handle types:\n * - `'bytes32'` is the raw 32-byte word type: accept any static-32-byte handle\n * (`uint256`/`uintN`/`address`/`bool`/`bytes32`) plus `'resource'` (a uint256\n * amount). At the IR1 boundary every value is a type-erased word, so any\n * static-32-byte handle is a sound bind for a `bytes32` payload port.\n * - `'uint256'` keeps its existing widening to also accept `'resource'`-tagged\n * handles (resources are uint256 amounts).\n * - every other type keeps exact-SolType matching.\n */\ntype SlotAssignable<T extends OutputKind> = T extends 'bytes32'\n ? StaticSolType | 'resource'\n : T extends 'uint256'\n ? T | 'resource'\n : T;\n\n/**\n * The set of values accepted by a typed bind slot.\n * - `OutputHandle<T>` / `InputHandle<T>` (same type)\n * - For `'uint256'` slots, also `'resource'`-tagged handles\n * - For `'bytes32'` slots, any static-32-byte handle (and `'resource'`)\n * - `TypedRef<T>` (typed context refs, subject to `SlotAssignable`)\n *\n * Plain `{ $ref: '...' }` objects are **not** accepted — use `raw.ref<T>()`\n * to create a typed ref when you need to reference a raw path.\n */\nexport type Bindable<T extends OutputKind> =\n | OutputHandle<SlotAssignable<T>>\n | InputHandle<SlotAssignable<T>>\n | TypedRef<SlotAssignable<T>>;\n"],"mappings":"AAuCO,MAAM,gBAAgB,CAAC,MAC5B,EAAE,SAAS;AAGN,MAAM,iBAAiB,CAAC,MAC7B,EAAE,SAAS;AAMN,MAAM,cAAc,CAAC,MAAmC;AAC7D,MAAI,EAAE,SAAS,SAAS;AACtB,QAAI,CAAC,EAAE,UAAW,OAAM,IAAI,MAAM,iCAAiC;AACnE,WAAO,EAAE,MAAM,SAAS,EAAE,SAAS,GAAG;AAAA,EACxC;AACA,MAAI,CAAC,EAAE,OAAQ,OAAM,IAAI,MAAM,+BAA+B;AAC9D,MAAI,CAAC,EAAE,SAAU,OAAM,IAAI,MAAM,iCAAiC;AAClE,SAAO,EAAE,MAAM,GAAG,EAAE,MAAM,IAAI,EAAE,QAAQ,GAAG;AAC7C;","names":[]}
1
+ {"version":3,"sources":["../../src/authoring/handles.ts"],"sourcesContent":["import type {\n BindValue,\n Resource,\n SolType,\n StaticSolType,\n} from '@lifi/compose-spec';\n\nimport type { OutputKind, TypedLiteral, TypedRef } from '../types.js';\n\n/**\n * A handle referencing a flow input declared in the flow's input schema.\n * The phantom `__outputKind` field carries the type parameter through\n * TypeScript's structural type system without affecting runtime shape.\n */\nexport interface InputHandle<T extends OutputKind = OutputKind> {\n readonly _tag: 'input';\n readonly inputName: string;\n readonly __outputKind?: T;\n}\n\n/**\n * An input handle for a resource (token) input, carrying the resource\n * declaration alongside the reference.\n */\nexport interface ResourceInputHandle extends InputHandle<'resource'> {\n readonly resource: Resource;\n}\n\n/**\n * A handle referencing an output port of a previously declared operation node.\n * The phantom `__outputKind` field carries the type parameter through\n * TypeScript's structural type system without affecting runtime shape.\n */\nexport interface OutputHandle<T extends OutputKind = OutputKind> {\n readonly _tag: 'output';\n readonly nodeId: string;\n readonly portName: string;\n readonly __outputKind?: T;\n}\n\n/** Discriminated union of all handle types that can be bound to operation arguments. */\nexport type AnyHandle = InputHandle | OutputHandle;\n\n/** Type guard that narrows an {@link AnyHandle} to an {@link InputHandle}. */\nexport const isInputHandle = (h: AnyHandle): h is InputHandle =>\n h._tag === 'input';\n\n/** Type guard that narrows an {@link AnyHandle} to an {@link OutputHandle}. */\nexport const isOutputHandle = (h: AnyHandle): h is OutputHandle =>\n h._tag === 'output';\n\n/**\n * Converts a handle to a JSON `$ref` pointer used in the flow document.\n * Input handles become `\"input.<name>\"`; output handles become `\"<nodeId>.<port>\"`.\n */\nexport const handleToRef = (h: AnyHandle): { $ref: string } => {\n if (h._tag === 'input') {\n if (!h.inputName) throw new Error('InputHandle has empty inputName');\n return { $ref: `input.${h.inputName}` };\n }\n if (!h.nodeId) throw new Error('OutputHandle has empty nodeId');\n if (!h.portName) throw new Error('OutputHandle has empty portName');\n return { $ref: `${h.nodeId}.${h.portName}` };\n};\n\n/**\n * For a bind slot expecting type T, widen the set of accepted handle types:\n * - `'bytes32'` is the raw 32-byte word type: accept any static-32-byte handle\n * (`uint256`/`uintN`/`address`/`bool`/`bytes32`) plus `'resource'` (a uint256\n * amount). At the IR1 boundary every value is a type-erased word, so any\n * static-32-byte handle is a sound bind for a `bytes32` payload port.\n * - `'uint256'` keeps its existing widening to also accept `'resource'`-tagged\n * handles (resources are uint256 amounts).\n * - every other type keeps exact-SolType matching.\n */\ntype SlotAssignable<T extends OutputKind> = T extends 'bytes32'\n ? StaticSolType | 'resource'\n : T extends 'uint256'\n ? T | 'resource'\n : T;\n\n/**\n * The values accepted by a typed slot that must resolve to a `$ref`:\n * - `OutputHandle<T>` / `InputHandle<T>` (same type)\n * - For `'uint256'` slots, also `'resource'`-tagged handles\n * - For `'bytes32'` slots, any static-32-byte handle (and `'resource'`)\n * - `TypedRef<T>` (typed context refs, subject to `SlotAssignable`)\n *\n * Plain `{ $ref: '...' }` objects are **not** accepted - use `raw.ref<T>()`\n * to create a typed ref when you need to reference a raw path.\n */\nexport type RefBindable<T extends OutputKind> =\n | OutputHandle<SlotAssignable<T>>\n | InputHandle<SlotAssignable<T>>\n | TypedRef<SlotAssignable<T>>;\n\n/**\n * Any ref-resolving bindable, with no per-slot type constraint.\n */\nexport type AnyRefBindable = InputHandle | OutputHandle | TypedRef;\n\n/**\n * The set of values accepted by a typed operation bind slot: everything\n * {@link RefBindable} accepts, plus a {@link TypedLiteral} constant baked\n * into the flow document. A `'resource'` slot rejects a literal: a token\n * resource is not a scalar constant.\n *\n * Use `raw.literal<T>()` to create one.\n */\nexport type Bindable<T extends OutputKind> =\n RefBindable<T> | LiteralBindable<SlotAssignable<T>>;\n\n// Distributes over the slot's assignable kinds and keeps the scalar ones: a\n// `'resource'` slot has no scalar arm, so its literal arm collapses to\n// `never` and a literal is rejected outright.\ntype LiteralBindable<K extends OutputKind> = K extends SolType\n ? TypedLiteral<K>\n : never;\n\n/**\n * Converts a bindable to the wire `bind` value it denotes: handles become\n * `$ref` pointers, refs and literals pass through as-is.\n */\nexport const toBindValue = (\n bindable: InputHandle | OutputHandle | TypedRef | TypedLiteral,\n): BindValue => ('_tag' in bindable ? handleToRef(bindable) : bindable);\n"],"mappings":"AA4CO,MAAM,gBAAgB,CAAC,MAC5B,EAAE,SAAS;AAGN,MAAM,iBAAiB,CAAC,MAC7B,EAAE,SAAS;AAMN,MAAM,cAAc,CAAC,MAAmC;AAC7D,MAAI,EAAE,SAAS,SAAS;AACtB,QAAI,CAAC,EAAE,UAAW,OAAM,IAAI,MAAM,iCAAiC;AACnE,WAAO,EAAE,MAAM,SAAS,EAAE,SAAS,GAAG;AAAA,EACxC;AACA,MAAI,CAAC,EAAE,OAAQ,OAAM,IAAI,MAAM,+BAA+B;AAC9D,MAAI,CAAC,EAAE,SAAU,OAAM,IAAI,MAAM,iCAAiC;AAClE,SAAO,EAAE,MAAM,GAAG,EAAE,MAAM,IAAI,EAAE,QAAQ,GAAG;AAC7C;AA4DO,MAAM,cAAc,CACzB,aACe,UAAU,WAAW,YAAY,QAAQ,IAAI;","names":[]}
@@ -18,6 +18,7 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var raw_exports = {};
20
20
  __export(raw_exports, {
21
+ literal: () => literal,
21
22
  rawGuard: () => rawGuard,
22
23
  ref: () => ref
23
24
  });
@@ -25,6 +26,7 @@ module.exports = __toCommonJS(raw_exports);
25
26
  const ref = (path) => ({
26
27
  $ref: path
27
28
  });
29
+ const literal = (kind, value) => ({ kind, value });
28
30
  const rawGuard = (kind, config = {}) => {
29
31
  if ("kind" in config) {
30
32
  throw new Error(
@@ -37,6 +39,7 @@ const rawGuard = (kind, config = {}) => {
37
39
  };
38
40
  // Annotate the CommonJS export names for ESM import in node:
39
41
  0 && (module.exports = {
42
+ literal,
40
43
  rawGuard,
41
44
  ref
42
45
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/authoring/raw.ts"],"sourcesContent":["import type { AppliedGuard } from '@lifi/compose-spec';\n\nimport type { OutputKind, TypedRef } from '../types.js';\n\n/**\n * Creates a typed `$ref` pointer for use in bind slots.\n *\n * This is an **escape hatch** for referencing values that the typed builder\n * API doesn't cover — for example, outputs of an `untypedOp` node or custom\n * context paths. The caller is responsible for choosing the correct type\n * parameter; no runtime validation occurs.\n *\n * Prefer typed handles (`OutputHandle`, `InputHandle`) and `builder.context`\n * whenever possible. Use `ref` only when you need to construct a `$ref` path\n * manually and want it accepted by a `Bindable<T>` slot.\n *\n * @typeParam T - The output kind this ref represents (e.g. `'address'`, `'uint256'`, `'resource'`).\n * @param path - The `$ref` path (e.g. `\"context.sender\"`, `\"myNode.result\"`).\n * @returns A {@link TypedRef} accepted by `Bindable<T>` slots matching `T`.\n *\n * @example\n * ```ts\n * import { raw } from '@lifi/composer-sdk';\n *\n * // Reference an untypedOp output in a typed operation:\n * builder.untypedOp('custom', 'some.op', {\n * bind: { x: { $ref: 'input.token' } },\n * config: {},\n * });\n * builder.core.add('sum', {\n * bind: {\n * a: raw.ref<'uint256'>('custom.result'),\n * b: someTypedHandle,\n * },\n * });\n * ```\n */\nexport const ref = <T extends OutputKind>(path: string): TypedRef<T> => ({\n $ref: path,\n});\n\n/**\n * Creates a raw guard object for use in the `guards` array of an operation call.\n *\n * @param kind - The guard type (e.g. `\"slippage\"`).\n * @param config - Guard-specific configuration (e.g. `{ toleranceBps: 300 }`).\n * @returns An {@link AppliedGuard} object.\n */\nexport const rawGuard = (\n kind: string,\n config: Record<string, unknown> = {},\n): AppliedGuard => {\n if ('kind' in config) {\n throw new Error(\n `rawGuard: 'kind' must not appear in config (got \"${String(\n config.kind,\n )}\"). Pass it as the first argument instead.`,\n );\n }\n return { ...config, kind };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqCO,MAAM,MAAM,CAAuB,UAA+B;AAAA,EACvE,MAAM;AACR;AASO,MAAM,WAAW,CACtB,MACA,SAAkC,CAAC,MAClB;AACjB,MAAI,UAAU,QAAQ;AACpB,UAAM,IAAI;AAAA,MACR,oDAAoD;AAAA,QAClD,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,EAAE,GAAG,QAAQ,KAAK;AAC3B;","names":[]}
1
+ {"version":3,"sources":["../../src/authoring/raw.ts"],"sourcesContent":["import type { AppliedGuard, SolType } from '@lifi/compose-spec';\n\nimport type { OutputKind, TypedLiteral, TypedRef } from '../types.js';\n\n/**\n * Creates a typed `$ref` pointer for use in bind slots.\n *\n * This is an **escape hatch** for referencing values that the typed builder\n * API doesn't cover — for example, outputs of an `untypedOp` node or custom\n * context paths. The caller is responsible for choosing the correct type\n * parameter; no runtime validation occurs.\n *\n * Prefer typed handles (`OutputHandle`, `InputHandle`) and `builder.context`\n * whenever possible. Use `ref` only when you need to construct a `$ref` path\n * manually and want it accepted by a `Bindable<T>` slot.\n *\n * @typeParam T - The output kind this ref represents (e.g. `'address'`, `'uint256'`, `'resource'`).\n * @param path - The `$ref` path (e.g. `\"context.sender\"`, `\"myNode.result\"`).\n * @returns A {@link TypedRef} accepted by `Bindable<T>` slots matching `T`.\n *\n * @example\n * ```ts\n * import { raw } from '@lifi/composer-sdk';\n *\n * // Reference an untypedOp output in a typed operation:\n * builder.untypedOp('custom', 'some.op', {\n * bind: { x: { $ref: 'input.token' } },\n * config: {},\n * });\n * builder.core.add('sum', {\n * bind: {\n * a: raw.ref<'uint256'>('custom.result'),\n * b: someTypedHandle,\n * },\n * });\n * ```\n */\nexport const ref = <T extends OutputKind>(path: string): TypedRef<T> => ({\n $ref: path,\n});\n\n/**\n * Creates a typed literal for use in bind slots: a constant baked into the\n * flow document rather than supplied at run time.\n *\n * Prefer a declared flow input and `builder.inputs.<name>` — a value the\n * caller supplies per run stays out of the document. Use a literal when the\n * flow has no run of its own to take the value from: the `continuationFlow`\n * of a `continuation.settle` node is stored by the settlement backend and\n * re-run later against the deferred proceeds, so its delivery address has to\n * be part of the committed document.\n *\n * @typeParam T - The Solidity type of the literal (e.g. `'address'`).\n * @param kind - The literal's Solidity type.\n * @param value - The literal value, as the wire format carries it: a string.\n * @returns A {@link TypedLiteral} accepted by `Bindable<T>` slots matching `T`.\n *\n * @example\n * ```ts\n * import { raw } from '@lifi/composer-sdk';\n *\n * builder.core.transfer('deliver', {\n * bind: {\n * amount: zap.amountOut,\n * recipient: raw.literal('address', '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'),\n * },\n * config: {},\n * });\n * ```\n */\nexport const literal = <T extends SolType>(\n kind: T,\n value: string,\n): TypedLiteral<T> => ({ kind, value });\n\n/**\n * Creates a raw guard object for use in the `guards` array of an operation call.\n *\n * @param kind - The guard type (e.g. `\"slippage\"`).\n * @param config - Guard-specific configuration (e.g. `{ toleranceBps: 300 }`).\n * @returns An {@link AppliedGuard} object.\n */\nexport const rawGuard = (\n kind: string,\n config: Record<string, unknown> = {},\n): AppliedGuard => {\n if ('kind' in config) {\n throw new Error(\n `rawGuard: 'kind' must not appear in config (got \"${String(\n config.kind,\n )}\"). Pass it as the first argument instead.`,\n );\n }\n return { ...config, kind };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqCO,MAAM,MAAM,CAAuB,UAA+B;AAAA,EACvE,MAAM;AACR;AA+BO,MAAM,UAAU,CACrB,MACA,WACqB,EAAE,MAAM,MAAM;AAS9B,MAAM,WAAW,CACtB,MACA,SAAkC,CAAC,MAClB;AACjB,MAAI,UAAU,QAAQ;AACpB,UAAM,IAAI;AAAA,MACR,oDAAoD;AAAA,QAClD,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,EAAE,GAAG,QAAQ,KAAK;AAC3B;","names":[]}
@@ -1,5 +1,5 @@
1
- import { AppliedGuard } from '@lifi/compose-spec';
2
- import { OutputKind, TypedRef } from '../types.cjs';
1
+ import { AppliedGuard, SolType } from '@lifi/compose-spec';
2
+ import { TypedLiteral, OutputKind, TypedRef } from '../types.cjs';
3
3
 
4
4
  /**
5
5
  * Creates a typed `$ref` pointer for use in bind slots.
@@ -35,6 +35,36 @@ import { OutputKind, TypedRef } from '../types.cjs';
35
35
  * ```
36
36
  */
37
37
  declare const ref: <T extends OutputKind>(path: string) => TypedRef<T>;
38
+ /**
39
+ * Creates a typed literal for use in bind slots: a constant baked into the
40
+ * flow document rather than supplied at run time.
41
+ *
42
+ * Prefer a declared flow input and `builder.inputs.<name>` — a value the
43
+ * caller supplies per run stays out of the document. Use a literal when the
44
+ * flow has no run of its own to take the value from: the `continuationFlow`
45
+ * of a `continuation.settle` node is stored by the settlement backend and
46
+ * re-run later against the deferred proceeds, so its delivery address has to
47
+ * be part of the committed document.
48
+ *
49
+ * @typeParam T - The Solidity type of the literal (e.g. `'address'`).
50
+ * @param kind - The literal's Solidity type.
51
+ * @param value - The literal value, as the wire format carries it: a string.
52
+ * @returns A {@link TypedLiteral} accepted by `Bindable<T>` slots matching `T`.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import { raw } from '@lifi/composer-sdk';
57
+ *
58
+ * builder.core.transfer('deliver', {
59
+ * bind: {
60
+ * amount: zap.amountOut,
61
+ * recipient: raw.literal('address', '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'),
62
+ * },
63
+ * config: {},
64
+ * });
65
+ * ```
66
+ */
67
+ declare const literal: <T extends SolType>(kind: T, value: string) => TypedLiteral<T>;
38
68
  /**
39
69
  * Creates a raw guard object for use in the `guards` array of an operation call.
40
70
  *
@@ -44,4 +74,4 @@ declare const ref: <T extends OutputKind>(path: string) => TypedRef<T>;
44
74
  */
45
75
  declare const rawGuard: (kind: string, config?: Record<string, unknown>) => AppliedGuard;
46
76
 
47
- export { rawGuard, ref };
77
+ export { literal, rawGuard, ref };
@@ -1,5 +1,5 @@
1
- import { AppliedGuard } from '@lifi/compose-spec';
2
- import { OutputKind, TypedRef } from '../types.js';
1
+ import { AppliedGuard, SolType } from '@lifi/compose-spec';
2
+ import { TypedLiteral, OutputKind, TypedRef } from '../types.js';
3
3
 
4
4
  /**
5
5
  * Creates a typed `$ref` pointer for use in bind slots.
@@ -35,6 +35,36 @@ import { OutputKind, TypedRef } from '../types.js';
35
35
  * ```
36
36
  */
37
37
  declare const ref: <T extends OutputKind>(path: string) => TypedRef<T>;
38
+ /**
39
+ * Creates a typed literal for use in bind slots: a constant baked into the
40
+ * flow document rather than supplied at run time.
41
+ *
42
+ * Prefer a declared flow input and `builder.inputs.<name>` — a value the
43
+ * caller supplies per run stays out of the document. Use a literal when the
44
+ * flow has no run of its own to take the value from: the `continuationFlow`
45
+ * of a `continuation.settle` node is stored by the settlement backend and
46
+ * re-run later against the deferred proceeds, so its delivery address has to
47
+ * be part of the committed document.
48
+ *
49
+ * @typeParam T - The Solidity type of the literal (e.g. `'address'`).
50
+ * @param kind - The literal's Solidity type.
51
+ * @param value - The literal value, as the wire format carries it: a string.
52
+ * @returns A {@link TypedLiteral} accepted by `Bindable<T>` slots matching `T`.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import { raw } from '@lifi/composer-sdk';
57
+ *
58
+ * builder.core.transfer('deliver', {
59
+ * bind: {
60
+ * amount: zap.amountOut,
61
+ * recipient: raw.literal('address', '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'),
62
+ * },
63
+ * config: {},
64
+ * });
65
+ * ```
66
+ */
67
+ declare const literal: <T extends SolType>(kind: T, value: string) => TypedLiteral<T>;
38
68
  /**
39
69
  * Creates a raw guard object for use in the `guards` array of an operation call.
40
70
  *
@@ -44,4 +74,4 @@ declare const ref: <T extends OutputKind>(path: string) => TypedRef<T>;
44
74
  */
45
75
  declare const rawGuard: (kind: string, config?: Record<string, unknown>) => AppliedGuard;
46
76
 
47
- export { rawGuard, ref };
77
+ export { literal, rawGuard, ref };