import type { InputPortKey, NodeId, NodeRef, OutputPortKey, RunnableNodeConfig, RunnableNodeOutputJson, UpstreamRefPlaceholder, WorkflowDefinition, } from "../../types"; import type { DefinedNodeCredentialBindings } from "../../authoring/defineNode.types"; import type { DefinedHumanApprovalNode, HumanApprovalOutputJson } from "../../authoring/defineHumanApprovalNode.types"; import { WorkflowBuilder } from "./WorkflowBuilder"; import { ChainCursor } from "./ChainCursorResolver"; import type { AnyRunnableNodeConfig, BooleanWhenOverloads, ValidStepSequence } from "./workflowBuilderTypes"; type WhenEndpoint = Readonly<{ node: NodeRef; output: OutputPortKey; inputPortHint?: InputPortKey }>; export class WhenBuilder { private armEndpoint: WhenEndpoint | undefined; constructor( private readonly wf: WorkflowBuilder, private readonly from: NodeRef, private readonly branchPort: OutputPortKey, private readonly priorEndpoints: ReadonlyArray = [], ) {} addBranch>( steps: TSteps & ValidStepSequence, ): this { const created: NodeRef[] = []; let prev: NodeRef | null = null; for (const cfg of steps) { const ref = (this.wf as any).add(cfg) as NodeRef; created.push(ref); if (!prev) (this.wf as any).connect(this.from, ref, this.branchPort, "in"); else (this.wf as any).connect(prev, ref, "main", "in"); prev = ref; } for (const cfg of steps) { const maybe = cfg as unknown as { upstreamRefs?: Array<{ nodeId: NodeId } | UpstreamRefPlaceholder> }; if (!Array.isArray(maybe.upstreamRefs) || maybe.upstreamRefs.length === 0) continue; maybe.upstreamRefs = maybe.upstreamRefs.map((r) => { if (typeof r !== "string") return r; const idx = parseInt(r.slice(1), 10); const nodeId = created[idx]?.id; return nodeId ? { nodeId } : { nodeId: r }; }); } this.armEndpoint = prev ? { node: prev, output: "main", inputPortHint: this.branchPort } : { node: this.from, output: this.branchPort, inputPortHint: this.branchPort }; return this; } readonly when: BooleanWhenOverloads> = ( branch: boolean, steps: ReadonlyArray | AnyRunnableNodeConfig, ...more: AnyRunnableNodeConfig[] ): WhenBuilder => { const list = Array.isArray(steps) ? steps : [steps, ...more]; const port: OutputPortKey = branch ? "true" : "false"; const b = new WhenBuilder(this.wf, this.from, port, this.accumulatedEndpoints); b.addBranch(list); return b; }; then>( config: TConfig, ): ChainCursor> { return this.toCursor().then(config); } humanApproval< TKey extends string, TConfig extends Record, TBindings extends DefinedNodeCredentialBindings | undefined = undefined, >( node: DefinedHumanApprovalNode, TBindings>, config: TConfig, metadata?: { name?: string; nodeId?: string }, ): ChainCursor>> { return this.toCursor().humanApproval(node, config, metadata); } build(): WorkflowDefinition { return this.wf.build(); } private get accumulatedEndpoints(): ReadonlyArray { return this.armEndpoint ? [...this.priorEndpoints, this.armEndpoint] : this.priorEndpoints; } private toCursor(): ChainCursor { return new ChainCursor(this.wf, this.accumulatedEndpoints); } }