@ldsg/workflow 0.3.8 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,OAAO,EAGL,YAAY,EACZ,gBAAgB,EAChB,eAAe,EAEf,YAAY,EACZ,gBAAgB,EAChB,gCAAgC,EAGjC,MAAM,SAAS,CAAC;AAIjB,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;CAC1C;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,0BAA0B,CAAC;AAEvE,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;CAC1C;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,0BAA0B,CAAC;AAEvE,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,qBAAa,gBAAiB,SAAQ,QAAQ,CAAC,gCAAgC,CAAC;IAC9E,uBAAuB,EAAE,uBAAuB,CA4B9C;IAEF,uBAAuB,EAAE,uBAAuB,CAsB9C;IAEF,eAAe,EAAE,eAAe,CAiB9B;IAEF,OAAO,EAAE,eAAe,CA+GtB;IAEF,eAAe,EAAE,eAAe,CAY9B;CACH"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,OAAO,EAGL,YAAY,EACZ,gBAAgB,EAChB,eAAe,EAEf,YAAY,EACZ,gBAAgB,EAChB,gCAAgC,EAGjC,MAAM,SAAS,CAAC;AAIjB,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;CAC1C;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,0BAA0B,CAAC;AAEvE,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;CAC1C;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,0BAA0B,CAAC;AAEvE,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,qBAAa,gBAAiB,SAAQ,QAAQ,CAAC,gCAAgC,CAAC;IAC9E,uBAAuB,EAAE,uBAAuB,CA4B9C;IAEF,uBAAuB,EAAE,uBAAuB,CAsB9C;IAEF,eAAe,EAAE,eAAe,CAiB9B;IAEF,OAAO,EAAE,eAAe,CAiItB;IAEF,eAAe,EAAE,eAAe,CAY9B;CACH"}
package/dist/resource.js CHANGED
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.WorkflowResource = void 0;
7
7
  const resource_1 = require("@ldsg/resource");
8
8
  const lodash_1 = __importDefault(require("lodash"));
9
- const utils_1 = require("./utils");
10
9
  const evaluate_variables_1 = require("./utils/evaluate-variables");
10
+ const get_execution_levels_1 = require("./utils/get-execution-levels");
11
11
  class WorkflowResource extends resource_1.Resource {
12
12
  constructor() {
13
13
  super(...arguments);
@@ -67,52 +67,65 @@ class WorkflowResource extends resource_1.Resource {
67
67
  if (!startNode || !endNode) {
68
68
  throw new Error("invalid workflow nodes");
69
69
  }
70
- const { orderedNodeList } = (0, utils_1.getOrderedNodeList)({
70
+ const { executionLevels } = (0, get_execution_levels_1.getExecutionLevels)({
71
71
  workflowData,
72
72
  startNode,
73
73
  endNode,
74
74
  });
75
- const orderedNodeListLength = orderedNodeList.length;
76
- if (orderedNodeListLength < 2) {
77
- throw new Error("invalid workflow nodes count");
75
+ if (executionLevels.length < 1) {
76
+ throw new Error("invalid workflow execution levels");
78
77
  }
79
78
  let workflowOutputVariables;
80
- for (let index = 0; index < orderedNodeListLength; index++) {
81
- const node = orderedNodeList[index];
82
- const { id, propertiesSchema, execute: nodeExecute } = node;
83
- const workflowVariables = Object.fromEntries(workflowVariablesMap);
84
- // 输入变量从工作流变量中使用表达式获取
85
- const workflowNodePropertiesInputSchema = lodash_1.default.get(propertiesSchema, "properties.input");
86
- const workflowNodePropertiesOutputSchema = lodash_1.default.get(propertiesSchema, "properties.output");
87
- if (!lodash_1.default.isObject(workflowNodePropertiesInputSchema) ||
88
- !lodash_1.default.isObject(workflowNodePropertiesOutputSchema)) {
89
- throw new Error("invalid workflow node properties schema");
90
- }
91
- const { variables: workflowNodeInputVariables } = await (0, evaluate_variables_1.evaluateVariables)({
92
- jsonSchema: workflowNodePropertiesInputSchema,
79
+ // 按层级执行,同一层级的节点可以并行执行
80
+ for (let levelIndex = 0; levelIndex < executionLevels.length; levelIndex++) {
81
+ const currentLevel = executionLevels[levelIndex];
82
+ // 并行执行当前层级的所有节点
83
+ const levelPromises = currentLevel.map(async (node) => {
84
+ const { id, propertiesSchema, execute: nodeExecute } = node;
85
+ const workflowVariables = Object.fromEntries(workflowVariablesMap);
86
+ // 输入变量从工作流变量中使用表达式获取
87
+ const workflowNodePropertiesInputSchema = lodash_1.default.get(propertiesSchema, "properties.input");
88
+ const workflowNodePropertiesOutputSchema = lodash_1.default.get(propertiesSchema, "properties.output");
89
+ if (!lodash_1.default.isObject(workflowNodePropertiesInputSchema) ||
90
+ !lodash_1.default.isObject(workflowNodePropertiesOutputSchema)) {
91
+ throw new Error("invalid workflow node properties schema");
92
+ }
93
+ const { variables: workflowNodeInputVariables } = await (0, evaluate_variables_1.evaluateVariables)({
94
+ jsonSchema: workflowNodePropertiesInputSchema,
95
+ /**
96
+ * 对于开始节点,不忽略 input 表达式,但替换工作流内部变量为工作流输入变量。
97
+ */
98
+ variables: levelIndex === 0 ? workflowInputVariables : workflowVariables,
99
+ });
100
+ const { variables: workflowNodeOutputExpressionEvaluateInput } = await nodeExecute({
101
+ variables: workflowNodeInputVariables,
102
+ context: {
103
+ workflowResourceInstance: this,
104
+ },
105
+ });
93
106
  /**
94
- * 对于开始节点,不忽略 input 表达式,但替换工作流内部变量为工作流输入变量。
107
+ * 输出变量从结果中使用表达式获取
108
+ * 对于结束节点,不忽略 output 表达式,但表达式固定为取返回变量。
95
109
  */
96
- variables: index === 0 ? workflowInputVariables : workflowVariables,
110
+ const { variables: workflowNodeOutputVariables } = await (0, evaluate_variables_1.evaluateVariables)({
111
+ jsonSchema: workflowNodePropertiesOutputSchema,
112
+ variables: workflowNodeOutputExpressionEvaluateInput,
113
+ });
114
+ return {
115
+ nodeId: id,
116
+ variables: workflowNodeOutputVariables,
117
+ };
97
118
  });
98
- const { variables: workflowNodeOutputExpressionEvaluateInput } = await nodeExecute({
99
- variables: workflowNodeInputVariables,
100
- context: {
101
- workflowResourceInstance: this,
102
- },
119
+ // 等待当前层级的所有节点执行完成
120
+ const levelResults = await Promise.all(levelPromises);
121
+ // 将当前层级的结果更新到工作流变量映射中
122
+ levelResults.forEach((result) => {
123
+ workflowVariablesMap.set(result.nodeId, result.variables);
124
+ // 如果是结束节点,保存输出变量
125
+ if (result.nodeId === endNode.id) {
126
+ workflowOutputVariables = result.variables;
127
+ }
103
128
  });
104
- /**
105
- * 输出变量从结果中使用表达式获取
106
- * 对于结束节点,不忽略 output 表达式,但表达式固定为取返回变量。
107
- */
108
- const { variables: workflowNodeOutputVariables } = await (0, evaluate_variables_1.evaluateVariables)({
109
- jsonSchema: workflowNodePropertiesOutputSchema,
110
- variables: workflowNodeOutputExpressionEvaluateInput,
111
- });
112
- workflowVariablesMap.set(id, workflowNodeOutputVariables);
113
- if (index === orderedNodeListLength - 1) {
114
- workflowOutputVariables = workflowNodeOutputVariables;
115
- }
116
129
  }
117
130
  const res = {
118
131
  variables: workflowOutputVariables,
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/utils/evaluate-variables/index.test.ts"],"names":[],"mappings":""}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const evaluate_variables_1 = require("./evaluate-variables");
3
+ const _1 = require(".");
4
4
  const input = {
5
5
  Account: {
6
6
  "Account Name": "Firefly",
@@ -177,7 +177,7 @@ const jsonSchema = {
177
177
  },
178
178
  };
179
179
  test("evaluate variables", async () => {
180
- const evaluateVariablesRes = await (0, evaluate_variables_1.evaluateVariables)({
180
+ const evaluateVariablesRes = await (0, _1.evaluateVariables)({
181
181
  jsonSchema,
182
182
  variables: input,
183
183
  });
@@ -0,0 +1,17 @@
1
+ import { WorkflowData, WorkflowNodeInfo } from "../types";
2
+ interface GetExecutionLevelsParams {
3
+ workflowData: WorkflowData;
4
+ startNode: WorkflowNodeInfo;
5
+ endNode: WorkflowNodeInfo;
6
+ }
7
+ interface GetExecutionLevelsRes {
8
+ executionLevels: WorkflowNodeInfo[][];
9
+ }
10
+ /**
11
+ * 获取执行层级列表,用于并行执行
12
+ * 根据节点依赖关系构建执行层级,同一层级的节点可以并行执行
13
+ * 例如:连线如 1-2、1-3、2-4、3-4、4-5 时,需要 2、3 均执行完成后才执行 4
14
+ */
15
+ export declare const getExecutionLevels: (params: GetExecutionLevelsParams) => GetExecutionLevelsRes;
16
+ export {};
17
+ //# sourceMappingURL=get-execution-levels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-execution-levels.d.ts","sourceRoot":"","sources":["../../src/utils/get-execution-levels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE1D,UAAU,wBAAwB;IAChC,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,OAAO,EAAE,gBAAgB,CAAC;CAC3B;AAED,UAAU,qBAAqB;IAC7B,eAAe,EAAE,gBAAgB,EAAE,EAAE,CAAC;CACvC;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,wBAAwB,KAC/B,qBAuJF,CAAC"}
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getExecutionLevels = void 0;
4
+ /**
5
+ * 获取执行层级列表,用于并行执行
6
+ * 根据节点依赖关系构建执行层级,同一层级的节点可以并行执行
7
+ * 例如:连线如 1-2、1-3、2-4、3-4、4-5 时,需要 2、3 均执行完成后才执行 4
8
+ */
9
+ const getExecutionLevels = (params) => {
10
+ const { workflowData, startNode, endNode } = params;
11
+ const { edges, nodes } = workflowData;
12
+ // 构建节点映射
13
+ const nodeMap = new Map();
14
+ nodes.forEach((node) => {
15
+ nodeMap.set(node.id, node);
16
+ });
17
+ // 构建依赖关系图
18
+ const dependencies = new Map(); // 节点 -> 依赖的节点集合
19
+ const dependents = new Map(); // 节点 -> 被依赖的节点集合
20
+ // 初始化所有节点的依赖和被依赖集合
21
+ nodes.forEach((node) => {
22
+ dependencies.set(node.id, new Set());
23
+ dependents.set(node.id, new Set());
24
+ });
25
+ // 根据边构建依赖关系
26
+ edges.forEach((edge) => {
27
+ var _a, _b;
28
+ const { sourceWorkflowNodeResourceId, targetWorkflowNodeResourceId } = edge;
29
+ // target 依赖于 source
30
+ (_a = dependencies.get(targetWorkflowNodeResourceId)) === null || _a === void 0 ? void 0 : _a.add(sourceWorkflowNodeResourceId);
31
+ // source 被 target 依赖
32
+ (_b = dependents.get(sourceWorkflowNodeResourceId)) === null || _b === void 0 ? void 0 : _b.add(targetWorkflowNodeResourceId);
33
+ });
34
+ // 使用拓扑排序构建执行层级
35
+ const executionLevels = [];
36
+ const completedNodes = new Set();
37
+ const remainingDependencies = new Map();
38
+ // 首先进行循环依赖检测
39
+ const visited = new Set();
40
+ const recursionStack = new Set();
41
+ function hasCycle(nodeId) {
42
+ if (recursionStack.has(nodeId)) {
43
+ return true; // 发现循环依赖
44
+ }
45
+ if (visited.has(nodeId)) {
46
+ return false; // 已经检查过,没有循环
47
+ }
48
+ visited.add(nodeId);
49
+ recursionStack.add(nodeId);
50
+ const deps = dependencies.get(nodeId);
51
+ if (deps) {
52
+ for (const dep of deps) {
53
+ if (hasCycle(dep)) {
54
+ return true;
55
+ }
56
+ }
57
+ }
58
+ recursionStack.delete(nodeId);
59
+ return false;
60
+ }
61
+ // 检查所有节点是否存在循环依赖
62
+ for (const nodeId of dependencies.keys()) {
63
+ if (hasCycle(nodeId)) {
64
+ throw new Error("Circular dependency detected in workflow");
65
+ }
66
+ }
67
+ // 复制依赖关系到剩余依赖映射
68
+ dependencies.forEach((deps, nodeId) => {
69
+ remainingDependencies.set(nodeId, new Set(deps));
70
+ });
71
+ // 开始节点必须在第一层
72
+ const firstLevel = [startNode];
73
+ executionLevels.push(firstLevel);
74
+ completedNodes.add(startNode.id);
75
+ // 更新剩余依赖
76
+ remainingDependencies.delete(startNode.id);
77
+ remainingDependencies.forEach((deps) => {
78
+ deps.delete(startNode.id);
79
+ });
80
+ // 构建后续层级
81
+ while (remainingDependencies.size > 0) {
82
+ const currentLevel = [];
83
+ const nodesToProcess = [];
84
+ // 找到所有依赖已满足的节点
85
+ remainingDependencies.forEach((deps, nodeId) => {
86
+ if (deps.size === 0) {
87
+ nodesToProcess.push(nodeId);
88
+ const node = nodeMap.get(nodeId);
89
+ if (node) {
90
+ currentLevel.push(node);
91
+ }
92
+ }
93
+ });
94
+ // 如果没有找到可以执行的节点,说明存在循环依赖
95
+ if (currentLevel.length === 0) {
96
+ throw new Error("Circular dependency detected in workflow");
97
+ }
98
+ executionLevels.push(currentLevel);
99
+ // 标记当前层级的节点为已完成
100
+ nodesToProcess.forEach((nodeId) => {
101
+ completedNodes.add(nodeId);
102
+ remainingDependencies.delete(nodeId);
103
+ });
104
+ // 更新剩余依赖
105
+ const remainingDepsArray = Array.from(remainingDependencies.entries());
106
+ remainingDepsArray.forEach(([nodeId, deps]) => {
107
+ nodesToProcess.forEach((processedNodeId) => {
108
+ deps.delete(processedNodeId);
109
+ });
110
+ remainingDependencies.set(nodeId, deps);
111
+ });
112
+ }
113
+ // 确保结束节点在最后一层
114
+ if (executionLevels.length > 0) {
115
+ const lastLevel = executionLevels[executionLevels.length - 1];
116
+ const endNodeInLastLevel = lastLevel.some(node => node.id === endNode.id);
117
+ if (!endNodeInLastLevel) {
118
+ // 如果结束节点不在最后一层,需要检查是否已经在某一层中
119
+ let endNodeFound = false;
120
+ for (const level of executionLevels) {
121
+ if (level.some(node => node.id === endNode.id)) {
122
+ endNodeFound = true;
123
+ break;
124
+ }
125
+ }
126
+ if (!endNodeFound) {
127
+ // 如果结束节点不在任何层级中,添加到最后一层
128
+ executionLevels.push([endNode]);
129
+ }
130
+ }
131
+ }
132
+ return {
133
+ executionLevels,
134
+ };
135
+ };
136
+ exports.getExecutionLevels = getExecutionLevels;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=get-execution-levels.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-execution-levels.test.d.ts","sourceRoot":"","sources":["../../src/utils/get-execution-levels.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const get_execution_levels_1 = require("./get-execution-levels");
4
+ // 创建测试节点
5
+ const createTestNode = (id) => ({
6
+ id,
7
+ propertiesSchema: {
8
+ type: "object",
9
+ properties: {
10
+ input: { type: "object" },
11
+ output: { type: "object" },
12
+ },
13
+ },
14
+ execute: async () => ({ variables: {} }),
15
+ });
16
+ describe("getExecutionLevels", () => {
17
+ test("should create correct execution levels for parallel workflow", () => {
18
+ // 测试场景:1-2, 1-3, 2-4, 3-4, 4-5
19
+ // 期望的层级:
20
+ // Level 0: [1]
21
+ // Level 1: [2, 3] (并行)
22
+ // Level 2: [4] (需要2,3都完成)
23
+ // Level 3: [5]
24
+ const nodes = [
25
+ createTestNode("node-1"),
26
+ createTestNode("node-2"),
27
+ createTestNode("node-3"),
28
+ createTestNode("node-4"),
29
+ createTestNode("node-5"),
30
+ ];
31
+ const workflowData = {
32
+ edges: [
33
+ {
34
+ sourceWorkflowNodeResourceId: "node-1",
35
+ targetWorkflowNodeResourceId: "node-2",
36
+ },
37
+ {
38
+ sourceWorkflowNodeResourceId: "node-1",
39
+ targetWorkflowNodeResourceId: "node-3",
40
+ },
41
+ {
42
+ sourceWorkflowNodeResourceId: "node-2",
43
+ targetWorkflowNodeResourceId: "node-4",
44
+ },
45
+ {
46
+ sourceWorkflowNodeResourceId: "node-3",
47
+ targetWorkflowNodeResourceId: "node-4",
48
+ },
49
+ {
50
+ sourceWorkflowNodeResourceId: "node-4",
51
+ targetWorkflowNodeResourceId: "node-5",
52
+ },
53
+ ],
54
+ nodes,
55
+ };
56
+ const startNode = nodes[0]; // node-1
57
+ const endNode = nodes[4]; // node-5
58
+ const { executionLevels } = (0, get_execution_levels_1.getExecutionLevels)({
59
+ workflowData,
60
+ startNode,
61
+ endNode,
62
+ });
63
+ // 验证层级数量
64
+ expect(executionLevels.length).toBe(4);
65
+ // 验证第一层(只有node-1)
66
+ expect(executionLevels[0]).toHaveLength(1);
67
+ expect(executionLevels[0][0].id).toBe("node-1");
68
+ // 验证第二层(node-2和node-3,可以并行)
69
+ expect(executionLevels[1]).toHaveLength(2);
70
+ const level2NodeIds = executionLevels[1].map((node) => node.id).sort();
71
+ expect(level2NodeIds).toEqual(["node-2", "node-3"]);
72
+ // 验证第三层(只有node-4,需要2,3都完成)
73
+ expect(executionLevels[2]).toHaveLength(1);
74
+ expect(executionLevels[2][0].id).toBe("node-4");
75
+ // 验证第四层(只有node-5)
76
+ expect(executionLevels[3]).toHaveLength(1);
77
+ expect(executionLevels[3][0].id).toBe("node-5");
78
+ });
79
+ test("should handle simple sequential workflow", () => {
80
+ // 测试简单的顺序工作流:1-2-3
81
+ const nodes = [
82
+ createTestNode("node-1"),
83
+ createTestNode("node-2"),
84
+ createTestNode("node-3"),
85
+ ];
86
+ const workflowData = {
87
+ edges: [
88
+ {
89
+ sourceWorkflowNodeResourceId: "node-1",
90
+ targetWorkflowNodeResourceId: "node-2",
91
+ },
92
+ {
93
+ sourceWorkflowNodeResourceId: "node-2",
94
+ targetWorkflowNodeResourceId: "node-3",
95
+ },
96
+ ],
97
+ nodes,
98
+ };
99
+ const startNode = nodes[0]; // node-1
100
+ const endNode = nodes[2]; // node-3
101
+ const { executionLevels } = (0, get_execution_levels_1.getExecutionLevels)({
102
+ workflowData,
103
+ startNode,
104
+ endNode,
105
+ });
106
+ // 验证层级数量
107
+ expect(executionLevels.length).toBe(3);
108
+ // 每层只有一个节点
109
+ executionLevels.forEach((level, index) => {
110
+ expect(level).toHaveLength(1);
111
+ expect(level[0].id).toBe(`node-${index + 1}`);
112
+ });
113
+ });
114
+ test("should handle complex parallel workflow", () => {
115
+ // 测试更复杂的并行工作流:1-2, 1-3, 1-4, 2-5, 3-5, 4-6, 5-6, 6-7
116
+ // 期望的层级:
117
+ // Level 0: [1]
118
+ // Level 1: [2, 3, 4] (并行)
119
+ // Level 2: [5] (需要2,3完成)
120
+ // Level 3: [6] (需要4,5完成)
121
+ // Level 4: [7]
122
+ const nodes = [
123
+ createTestNode("node-1"),
124
+ createTestNode("node-2"),
125
+ createTestNode("node-3"),
126
+ createTestNode("node-4"),
127
+ createTestNode("node-5"),
128
+ createTestNode("node-6"),
129
+ createTestNode("node-7"),
130
+ ];
131
+ const workflowData = {
132
+ edges: [
133
+ {
134
+ sourceWorkflowNodeResourceId: "node-1",
135
+ targetWorkflowNodeResourceId: "node-2",
136
+ },
137
+ {
138
+ sourceWorkflowNodeResourceId: "node-1",
139
+ targetWorkflowNodeResourceId: "node-3",
140
+ },
141
+ {
142
+ sourceWorkflowNodeResourceId: "node-1",
143
+ targetWorkflowNodeResourceId: "node-4",
144
+ },
145
+ {
146
+ sourceWorkflowNodeResourceId: "node-2",
147
+ targetWorkflowNodeResourceId: "node-5",
148
+ },
149
+ {
150
+ sourceWorkflowNodeResourceId: "node-3",
151
+ targetWorkflowNodeResourceId: "node-5",
152
+ },
153
+ {
154
+ sourceWorkflowNodeResourceId: "node-4",
155
+ targetWorkflowNodeResourceId: "node-6",
156
+ },
157
+ {
158
+ sourceWorkflowNodeResourceId: "node-5",
159
+ targetWorkflowNodeResourceId: "node-6",
160
+ },
161
+ {
162
+ sourceWorkflowNodeResourceId: "node-6",
163
+ targetWorkflowNodeResourceId: "node-7",
164
+ },
165
+ ],
166
+ nodes,
167
+ };
168
+ const startNode = nodes[0]; // node-1
169
+ const endNode = nodes[6]; // node-7
170
+ const { executionLevels } = (0, get_execution_levels_1.getExecutionLevels)({
171
+ workflowData,
172
+ startNode,
173
+ endNode,
174
+ });
175
+ // 验证层级数量
176
+ expect(executionLevels.length).toBe(5);
177
+ // 验证第一层(只有node-1)
178
+ expect(executionLevels[0]).toHaveLength(1);
179
+ expect(executionLevels[0][0].id).toBe("node-1");
180
+ // 验证第二层(node-2, node-3, node-4,可以并行)
181
+ expect(executionLevels[1]).toHaveLength(3);
182
+ const level2NodeIds = executionLevels[1].map((node) => node.id).sort();
183
+ expect(level2NodeIds).toEqual(["node-2", "node-3", "node-4"]);
184
+ // 验证第三层(只有node-5,需要2,3完成)
185
+ expect(executionLevels[2]).toHaveLength(1);
186
+ expect(executionLevels[2][0].id).toBe("node-5");
187
+ // 验证第四层(只有node-6,需要4,5完成)
188
+ expect(executionLevels[3]).toHaveLength(1);
189
+ expect(executionLevels[3][0].id).toBe("node-6");
190
+ // 验证第五层(只有node-7)
191
+ expect(executionLevels[4]).toHaveLength(1);
192
+ expect(executionLevels[4][0].id).toBe("node-7");
193
+ });
194
+ test("should throw error for circular dependency", () => {
195
+ // 测试循环依赖:1-2, 2-3, 3-1
196
+ const nodes = [
197
+ createTestNode("node-1"),
198
+ createTestNode("node-2"),
199
+ createTestNode("node-3"),
200
+ ];
201
+ const workflowData = {
202
+ edges: [
203
+ {
204
+ sourceWorkflowNodeResourceId: "node-1",
205
+ targetWorkflowNodeResourceId: "node-2",
206
+ },
207
+ {
208
+ sourceWorkflowNodeResourceId: "node-2",
209
+ targetWorkflowNodeResourceId: "node-3",
210
+ },
211
+ {
212
+ sourceWorkflowNodeResourceId: "node-3",
213
+ targetWorkflowNodeResourceId: "node-1",
214
+ },
215
+ ],
216
+ nodes,
217
+ };
218
+ const startNode = nodes[0]; // node-1
219
+ const endNode = nodes[2]; // node-3
220
+ expect(() => {
221
+ (0, get_execution_levels_1.getExecutionLevels)({
222
+ workflowData,
223
+ startNode,
224
+ endNode,
225
+ });
226
+ }).toThrow("Circular dependency detected in workflow");
227
+ });
228
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ldsg/workflow",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -17,15 +17,15 @@
17
17
  "json-schema": "0.4.0",
18
18
  "json-schema-default": "^1.0.2",
19
19
  "lodash": "^4.17.21",
20
- "@ldsg/handler": "0.3.8",
21
- "@ldsg/resource": "0.3.8",
22
- "@ldsg/resource-definition": "0.3.8"
20
+ "@ldsg/handler": "0.3.9",
21
+ "@ldsg/resource": "0.3.9",
22
+ "@ldsg/resource-definition": "0.3.9"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/flat": "^5.0.5",
26
26
  "@types/json-schema": "^7.0.15",
27
27
  "@types/lodash": "^4.14.202",
28
- "@ldsg/types": "0.3.8"
28
+ "@ldsg/types": "0.3.9"
29
29
  },
30
30
  "scripts": {
31
31
  "build": "tsc --project tsconfig.build.json",
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=evaluate-variables.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"evaluate-variables.test.d.ts","sourceRoot":"","sources":["../../src/utils/evaluate-variables.test.ts"],"names":[],"mappings":""}
@@ -1,19 +0,0 @@
1
- import { WorkflowData, WorkflowNodeInfo } from "../types";
2
- interface GetOrderedNodeListParams {
3
- workflowData: WorkflowData;
4
- startNode: WorkflowNodeInfo;
5
- endNode: WorkflowNodeInfo;
6
- }
7
- interface GetOrderedNodeListRes {
8
- orderedNodeList: WorkflowNodeInfo[];
9
- }
10
- type GetOrderedNodeList = (params: GetOrderedNodeListParams) => GetOrderedNodeListRes;
11
- /**
12
- * 获取有序的节点列表
13
- * 暂时先实现单线工作流,且节点只有开始与目标端口各一个。
14
- * 1. 找到开始与结束节点
15
- * 2. 从开始节点逐一向后查找
16
- */
17
- export declare const getOrderedNodeList: GetOrderedNodeList;
18
- export {};
19
- //# sourceMappingURL=get-ordered-node-list.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"get-ordered-node-list.d.ts","sourceRoot":"","sources":["../../src/utils/get-ordered-node-list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE1D,UAAU,wBAAwB;IAChC,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,OAAO,EAAE,gBAAgB,CAAC;CAC3B;AAED,UAAU,qBAAqB;IAC7B,eAAe,EAAE,gBAAgB,EAAE,CAAC;CACrC;AAED,KAAK,kBAAkB,GAAG,CACxB,MAAM,EAAE,wBAAwB,KAC7B,qBAAqB,CAAC;AAE3B;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,kBA4ChC,CAAC"}
@@ -1,41 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getOrderedNodeList = void 0;
4
- /**
5
- * 获取有序的节点列表
6
- * 暂时先实现单线工作流,且节点只有开始与目标端口各一个。
7
- * 1. 找到开始与结束节点
8
- * 2. 从开始节点逐一向后查找
9
- */
10
- const getOrderedNodeList = (params) => {
11
- const { workflowData, startNode, endNode } = params;
12
- const { edges, nodes } = workflowData;
13
- const orderedNodeList = [startNode, endNode];
14
- let currentNodeId = startNode.id;
15
- let pending = true;
16
- do {
17
- const currentEdge = edges.find((edge) => edge.sourceWorkflowNodeResourceId === currentNodeId);
18
- if (!currentEdge) {
19
- throw new Error("invalid edge");
20
- }
21
- const { targetWorkflowNodeResourceId } = currentEdge;
22
- const targetWorkflowNode = nodes.find((node) => node.id === targetWorkflowNodeResourceId);
23
- if (targetWorkflowNode) {
24
- currentNodeId = targetWorkflowNode.id;
25
- if (currentNodeId === endNode.id) {
26
- pending = false;
27
- }
28
- else {
29
- orderedNodeList.splice(-1, 0, targetWorkflowNode);
30
- }
31
- }
32
- else {
33
- throw new Error("invalid edge");
34
- }
35
- } while (pending);
36
- const res = {
37
- orderedNodeList,
38
- };
39
- return res;
40
- };
41
- exports.getOrderedNodeList = getOrderedNodeList;
@@ -1,2 +0,0 @@
1
- export * from "./get-ordered-node-list";
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC"}
@@ -1,17 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./get-ordered-node-list"), exports);