* Resolves the executable node & link IDs for a given input slot. * @param slot The slot index of the input. * @param visited Leave empty unless overriding this method. * A set of unique IDs, used to guard against infinite recursion. * If overriding, ensure that the set is passed on all
(slot: number, visited = new Set<string>())
| 130 | * @returns The node and the origin ID / slot index of the output. |
| 131 | */ |
| 132 | resolveInput(slot: number, visited = new Set<string>()): ResolvedInput | undefined { |
| 133 | const uniqueId = `${this.subgraphNode?.subgraph.id}:${this.node.id}[I]${slot}` |
| 134 | if (visited.has(uniqueId)) { |
| 135 | const nodeInfo = `${this.node.id}${this.node.title ? ` (${this.node.title})` : ""}` |
| 136 | const pathInfo = this.subgraphNodePath.length > 0 ? ` at path ${this.subgraphNodePath.join(":")}` : "" |
| 137 | throw new RecursionError( |
| 138 | `Circular reference detected while resolving input ${slot} of node ${nodeInfo}${pathInfo}. ` + |
| 139 | `This creates an infinite loop in link resolution. UniqueID: [${uniqueId}]`, |
| 140 | ) |
| 141 | } |
| 142 | visited.add(uniqueId) |
| 143 | |
| 144 | const input = this.inputs.at(slot) |
| 145 | if (!input) throw new SlotIndexError(`No input found for flattened id [${this.id}] slot [${slot}]`) |
| 146 | |
| 147 | // Nothing connected |
| 148 | if (input.linkId == null) return |
| 149 | |
| 150 | const link = this.graph.getLink(input.linkId) |
| 151 | if (!link) throw new InvalidLinkError(`No link found in parent graph for id [${this.id}] slot [${slot}] ${input.name}`) |
| 152 | |
| 153 | const { subgraphNode } = this |
| 154 | |
| 155 | // Link goes up and out of this subgraph |
| 156 | if (subgraphNode && link.originIsIoNode) { |
| 157 | const subgraphNodeInput = subgraphNode.inputs.at(link.origin_slot) |
| 158 | if (!subgraphNodeInput) throw new SlotIndexError(`No input found for slot [${link.origin_slot}] ${input.name}`) |
| 159 | |
| 160 | // Nothing connected |
| 161 | const linkId = subgraphNodeInput.link |
| 162 | if (linkId == null) { |
| 163 | const widget = subgraphNode.getWidgetFromSlot(subgraphNodeInput) |
| 164 | if (!widget) return |
| 165 | |
| 166 | // Special case: SubgraphNode widget. |
| 167 | return { |
| 168 | node: this, |
| 169 | origin_id: this.id, |
| 170 | origin_slot: -1, |
| 171 | widgetInfo: { value: widget.value }, |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | const outerLink = subgraphNode.graph.getLink(linkId) |
| 176 | if (!outerLink) throw new InvalidLinkError(`No outer link found for slot [${link.origin_slot}] ${input.name}`) |
| 177 | |
| 178 | const subgraphNodeExecutionId = this.subgraphNodePath.join(":") |
| 179 | const subgraphNodeDto = this.nodesByExecutionId.get(subgraphNodeExecutionId) |
| 180 | if (!subgraphNodeDto) throw new Error(`No subgraph node DTO found for id [${subgraphNodeExecutionId}]`) |
| 181 | |
| 182 | return subgraphNodeDto.resolveInput(outerLink.target_slot, visited) |
| 183 | } |
| 184 | |
| 185 | // Not part of a subgraph; use the original link |
| 186 | const outputNode = this.graph.getNodeById(link.origin_id) |
| 187 | if (!outputNode) throw new InvalidLinkError(`No input node found for id [${this.id}] slot [${slot}] ${input.name}`) |
| 188 | |
| 189 | const outputNodeExecutionId = [...this.subgraphNodePath, outputNode.id].join(":") |
no test coverage detected