UNPKG

@lifi/composer-sdk

Version:

Public Composer SDK for building and submitting flows

91 lines (80 loc) 3.53 kB
import type { Resource, StaticSolType } from '@lifi/compose-spec'; import type { OutputKind, TypedRef } from '../types.js'; /** * A handle referencing a flow input declared in the flow's input schema. * The phantom `__outputKind` field carries the type parameter through * TypeScript's structural type system without affecting runtime shape. */ export interface InputHandle<T extends OutputKind = OutputKind> { readonly _tag: 'input'; readonly inputName: string; readonly __outputKind?: T; } /** * An input handle for a resource (token) input, carrying the resource * declaration alongside the reference. */ export interface ResourceInputHandle extends InputHandle<'resource'> { readonly resource: Resource; } /** * A handle referencing an output port of a previously declared operation node. * The phantom `__outputKind` field carries the type parameter through * TypeScript's structural type system without affecting runtime shape. */ export interface OutputHandle<T extends OutputKind = OutputKind> { readonly _tag: 'output'; readonly nodeId: string; readonly portName: string; readonly __outputKind?: T; } /** Discriminated union of all handle types that can be bound to operation arguments. */ export type AnyHandle = InputHandle | OutputHandle; /** Type guard that narrows an {@link AnyHandle} to an {@link InputHandle}. */ export const isInputHandle = (h: AnyHandle): h is InputHandle => h._tag === 'input'; /** Type guard that narrows an {@link AnyHandle} to an {@link OutputHandle}. */ export const isOutputHandle = (h: AnyHandle): h is OutputHandle => h._tag === 'output'; /** * Converts a handle to a JSON `$ref` pointer used in the flow document. * Input handles become `"input.<name>"`; output handles become `"<nodeId>.<port>"`. */ export const handleToRef = (h: AnyHandle): { $ref: string } => { if (h._tag === 'input') { if (!h.inputName) throw new Error('InputHandle has empty inputName'); return { $ref: `input.${h.inputName}` }; } if (!h.nodeId) throw new Error('OutputHandle has empty nodeId'); if (!h.portName) throw new Error('OutputHandle has empty portName'); return { $ref: `${h.nodeId}.${h.portName}` }; }; /** * For a bind slot expecting type T, widen the set of accepted handle types: * - `'bytes32'` is the raw 32-byte word type: accept any static-32-byte handle * (`uint256`/`uintN`/`address`/`bool`/`bytes32`) plus `'resource'` (a uint256 * amount). At the IR1 boundary every value is a type-erased word, so any * static-32-byte handle is a sound bind for a `bytes32` payload port. * - `'uint256'` keeps its existing widening to also accept `'resource'`-tagged * handles (resources are uint256 amounts). * - every other type keeps exact-SolType matching. */ type SlotAssignable<T extends OutputKind> = T extends 'bytes32' ? StaticSolType | 'resource' : T extends 'uint256' ? T | 'resource' : T; /** * The set of values accepted by a typed bind slot. * - `OutputHandle<T>` / `InputHandle<T>` (same type) * - For `'uint256'` slots, also `'resource'`-tagged handles * - For `'bytes32'` slots, any static-32-byte handle (and `'resource'`) * - `TypedRef<T>` (typed context refs, subject to `SlotAssignable`) * * Plain `{ $ref: '...' }` objects are **not** accepted — use `raw.ref<T>()` * to create a typed ref when you need to reference a raw path. */ export type Bindable<T extends OutputKind> = | OutputHandle<SlotAssignable<T>> | InputHandle<SlotAssignable<T>> | TypedRef<SlotAssignable<T>>;