refactor(core): Reorganize n8n-core and enforce file-name casing (no-changelog) (#12667)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2025-01-17 15:17:25 +01:00
committed by GitHub
parent e7f00bcb7f
commit 05858c2153
132 changed files with 459 additions and 441 deletions

View File

@@ -0,0 +1,114 @@
import { NodeConnectionType, type INode } from 'n8n-workflow';
import type { GraphConnection } from './directed-graph';
import { DirectedGraph } from './directed-graph';
function findSubgraphRecursive(
graph: DirectedGraph,
destinationNode: INode,
current: INode,
trigger: INode,
newGraph: DirectedGraph,
currentBranch: GraphConnection[],
) {
// If the current node is the chosen trigger keep this branch.
if (current === trigger) {
for (const connection of currentBranch) {
newGraph.addNodes(connection.from, connection.to);
newGraph.addConnection(connection);
}
return;
}
const parentConnections = graph.getDirectParentConnections(current);
// If the current node has no parents, dont keep this branch.
if (parentConnections.length === 0) {
return;
}
// If the current node is the destination node again, dont keep this branch.
const isCycleWithDestinationNode =
current === destinationNode && currentBranch.some((c) => c.to === destinationNode);
if (isCycleWithDestinationNode) {
return;
}
// If the current node was already visited, keep this branch.
const isCycleWithCurrentNode = currentBranch.some((c) => c.to === current);
if (isCycleWithCurrentNode) {
// TODO: write function that adds nodes when adding connections
for (const connection of currentBranch) {
newGraph.addNodes(connection.from, connection.to);
newGraph.addConnection(connection);
}
return;
}
// Recurse on each parent.
for (const parentConnection of parentConnections) {
// Skip parents that are connected via non-Main connection types. They are
// only utility nodes for AI and are not part of the data or control flow
// and can never lead too the trigger.
if (parentConnection.type !== NodeConnectionType.Main) {
continue;
}
findSubgraphRecursive(graph, destinationNode, parentConnection.from, trigger, newGraph, [
...currentBranch,
parentConnection,
]);
}
}
/**
* Find all nodes that can lead from the trigger to the destination node.
*
* The algorithm is:
* Start with Destination Node
*
* 1. if the current node is the chosen trigger keep this branch
* 2. if the current node has no parents, dont keep this branch
* 3. if the current node is the destination node again, dont keep this
* branch
* 4. if the current node was already visited, keep this branch
* 5. Recurse on each parent
* 6. Re-add all connections that don't use the `Main` connections type.
* Theses are used by nodes called root nodes and they are not part of the
* dataflow in the graph they are utility nodes, like the AI model used in a
* lang chain node.
*/
export function findSubgraph(options: {
graph: DirectedGraph;
destination: INode;
trigger: INode;
}): DirectedGraph {
const graph = options.graph;
const destination = options.destination;
const trigger = options.trigger;
const subgraph = new DirectedGraph();
findSubgraphRecursive(graph, destination, destination, trigger, subgraph, []);
// For each node in the subgraph, if it has parent connections of a type that
// is not `Main` in the input graph, add the connections and the nodes
// connected to it to the subgraph
//
// Without this all AI related workflows would not work when executed
// partially, because all utility nodes would be missing.
for (const node of subgraph.getNodes().values()) {
const parentConnections = graph.getParentConnections(node);
for (const connection of parentConnections) {
if (connection.type === NodeConnectionType.Main) {
continue;
}
subgraph.addNodes(connection.from, connection.to);
subgraph.addConnection(connection);
}
}
return subgraph;
}