@flowgram.ai/runtime-js 0.2.24 → 0.2.26
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.
- package/dist/esm/index.js +355 -33
- package/dist/esm/index.js.map +1 -1
- package/dist/index.js +362 -40
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
- /package/dist/{index.d.mts → index.d.cts} +0 -0
package/dist/index.js
CHANGED
|
@@ -40,13 +40,13 @@ __export(src_exports, {
|
|
|
40
40
|
module.exports = __toCommonJS(src_exports);
|
|
41
41
|
|
|
42
42
|
// ../interface/dist/esm/index.js
|
|
43
|
-
var import_zod = __toESM(require("zod"));
|
|
44
|
-
var import_zod2 = __toESM(require("zod"));
|
|
45
|
-
var import_zod3 = __toESM(require("zod"));
|
|
46
|
-
var import_zod4 = __toESM(require("zod"));
|
|
47
|
-
var import_zod5 = __toESM(require("zod"));
|
|
48
|
-
var import_zod6 = __toESM(require("zod"));
|
|
49
|
-
var import_zod7 = __toESM(require("zod"));
|
|
43
|
+
var import_zod = __toESM(require("zod"), 1);
|
|
44
|
+
var import_zod2 = __toESM(require("zod"), 1);
|
|
45
|
+
var import_zod3 = __toESM(require("zod"), 1);
|
|
46
|
+
var import_zod4 = __toESM(require("zod"), 1);
|
|
47
|
+
var import_zod5 = __toESM(require("zod"), 1);
|
|
48
|
+
var import_zod6 = __toESM(require("zod"), 1);
|
|
49
|
+
var import_zod7 = __toESM(require("zod"), 1);
|
|
50
50
|
var WorkflowIOZodSchema = import_zod2.default.record(import_zod2.default.string(), import_zod2.default.any());
|
|
51
51
|
var WorkflowSnapshotZodSchema = import_zod2.default.object({
|
|
52
52
|
id: import_zod2.default.string(),
|
|
@@ -345,7 +345,7 @@ var WorkflowRuntimeType;
|
|
|
345
345
|
const expectedType = types[0];
|
|
346
346
|
types.forEach((type) => {
|
|
347
347
|
if (type !== expectedType) {
|
|
348
|
-
throw new Error(`
|
|
348
|
+
throw new Error(`Array items type must be same, expect ${expectedType}, but got ${type}`);
|
|
349
349
|
}
|
|
350
350
|
});
|
|
351
351
|
return expectedType;
|
|
@@ -503,8 +503,8 @@ var validateObject = (value, schema, path) => {
|
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
if (schema.properties) {
|
|
506
|
-
for (const [propertyName
|
|
507
|
-
const isRequired =
|
|
506
|
+
for (const [propertyName] of Object.entries(schema.properties)) {
|
|
507
|
+
const isRequired = schema.required?.includes(propertyName) ?? false;
|
|
508
508
|
if (isRequired && !(propertyName in objectValue)) {
|
|
509
509
|
return {
|
|
510
510
|
result: false,
|
|
@@ -614,7 +614,7 @@ var LoopExecutor = class {
|
|
|
614
614
|
const subNodes = context.node.children;
|
|
615
615
|
const blockStartNode = subNodes.find((node) => node.type === FlowGramNode.BlockStart);
|
|
616
616
|
if (!blockStartNode) {
|
|
617
|
-
throw new Error("block start node not found");
|
|
617
|
+
throw new Error("Loop block start node not found");
|
|
618
618
|
}
|
|
619
619
|
const blockOutputs = [];
|
|
620
620
|
for (let index = 0; index < loopArray.length; index++) {
|
|
@@ -632,10 +632,14 @@ var LoopExecutor = class {
|
|
|
632
632
|
type: WorkflowVariableType.Number,
|
|
633
633
|
value: index
|
|
634
634
|
});
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
635
|
+
try {
|
|
636
|
+
await engine.executeNode({
|
|
637
|
+
context: subContext,
|
|
638
|
+
node: blockStartNode
|
|
639
|
+
});
|
|
640
|
+
} catch (e) {
|
|
641
|
+
throw new Error(`Loop block execute error`);
|
|
642
|
+
}
|
|
639
643
|
const blockOutput = this.getBlockOutput(context, subContext);
|
|
640
644
|
blockOutputs.push(blockOutput);
|
|
641
645
|
}
|
|
@@ -656,15 +660,15 @@ var LoopExecutor = class {
|
|
|
656
660
|
checkLoopArray(LoopArrayVariable) {
|
|
657
661
|
const loopArray = LoopArrayVariable?.value;
|
|
658
662
|
if (!loopArray || (0, import_lodash_es.isNil)(loopArray) || !Array.isArray(loopArray)) {
|
|
659
|
-
throw new Error("loopFor is required
|
|
663
|
+
throw new Error('Loop "loopFor" is required');
|
|
660
664
|
}
|
|
661
665
|
const loopArrayType = LoopArrayVariable.type;
|
|
662
666
|
if (loopArrayType !== WorkflowVariableType.Array) {
|
|
663
|
-
throw new Error("loopFor must be an array
|
|
667
|
+
throw new Error('Loop "loopFor" must be an array');
|
|
664
668
|
}
|
|
665
669
|
const loopArrayItemType = LoopArrayVariable.itemsType;
|
|
666
670
|
if ((0, import_lodash_es.isNil)(loopArrayItemType)) {
|
|
667
|
-
throw new Error("loopFor
|
|
671
|
+
throw new Error('Loop "loopFor.items" must be array items');
|
|
668
672
|
}
|
|
669
673
|
}
|
|
670
674
|
getBlockOutput(executionContext, subContext) {
|
|
@@ -725,13 +729,63 @@ var LoopExecutor = class {
|
|
|
725
729
|
var import_lodash_es2 = require("lodash-es");
|
|
726
730
|
var import_openai = require("@langchain/openai");
|
|
727
731
|
var import_messages = require("@langchain/core/messages");
|
|
732
|
+
|
|
733
|
+
// src/nodes/llm/api-validator.ts
|
|
734
|
+
var APIValidator;
|
|
735
|
+
((APIValidator2) => {
|
|
736
|
+
APIValidator2.isValidFormat = (apiHost) => {
|
|
737
|
+
if (!apiHost || typeof apiHost !== "string") {
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
try {
|
|
741
|
+
const url = new URL(apiHost);
|
|
742
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
743
|
+
} catch (error) {
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
APIValidator2.isExist = async (apiHost) => {
|
|
748
|
+
try {
|
|
749
|
+
const controller = new AbortController();
|
|
750
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
751
|
+
await fetch(apiHost, {
|
|
752
|
+
method: "HEAD",
|
|
753
|
+
// Use HEAD to minimize data transfer
|
|
754
|
+
signal: controller.signal,
|
|
755
|
+
// Disable following redirects to get the actual host response
|
|
756
|
+
redirect: "manual"
|
|
757
|
+
});
|
|
758
|
+
clearTimeout(timeoutId);
|
|
759
|
+
return true;
|
|
760
|
+
} catch (error) {
|
|
761
|
+
if (error.name === "AbortError") {
|
|
762
|
+
return false;
|
|
763
|
+
}
|
|
764
|
+
const errorMessage = error.message?.toLowerCase() || "";
|
|
765
|
+
const networkFailurePatterns = [
|
|
766
|
+
"network error",
|
|
767
|
+
"connection refused",
|
|
768
|
+
"dns",
|
|
769
|
+
"resolve",
|
|
770
|
+
"timeout",
|
|
771
|
+
"unreachable"
|
|
772
|
+
];
|
|
773
|
+
const isNetworkFailure = networkFailurePatterns.some(
|
|
774
|
+
(pattern) => errorMessage.includes(pattern)
|
|
775
|
+
);
|
|
776
|
+
return !isNetworkFailure;
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
})(APIValidator || (APIValidator = {}));
|
|
780
|
+
|
|
781
|
+
// src/nodes/llm/index.ts
|
|
728
782
|
var LLMExecutor = class {
|
|
729
783
|
constructor() {
|
|
730
784
|
this.type = FlowGramNode.LLM;
|
|
731
785
|
}
|
|
732
786
|
async execute(context) {
|
|
733
787
|
const inputs = context.inputs;
|
|
734
|
-
this.checkInputs(inputs);
|
|
788
|
+
await this.checkInputs(inputs);
|
|
735
789
|
const { modelName, temperature, apiKey, apiHost, systemPrompt, prompt } = inputs;
|
|
736
790
|
const model = new import_openai.ChatOpenAI({
|
|
737
791
|
modelName,
|
|
@@ -746,7 +800,16 @@ var LLMExecutor = class {
|
|
|
746
800
|
messages.push(new import_messages.SystemMessage(systemPrompt));
|
|
747
801
|
}
|
|
748
802
|
messages.push(new import_messages.HumanMessage(prompt));
|
|
749
|
-
|
|
803
|
+
let apiMessage;
|
|
804
|
+
try {
|
|
805
|
+
apiMessage = await model.invoke(messages);
|
|
806
|
+
} catch (error) {
|
|
807
|
+
const errorMessage = error?.message;
|
|
808
|
+
if (errorMessage === "Connection error.") {
|
|
809
|
+
throw new Error(`Network error: unreachable api "${apiHost}"`);
|
|
810
|
+
}
|
|
811
|
+
throw error;
|
|
812
|
+
}
|
|
750
813
|
const result = apiMessage.content;
|
|
751
814
|
return {
|
|
752
815
|
outputs: {
|
|
@@ -754,16 +817,23 @@ var LLMExecutor = class {
|
|
|
754
817
|
}
|
|
755
818
|
};
|
|
756
819
|
}
|
|
757
|
-
checkInputs(inputs) {
|
|
820
|
+
async checkInputs(inputs) {
|
|
758
821
|
const { modelName, temperature, apiKey, apiHost, prompt } = inputs;
|
|
759
822
|
const missingInputs = [];
|
|
760
|
-
if (
|
|
823
|
+
if (!modelName) missingInputs.push("modelName");
|
|
761
824
|
if ((0, import_lodash_es2.isNil)(temperature)) missingInputs.push("temperature");
|
|
762
|
-
if (
|
|
763
|
-
if (
|
|
764
|
-
if (
|
|
825
|
+
if (!apiKey) missingInputs.push("apiKey");
|
|
826
|
+
if (!apiHost) missingInputs.push("apiHost");
|
|
827
|
+
if (!prompt) missingInputs.push("prompt");
|
|
765
828
|
if (missingInputs.length > 0) {
|
|
766
|
-
throw new Error(`LLM node missing required inputs: ${missingInputs.join(", ")}`);
|
|
829
|
+
throw new Error(`LLM node missing required inputs: "${missingInputs.join('", "')}"`);
|
|
830
|
+
}
|
|
831
|
+
if (!APIValidator.isValidFormat(apiHost)) {
|
|
832
|
+
throw new Error(`Invalid API host format - ${apiHost}`);
|
|
833
|
+
}
|
|
834
|
+
const apiHostExists = await APIValidator.isExist(apiHost);
|
|
835
|
+
if (!apiHostExists) {
|
|
836
|
+
throw new Error(`Unreachable API host - ${apiHost}`);
|
|
767
837
|
}
|
|
768
838
|
}
|
|
769
839
|
};
|
|
@@ -1058,7 +1128,7 @@ var ConditionExecutor = class {
|
|
|
1058
1128
|
const parsedConditions = conditions.map((item) => this.parseCondition(item, context)).filter((item) => this.checkCondition(item));
|
|
1059
1129
|
const activatedCondition = parsedConditions.find((item) => this.handleCondition(item));
|
|
1060
1130
|
if (!activatedCondition) {
|
|
1061
|
-
throw new Error("
|
|
1131
|
+
throw new Error("No condition is activated");
|
|
1062
1132
|
}
|
|
1063
1133
|
return {
|
|
1064
1134
|
outputs: {},
|
|
@@ -1086,11 +1156,13 @@ var ConditionExecutor = class {
|
|
|
1086
1156
|
checkCondition(condition) {
|
|
1087
1157
|
const rule = conditionRules[condition.leftType];
|
|
1088
1158
|
if ((0, import_lodash_es9.isNil)(rule)) {
|
|
1089
|
-
throw new Error(`
|
|
1159
|
+
throw new Error(`Condition left type "${condition.leftType}" is not supported`);
|
|
1090
1160
|
}
|
|
1091
1161
|
const ruleType = rule[condition.operator];
|
|
1092
1162
|
if ((0, import_lodash_es9.isNil)(ruleType)) {
|
|
1093
|
-
throw new Error(
|
|
1163
|
+
throw new Error(
|
|
1164
|
+
`Condition left type "${condition.leftType}" has no operator "${condition.operator}"`
|
|
1165
|
+
);
|
|
1094
1166
|
}
|
|
1095
1167
|
if (ruleType !== condition.rightType) {
|
|
1096
1168
|
return false;
|
|
@@ -1100,7 +1172,7 @@ var ConditionExecutor = class {
|
|
|
1100
1172
|
handleCondition(condition) {
|
|
1101
1173
|
const handler = conditionHandlers[condition.leftType];
|
|
1102
1174
|
if (!handler) {
|
|
1103
|
-
throw new Error(`
|
|
1175
|
+
throw new Error(`Condition left type ${condition.leftType} is not supported`);
|
|
1104
1176
|
}
|
|
1105
1177
|
const isActive = handler(condition);
|
|
1106
1178
|
return isActive;
|
|
@@ -1118,6 +1190,241 @@ var WorkflowRuntimeNodeExecutors = [
|
|
|
1118
1190
|
BlockEndExecutor
|
|
1119
1191
|
];
|
|
1120
1192
|
|
|
1193
|
+
// src/domain/validation/validators/cycle-detection.ts
|
|
1194
|
+
var cycleDetection = (schema) => {
|
|
1195
|
+
const { nodes, edges } = schema;
|
|
1196
|
+
const adjacencyList = /* @__PURE__ */ new Map();
|
|
1197
|
+
const nodeIds = new Set(nodes.map((node) => node.id));
|
|
1198
|
+
nodeIds.forEach((nodeId) => {
|
|
1199
|
+
adjacencyList.set(nodeId, []);
|
|
1200
|
+
});
|
|
1201
|
+
edges.forEach((edge) => {
|
|
1202
|
+
const sourceList = adjacencyList.get(edge.sourceNodeID);
|
|
1203
|
+
if (sourceList) {
|
|
1204
|
+
sourceList.push(edge.targetNodeID);
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
let NodeStatus;
|
|
1208
|
+
((NodeStatus2) => {
|
|
1209
|
+
NodeStatus2[NodeStatus2["Unvisited"] = 0] = "Unvisited";
|
|
1210
|
+
NodeStatus2[NodeStatus2["Visiting"] = 1] = "Visiting";
|
|
1211
|
+
NodeStatus2[NodeStatus2["Visited"] = 2] = "Visited";
|
|
1212
|
+
})(NodeStatus || (NodeStatus = {}));
|
|
1213
|
+
const nodeStatusMap = /* @__PURE__ */ new Map();
|
|
1214
|
+
nodeIds.forEach((nodeId) => {
|
|
1215
|
+
nodeStatusMap.set(nodeId, 0 /* Unvisited */);
|
|
1216
|
+
});
|
|
1217
|
+
const detectCycleFromNode = (nodeId) => {
|
|
1218
|
+
nodeStatusMap.set(nodeId, 1 /* Visiting */);
|
|
1219
|
+
const neighbors = adjacencyList.get(nodeId) || [];
|
|
1220
|
+
for (const neighbor of neighbors) {
|
|
1221
|
+
const neighborColor = nodeStatusMap.get(neighbor);
|
|
1222
|
+
if (neighborColor === 1 /* Visiting */) {
|
|
1223
|
+
return true;
|
|
1224
|
+
}
|
|
1225
|
+
if (neighborColor === 0 /* Unvisited */ && detectCycleFromNode(neighbor)) {
|
|
1226
|
+
return true;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
nodeStatusMap.set(nodeId, 2 /* Visited */);
|
|
1230
|
+
return false;
|
|
1231
|
+
};
|
|
1232
|
+
for (const nodeId of nodeIds) {
|
|
1233
|
+
if (nodeStatusMap.get(nodeId) === 0 /* Unvisited */) {
|
|
1234
|
+
if (detectCycleFromNode(nodeId)) {
|
|
1235
|
+
throw new Error("Workflow schema contains a cycle, which is not allowed");
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
nodes.forEach((node) => {
|
|
1240
|
+
if (node.blocks) {
|
|
1241
|
+
cycleDetection({
|
|
1242
|
+
nodes: node.blocks,
|
|
1243
|
+
edges: node.edges ?? []
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
};
|
|
1248
|
+
|
|
1249
|
+
// src/domain/validation/validators/start-end-node.ts
|
|
1250
|
+
var blockStartEndNode = (schema) => {
|
|
1251
|
+
const { blockStartNodes, blockEndNodes } = schema.nodes.reduce(
|
|
1252
|
+
(acc, node) => {
|
|
1253
|
+
if (node.type === FlowGramNode.BlockStart) {
|
|
1254
|
+
acc.blockStartNodes.push(node);
|
|
1255
|
+
} else if (node.type === FlowGramNode.BlockEnd) {
|
|
1256
|
+
acc.blockEndNodes.push(node);
|
|
1257
|
+
}
|
|
1258
|
+
return acc;
|
|
1259
|
+
},
|
|
1260
|
+
{ blockStartNodes: [], blockEndNodes: [] }
|
|
1261
|
+
);
|
|
1262
|
+
if (!blockStartNodes.length && !blockEndNodes.length) {
|
|
1263
|
+
throw new Error("Workflow block schema must have a block-start node and a block-end node");
|
|
1264
|
+
}
|
|
1265
|
+
if (!blockStartNodes.length) {
|
|
1266
|
+
throw new Error("Workflow block schema must have a block-start node");
|
|
1267
|
+
}
|
|
1268
|
+
if (!blockEndNodes.length) {
|
|
1269
|
+
throw new Error("Workflow block schema must have an block-end node");
|
|
1270
|
+
}
|
|
1271
|
+
if (blockStartNodes.length > 1) {
|
|
1272
|
+
throw new Error("Workflow block schema must have only one block-start node");
|
|
1273
|
+
}
|
|
1274
|
+
if (blockEndNodes.length > 1) {
|
|
1275
|
+
throw new Error("Workflow block schema must have only one block-end node");
|
|
1276
|
+
}
|
|
1277
|
+
schema.nodes.forEach((node) => {
|
|
1278
|
+
if (node.blocks) {
|
|
1279
|
+
blockStartEndNode({
|
|
1280
|
+
nodes: node.blocks,
|
|
1281
|
+
edges: node.edges ?? []
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
});
|
|
1285
|
+
};
|
|
1286
|
+
var startEndNode = (schema) => {
|
|
1287
|
+
const { startNodes, endNodes } = schema.nodes.reduce(
|
|
1288
|
+
(acc, node) => {
|
|
1289
|
+
if (node.type === FlowGramNode.Start) {
|
|
1290
|
+
acc.startNodes.push(node);
|
|
1291
|
+
} else if (node.type === FlowGramNode.End) {
|
|
1292
|
+
acc.endNodes.push(node);
|
|
1293
|
+
}
|
|
1294
|
+
return acc;
|
|
1295
|
+
},
|
|
1296
|
+
{ startNodes: [], endNodes: [] }
|
|
1297
|
+
);
|
|
1298
|
+
if (!startNodes.length && !endNodes.length) {
|
|
1299
|
+
throw new Error("Workflow schema must have a start node and an end node");
|
|
1300
|
+
}
|
|
1301
|
+
if (!startNodes.length) {
|
|
1302
|
+
throw new Error("Workflow schema must have a start node");
|
|
1303
|
+
}
|
|
1304
|
+
if (!endNodes.length) {
|
|
1305
|
+
throw new Error("Workflow schema must have an end node");
|
|
1306
|
+
}
|
|
1307
|
+
if (startNodes.length > 1) {
|
|
1308
|
+
throw new Error("Workflow schema must have only one start node");
|
|
1309
|
+
}
|
|
1310
|
+
if (endNodes.length > 1) {
|
|
1311
|
+
throw new Error("Workflow schema must have only one end node");
|
|
1312
|
+
}
|
|
1313
|
+
schema.nodes.forEach((node) => {
|
|
1314
|
+
if (node.blocks) {
|
|
1315
|
+
blockStartEndNode({
|
|
1316
|
+
nodes: node.blocks,
|
|
1317
|
+
edges: node.edges ?? []
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
});
|
|
1321
|
+
};
|
|
1322
|
+
|
|
1323
|
+
// src/domain/validation/validators/edge-source-target-exist.ts
|
|
1324
|
+
var edgeSourceTargetExist = (schema) => {
|
|
1325
|
+
const { nodes, edges } = schema;
|
|
1326
|
+
const nodeSet = new Set(nodes.map((node) => node.id));
|
|
1327
|
+
edges.forEach((edge) => {
|
|
1328
|
+
if (!nodeSet.has(edge.sourceNodeID)) {
|
|
1329
|
+
throw new Error(`Workflow schema edge source node "${edge.sourceNodeID}" not exist`);
|
|
1330
|
+
}
|
|
1331
|
+
if (!nodeSet.has(edge.targetNodeID)) {
|
|
1332
|
+
throw new Error(`Workflow schema edge target node "${edge.targetNodeID}" not exist`);
|
|
1333
|
+
}
|
|
1334
|
+
});
|
|
1335
|
+
nodes.forEach((node) => {
|
|
1336
|
+
if (node.blocks) {
|
|
1337
|
+
edgeSourceTargetExist({
|
|
1338
|
+
nodes: node.blocks,
|
|
1339
|
+
edges: node.edges ?? []
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
// src/domain/validation/validators/schema-format.ts
|
|
1346
|
+
var schemaFormat = (schema) => {
|
|
1347
|
+
if (!schema || typeof schema !== "object") {
|
|
1348
|
+
throw new Error("Workflow schema must be a valid object");
|
|
1349
|
+
}
|
|
1350
|
+
if (!Array.isArray(schema.nodes)) {
|
|
1351
|
+
throw new Error("Workflow schema must have a valid nodes array");
|
|
1352
|
+
}
|
|
1353
|
+
if (!Array.isArray(schema.edges)) {
|
|
1354
|
+
throw new Error("Workflow schema must have a valid edges array");
|
|
1355
|
+
}
|
|
1356
|
+
schema.nodes.forEach((node, index) => {
|
|
1357
|
+
validateNodeFormat(node, `nodes[${index}]`);
|
|
1358
|
+
});
|
|
1359
|
+
schema.edges.forEach((edge, index) => {
|
|
1360
|
+
validateEdgeFormat(edge, `edges[${index}]`);
|
|
1361
|
+
});
|
|
1362
|
+
schema.nodes.forEach((node, nodeIndex) => {
|
|
1363
|
+
if (node.blocks) {
|
|
1364
|
+
if (!Array.isArray(node.blocks)) {
|
|
1365
|
+
throw new Error(`Node nodes[${nodeIndex}].blocks must be an array`);
|
|
1366
|
+
}
|
|
1367
|
+
const nestedSchema = {
|
|
1368
|
+
nodes: node.blocks,
|
|
1369
|
+
edges: node.edges || []
|
|
1370
|
+
};
|
|
1371
|
+
schemaFormat(nestedSchema);
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1374
|
+
};
|
|
1375
|
+
var validateNodeFormat = (node, path) => {
|
|
1376
|
+
if (!node || typeof node !== "object") {
|
|
1377
|
+
throw new Error(`${path} must be a valid object`);
|
|
1378
|
+
}
|
|
1379
|
+
if (typeof node.id !== "string" || !node.id.trim()) {
|
|
1380
|
+
throw new Error(`${path}.id must be a non-empty string`);
|
|
1381
|
+
}
|
|
1382
|
+
if (typeof node.type !== "string" || !node.type.trim()) {
|
|
1383
|
+
throw new Error(`${path}.type must be a non-empty string`);
|
|
1384
|
+
}
|
|
1385
|
+
if (!node.meta || typeof node.meta !== "object") {
|
|
1386
|
+
throw new Error(`${path}.meta must be a valid object`);
|
|
1387
|
+
}
|
|
1388
|
+
if (!node.data || typeof node.data !== "object") {
|
|
1389
|
+
throw new Error(`${path}.data must be a valid object`);
|
|
1390
|
+
}
|
|
1391
|
+
if (node.blocks !== void 0 && !Array.isArray(node.blocks)) {
|
|
1392
|
+
throw new Error(`${path}.blocks must be an array if present`);
|
|
1393
|
+
}
|
|
1394
|
+
if (node.edges !== void 0 && !Array.isArray(node.edges)) {
|
|
1395
|
+
throw new Error(`${path}.edges must be an array if present`);
|
|
1396
|
+
}
|
|
1397
|
+
if (node.data.inputs !== void 0 && (typeof node.data.inputs !== "object" || node.data.inputs === null)) {
|
|
1398
|
+
throw new Error(`${path}.data.inputs must be a valid object if present`);
|
|
1399
|
+
}
|
|
1400
|
+
if (node.data.outputs !== void 0 && (typeof node.data.outputs !== "object" || node.data.outputs === null)) {
|
|
1401
|
+
throw new Error(`${path}.data.outputs must be a valid object if present`);
|
|
1402
|
+
}
|
|
1403
|
+
if (node.data.inputsValues !== void 0 && (typeof node.data.inputsValues !== "object" || node.data.inputsValues === null)) {
|
|
1404
|
+
throw new Error(`${path}.data.inputsValues must be a valid object if present`);
|
|
1405
|
+
}
|
|
1406
|
+
if (node.data.title !== void 0 && typeof node.data.title !== "string") {
|
|
1407
|
+
throw new Error(`${path}.data.title must be a string if present`);
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
var validateEdgeFormat = (edge, path) => {
|
|
1411
|
+
if (!edge || typeof edge !== "object") {
|
|
1412
|
+
throw new Error(`${path} must be a valid object`);
|
|
1413
|
+
}
|
|
1414
|
+
if (typeof edge.sourceNodeID !== "string" || !edge.sourceNodeID.trim()) {
|
|
1415
|
+
throw new Error(`${path}.sourceNodeID must be a non-empty string`);
|
|
1416
|
+
}
|
|
1417
|
+
if (typeof edge.targetNodeID !== "string" || !edge.targetNodeID.trim()) {
|
|
1418
|
+
throw new Error(`${path}.targetNodeID must be a non-empty string`);
|
|
1419
|
+
}
|
|
1420
|
+
if (edge.sourcePortID !== void 0 && typeof edge.sourcePortID !== "string") {
|
|
1421
|
+
throw new Error(`${path}.sourcePortID must be a string if present`);
|
|
1422
|
+
}
|
|
1423
|
+
if (edge.targetPortID !== void 0 && typeof edge.targetPortID !== "string") {
|
|
1424
|
+
throw new Error(`${path}.targetPortID must be a string if present`);
|
|
1425
|
+
}
|
|
1426
|
+
};
|
|
1427
|
+
|
|
1121
1428
|
// src/domain/validation/index.ts
|
|
1122
1429
|
var WorkflowRuntimeValidation = class {
|
|
1123
1430
|
invoke(params) {
|
|
@@ -1135,8 +1442,23 @@ var WorkflowRuntimeValidation = class {
|
|
|
1135
1442
|
};
|
|
1136
1443
|
}
|
|
1137
1444
|
schema(schema) {
|
|
1445
|
+
const errors = [];
|
|
1446
|
+
const validations = [
|
|
1447
|
+
() => schemaFormat(schema),
|
|
1448
|
+
() => cycleDetection(schema),
|
|
1449
|
+
() => edgeSourceTargetExist(schema),
|
|
1450
|
+
() => startEndNode(schema)
|
|
1451
|
+
];
|
|
1452
|
+
validations.forEach((validation) => {
|
|
1453
|
+
try {
|
|
1454
|
+
validation();
|
|
1455
|
+
} catch (error) {
|
|
1456
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
1457
|
+
}
|
|
1458
|
+
});
|
|
1138
1459
|
return {
|
|
1139
|
-
valid:
|
|
1460
|
+
valid: errors.length === 0,
|
|
1461
|
+
errors: errors.length > 0 ? errors : void 0
|
|
1140
1462
|
};
|
|
1141
1463
|
}
|
|
1142
1464
|
inputs(inputsSchema, inputs) {
|
|
@@ -1179,7 +1501,7 @@ var WorkflowRuntimeExecutor = class {
|
|
|
1179
1501
|
const nodeType = context.node.type;
|
|
1180
1502
|
const nodeExecutor = this.nodeExecutors.get(nodeType);
|
|
1181
1503
|
if (!nodeExecutor) {
|
|
1182
|
-
throw new Error(`
|
|
1504
|
+
throw new Error(`No executor found for node type ${nodeType}`);
|
|
1183
1505
|
}
|
|
1184
1506
|
const output = await nodeExecutor.execute(context);
|
|
1185
1507
|
return output;
|
|
@@ -1554,7 +1876,7 @@ var WorkflowRuntimeState = class {
|
|
|
1554
1876
|
}
|
|
1555
1877
|
parseRef(ref) {
|
|
1556
1878
|
if (ref?.type !== "ref") {
|
|
1557
|
-
throw new Error(`
|
|
1879
|
+
throw new Error(`Invalid ref value: ${ref}`);
|
|
1558
1880
|
}
|
|
1559
1881
|
if (!ref.content || ref.content.length < 2) {
|
|
1560
1882
|
return null;
|
|
@@ -1572,7 +1894,7 @@ var WorkflowRuntimeState = class {
|
|
|
1572
1894
|
}
|
|
1573
1895
|
parseTemplate(template) {
|
|
1574
1896
|
if (template?.type !== "template") {
|
|
1575
|
-
throw new Error(`
|
|
1897
|
+
throw new Error(`Invalid template value: ${template}`);
|
|
1576
1898
|
}
|
|
1577
1899
|
if (!template.content) {
|
|
1578
1900
|
return null;
|
|
@@ -1598,7 +1920,7 @@ var WorkflowRuntimeState = class {
|
|
|
1598
1920
|
}
|
|
1599
1921
|
parseValue(flowValue) {
|
|
1600
1922
|
if (!flowValue?.type) {
|
|
1601
|
-
throw new Error(`
|
|
1923
|
+
throw new Error(`Invalid flow value type: ${flowValue.type}`);
|
|
1602
1924
|
}
|
|
1603
1925
|
if (flowValue.type === "constant") {
|
|
1604
1926
|
const value = flowValue.content;
|
|
@@ -1617,7 +1939,7 @@ var WorkflowRuntimeState = class {
|
|
|
1617
1939
|
if (flowValue.type === "template") {
|
|
1618
1940
|
return this.parseTemplate(flowValue);
|
|
1619
1941
|
}
|
|
1620
|
-
throw new Error(`
|
|
1942
|
+
throw new Error(`Unknown flow value type: ${flowValue.type}`);
|
|
1621
1943
|
}
|
|
1622
1944
|
isExecutedNode(node) {
|
|
1623
1945
|
return this.executedNodes.has(node.id);
|
|
@@ -2002,7 +2324,7 @@ var createStore = (params) => {
|
|
|
2002
2324
|
const from = store.nodes.get(sourceNodeID);
|
|
2003
2325
|
const to = store.nodes.get(targetNodeID);
|
|
2004
2326
|
if (!from || !to) {
|
|
2005
|
-
throw new Error(`
|
|
2327
|
+
throw new Error(`Invalid edge schema ID: ${id}, from: ${sourceNodeID}, to: ${targetNodeID}`);
|
|
2006
2328
|
}
|
|
2007
2329
|
const edge = createEdge(store, {
|
|
2008
2330
|
id,
|
|
@@ -2277,7 +2599,7 @@ var WorkflowRuntimeEngine = class {
|
|
|
2277
2599
|
}
|
|
2278
2600
|
const targetPort = node.ports.outputs.find((port) => port.id === branch);
|
|
2279
2601
|
if (!targetPort) {
|
|
2280
|
-
throw new Error(`branch ${branch} not found`);
|
|
2602
|
+
throw new Error(`Engine branch ${branch} not found`);
|
|
2281
2603
|
}
|
|
2282
2604
|
const nextNodeIDs = new Set(targetPort.edges.map((edge) => edge.to.id));
|
|
2283
2605
|
const nextNodes = allNextNodes.filter((nextNode) => nextNodeIDs.has(nextNode.id));
|
|
@@ -2292,11 +2614,11 @@ var WorkflowRuntimeEngine = class {
|
|
|
2292
2614
|
}
|
|
2293
2615
|
async executeNext(params) {
|
|
2294
2616
|
const { context, node, nextNodes } = params;
|
|
2295
|
-
if (node.type === FlowGramNode.End) {
|
|
2617
|
+
if (node.type === FlowGramNode.End || node.type === FlowGramNode.BlockEnd) {
|
|
2296
2618
|
return;
|
|
2297
2619
|
}
|
|
2298
2620
|
if (nextNodes.length === 0) {
|
|
2299
|
-
|
|
2621
|
+
throw new Error(`Node ${node.id} has no next nodes`);
|
|
2300
2622
|
}
|
|
2301
2623
|
await Promise.all(
|
|
2302
2624
|
nextNodes.map(
|