first commit

This commit is contained in:
2025-10-26 23:10:15 +08:00
commit 8f0345b7be
14961 changed files with 2356381 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
import type { IConnection, IConnections } from '../interfaces';
type MultipleInputNodesError = {
errorCode: 'Multiple Input Nodes';
nodes: Set<string>;
};
type MultipleOutputNodesError = {
errorCode: 'Multiple Output Nodes';
nodes: Set<string>;
};
type InputEdgeToNonRootNode = {
errorCode: 'Input Edge To Non-Root Node';
node: string;
};
type OutputEdgeFromNonLeafNode = {
errorCode: 'Output Edge From Non-Leaf Node';
node: string;
};
type NoContinuousPathFromRootToLeaf = {
errorCode: 'No Continuous Path From Root To Leaf In Selection';
start: string;
end: string;
};
export type ExtractableErrorResult = MultipleInputNodesError | MultipleOutputNodesError | InputEdgeToNonRootNode | OutputEdgeFromNonLeafNode | NoContinuousPathFromRootToLeaf;
export type IConnectionAdjacencyList = Map<string, Set<IConnection>>;
/**
* Find all edges leading into the graph described in `graphIds`.
*/
export declare function getInputEdges(graphIds: Set<string>, adjacencyList: IConnectionAdjacencyList): Array<[string, IConnection]>;
/**
* Find all edges leading out of the graph described in `graphIds`.
*/
export declare function getOutputEdges(graphIds: Set<string>, adjacencyList: IConnectionAdjacencyList): Array<[string, IConnection]>;
export declare function getRootNodes(graphIds: Set<string>, adjacencyList: IConnectionAdjacencyList): Set<string>;
export declare function getLeafNodes(graphIds: Set<string>, adjacencyList: IConnectionAdjacencyList): Set<string>;
export declare function hasPath(start: string, end: string, adjacencyList: IConnectionAdjacencyList): boolean;
export type ExtractableSubgraphData = {
start?: string;
end?: string;
};
export declare function buildAdjacencyList(connectionsBySourceNode: IConnections): IConnectionAdjacencyList;
/**
* A subgraph is considered extractable if the following properties hold:
* - 0-1 input nodes from outside the subgraph, to a root node
* - 0-1 output nodes to outside the subgraph, from a leaf node
* - continuous path between input and output nodes if they exist
*
* This also covers the requirement that all "inner" nodes between the root node
* and the output node are selected, since this would otherwise create extra
* input or output nodes.
*
* @returns An object containing optional start and end nodeIds
* indicating which nodes have outside connections, OR
* An array of errors if the selection is not valid.
*/
export declare function parseExtractableSubgraphSelection(graphIds: Set<string>, adjacencyList: IConnectionAdjacencyList): ExtractableSubgraphData | ExtractableErrorResult[];
export {};
//# sourceMappingURL=graph-utils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"graph-utils.d.ts","sourceRoot":"","sources":["../../../src/graph/graph-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE/D,KAAK,uBAAuB,GAAG;IAC9B,SAAS,EAAE,sBAAsB,CAAC;IAClC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC/B,SAAS,EAAE,uBAAuB,CAAC;IACnC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC7B,SAAS,EAAE,6BAA6B,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAChC,SAAS,EAAE,gCAAgC,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,KAAK,8BAA8B,GAAG;IACrC,SAAS,EAAE,mDAAmD,CAAC;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAC/B,uBAAuB,GACvB,wBAAwB,GACxB,sBAAsB,GACtB,yBAAyB,GACzB,8BAA8B,CAAC;AAElC,MAAM,MAAM,wBAAwB,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;AAErE;;GAEG;AACH,wBAAgB,aAAa,CAC5B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,EACrB,aAAa,EAAE,wBAAwB,GACrC,KAAK,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAa9B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC7B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,EACrB,aAAa,EAAE,wBAAwB,GACrC,KAAK,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAa9B;AAyBD,wBAAgB,YAAY,CAC3B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,EACrB,aAAa,EAAE,wBAAwB,GACrC,GAAG,CAAC,MAAM,CAAC,CAeb;AAED,wBAAgB,YAAY,CAC3B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,EACrB,aAAa,EAAE,wBAAwB,GACrC,GAAG,CAAC,MAAM,CAAC,CAiBb;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,wBAAwB,WAkB1F;AAED,MAAM,MAAM,uBAAuB,GAAG;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,wBAAgB,kBAAkB,CACjC,uBAAuB,EAAE,YAAY,GACnC,wBAAwB,CAqB1B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iCAAiC,CAChD,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,EACrB,aAAa,EAAE,wBAAwB,GACrC,uBAAuB,GAAG,sBAAsB,EAAE,CA6DpD"}

176
node_modules/n8n-workflow/dist/esm/graph/graph-utils.js generated vendored Normal file
View File

@@ -0,0 +1,176 @@
/**
* Find all edges leading into the graph described in `graphIds`.
*/
export function getInputEdges(graphIds, adjacencyList) {
const result = [];
for (const [from, tos] of adjacencyList.entries()) {
if (graphIds.has(from))
continue;
for (const to of tos) {
if (graphIds.has(to.node)) {
result.push([from, to]);
}
}
}
return result;
}
/**
* Find all edges leading out of the graph described in `graphIds`.
*/
export function getOutputEdges(graphIds, adjacencyList) {
const result = [];
for (const [from, tos] of adjacencyList.entries()) {
if (!graphIds.has(from))
continue;
for (const to of tos) {
if (!graphIds.has(to.node)) {
result.push([from, to]);
}
}
}
return result;
}
function intersection(a, b) {
const result = new Set();
for (const x of a) {
if (b.has(x))
result.add(x);
}
return result;
}
function union(a, b) {
const result = new Set();
for (const x of a)
result.add(x);
for (const x of b)
result.add(x);
return result;
}
function difference(minuend, subtrahend) {
const result = new Set(minuend.values());
for (const x of subtrahend) {
result.delete(x);
}
return result;
}
export function getRootNodes(graphIds, adjacencyList) {
// Inner nodes are all nodes with an incoming edge from another node in the graph
let innerNodes = new Set();
for (const nodeId of graphIds) {
innerNodes = union(innerNodes, new Set([...(adjacencyList.get(nodeId) ?? [])]
.filter((x) => x.type === 'main' && x.node !== nodeId)
.map((x) => x.node)));
}
return difference(graphIds, innerNodes);
}
export function getLeafNodes(graphIds, adjacencyList) {
const result = new Set();
for (const nodeId of graphIds) {
if (intersection(new Set([...(adjacencyList.get(nodeId) ?? [])]
.filter((x) => x.type === 'main' && x.node !== nodeId)
.map((x) => x.node)), graphIds).size === 0) {
result.add(nodeId);
}
}
return result;
}
export function hasPath(start, end, adjacencyList) {
const seen = new Set();
const paths = [start];
while (true) {
const next = paths.pop();
if (next === end)
return true;
if (next === undefined)
return false;
seen.add(next);
paths.push(...difference(new Set([...(adjacencyList.get(next) ?? [])].filter((x) => x.type === 'main').map((x) => x.node)), seen));
}
}
export function buildAdjacencyList(connectionsBySourceNode) {
const result = new Map();
const addOrCreate = (k, v) => result.set(k, union(result.get(k) ?? new Set(), new Set([v])));
for (const sourceNode of Object.keys(connectionsBySourceNode)) {
for (const type of Object.keys(connectionsBySourceNode[sourceNode])) {
for (const sourceIndex of Object.keys(connectionsBySourceNode[sourceNode][type])) {
for (const connectionIndex of Object.keys(connectionsBySourceNode[sourceNode][type][parseInt(sourceIndex, 10)] ?? [])) {
const connection = connectionsBySourceNode[sourceNode][type][parseInt(sourceIndex, 10)]?.[parseInt(connectionIndex, 10)];
if (connection)
addOrCreate(sourceNode, connection);
}
}
}
}
return result;
}
/**
* A subgraph is considered extractable if the following properties hold:
* - 0-1 input nodes from outside the subgraph, to a root node
* - 0-1 output nodes to outside the subgraph, from a leaf node
* - continuous path between input and output nodes if they exist
*
* This also covers the requirement that all "inner" nodes between the root node
* and the output node are selected, since this would otherwise create extra
* input or output nodes.
*
* @returns An object containing optional start and end nodeIds
* indicating which nodes have outside connections, OR
* An array of errors if the selection is not valid.
*/
export function parseExtractableSubgraphSelection(graphIds, adjacencyList) {
const errors = [];
// 0-1 Input nodes
const inputEdges = getInputEdges(graphIds, adjacencyList);
// This filters out e.g. sub-nodes, which are technically parents
const inputNodes = new Set(inputEdges.filter((x) => x[1].type === 'main').map((x) => x[1].node));
let rootNodes = getRootNodes(graphIds, adjacencyList);
// this enables supporting cases where we have one input and a loop back to it from within the selection
if (rootNodes.size === 0 && inputNodes.size === 1)
rootNodes = inputNodes;
for (const inputNode of difference(inputNodes, rootNodes).values()) {
errors.push({
errorCode: 'Input Edge To Non-Root Node',
node: inputNode,
});
}
const rootInputNodes = intersection(rootNodes, inputNodes);
if (rootInputNodes.size > 1) {
errors.push({
errorCode: 'Multiple Input Nodes',
nodes: rootInputNodes,
});
}
// 0-1 Output nodes
const outputEdges = getOutputEdges(graphIds, adjacencyList);
const outputNodes = new Set(outputEdges.filter((x) => x[1].type === 'main').map((x) => x[0]));
let leafNodes = getLeafNodes(graphIds, adjacencyList);
// If we have no leaf nodes, and only one output node, we can tolerate this output node
// and connect to it.
// Note that this is fairly theoretical, as return semantics in this case are not well-defined.
if (leafNodes.size === 0 && outputNodes.size === 1)
leafNodes = outputNodes;
for (const outputNode of difference(outputNodes, leafNodes).values()) {
errors.push({
errorCode: 'Output Edge From Non-Leaf Node',
node: outputNode,
});
}
const leafOutputNodes = intersection(leafNodes, outputNodes);
if (leafOutputNodes.size > 1) {
errors.push({
errorCode: 'Multiple Output Nodes',
nodes: leafOutputNodes,
});
}
const start = rootInputNodes.values().next().value;
const end = leafOutputNodes.values().next().value;
if (start && end && !hasPath(start, end, adjacencyList)) {
errors.push({
errorCode: 'No Continuous Path From Root To Leaf In Selection',
start,
end,
});
}
return errors.length > 0 ? errors : { start, end };
}
//# sourceMappingURL=graph-utils.js.map

File diff suppressed because one or more lines are too long