@qualitas-id/mcp 1.0.0
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/LICENSE +21 -0
- package/README.md +262 -0
- package/dist/constants.d.ts +18 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +18 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/common.d.ts +20 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +10 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/flow.d.ts +358 -0
- package/dist/schemas/flow.d.ts.map +1 -0
- package/dist/schemas/flow.js +72 -0
- package/dist/schemas/flow.js.map +1 -0
- package/dist/schemas/project.d.ts +70 -0
- package/dist/schemas/project.d.ts.map +1 -0
- package/dist/schemas/project.js +26 -0
- package/dist/schemas/project.js.map +1 -0
- package/dist/schemas/run.d.ts +54 -0
- package/dist/schemas/run.d.ts.map +1 -0
- package/dist/schemas/run.js +20 -0
- package/dist/schemas/run.js.map +1 -0
- package/dist/schemas/variable.d.ts +91 -0
- package/dist/schemas/variable.d.ts.map +1 -0
- package/dist/schemas/variable.js +30 -0
- package/dist/schemas/variable.js.map +1 -0
- package/dist/services/api-client.d.ts +101 -0
- package/dist/services/api-client.d.ts.map +1 -0
- package/dist/services/api-client.js +184 -0
- package/dist/services/api-client.js.map +1 -0
- package/dist/services/flow-generator.d.ts +31 -0
- package/dist/services/flow-generator.d.ts.map +1 -0
- package/dist/services/flow-generator.js +638 -0
- package/dist/services/flow-generator.js.map +1 -0
- package/dist/shared-types.d.ts +579 -0
- package/dist/shared-types.d.ts.map +1 -0
- package/dist/shared-types.js +12 -0
- package/dist/shared-types.js.map +1 -0
- package/dist/tools/flows.d.ts +13 -0
- package/dist/tools/flows.d.ts.map +1 -0
- package/dist/tools/flows.js +458 -0
- package/dist/tools/flows.js.map +1 -0
- package/dist/tools/projects.d.ts +13 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +381 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/runs.d.ts +9 -0
- package/dist/tools/runs.d.ts.map +1 -0
- package/dist/tools/runs.js +342 -0
- package/dist/tools/runs.js.map +1 -0
- package/dist/tools/utils.d.ts +12 -0
- package/dist/tools/utils.d.ts.map +1 -0
- package/dist/tools/utils.js +144 -0
- package/dist/tools/utils.js.map +1 -0
- package/dist/tools/variables.d.ts +9 -0
- package/dist/tools/variables.d.ts.map +1 -0
- package/dist/tools/variables.js +316 -0
- package/dist/tools/variables.js.map +1 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/flow-layout.d.ts +34 -0
- package/dist/utils/flow-layout.d.ts.map +1 -0
- package/dist/utils/flow-layout.js +109 -0
- package/dist/utils/flow-layout.js.map +1 -0
- package/dist/utils/flow-validation.d.ts +74 -0
- package/dist/utils/flow-validation.d.ts.map +1 -0
- package/dist/utils/flow-validation.js +386 -0
- package/dist/utils/flow-validation.js.map +1 -0
- package/dist/utils/ocr.d.ts +25 -0
- package/dist/utils/ocr.d.ts.map +1 -0
- package/dist/utils/ocr.js +88 -0
- package/dist/utils/ocr.js.map +1 -0
- package/package.json +65 -0
- package/skills/qualitas.md +253 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Validates flow structure, node inputs, and connections.
|
|
5
|
+
* Ported from front-end/src/features/flows/utils/flow-validation.ts
|
|
6
|
+
*/
|
|
7
|
+
interface ValidationNode {
|
|
8
|
+
id: string;
|
|
9
|
+
type: string;
|
|
10
|
+
data: Record<string, unknown>;
|
|
11
|
+
position: {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
interface ValidationEdge {
|
|
17
|
+
id: string;
|
|
18
|
+
source: string;
|
|
19
|
+
target: string;
|
|
20
|
+
sourceHandle?: string;
|
|
21
|
+
targetHandle?: string;
|
|
22
|
+
}
|
|
23
|
+
interface ValidationResult {
|
|
24
|
+
isValid: boolean;
|
|
25
|
+
error?: {
|
|
26
|
+
title: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
interface NodeInputValidationResult {
|
|
31
|
+
isValid: boolean;
|
|
32
|
+
errors: {
|
|
33
|
+
nodeId: string;
|
|
34
|
+
nodeName: string;
|
|
35
|
+
missingFields: string[];
|
|
36
|
+
}[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Validate flow structure before saving/running.
|
|
40
|
+
*
|
|
41
|
+
* Rules:
|
|
42
|
+
* 1. Flow MUST start with a 'start' OR 'schedule' node
|
|
43
|
+
* 2. The immediate next node after root MUST be navigate/callFragment/apiCall/generateEmail/ifCondition/loop
|
|
44
|
+
*/
|
|
45
|
+
export declare function validateFlowStructure(nodes: ValidationNode[], edges: ValidationEdge[]): ValidationResult;
|
|
46
|
+
/**
|
|
47
|
+
* Validate fragment structure.
|
|
48
|
+
*
|
|
49
|
+
* Rules:
|
|
50
|
+
* 1. Fragment must have at least one action node
|
|
51
|
+
* 2. Fragment CANNOT contain start, schedule, or callFragment nodes
|
|
52
|
+
*/
|
|
53
|
+
export declare function validateFragmentStructure(nodes: ValidationNode[], edges: ValidationEdge[]): ValidationResult;
|
|
54
|
+
/**
|
|
55
|
+
* Validate structure based on flow type
|
|
56
|
+
*/
|
|
57
|
+
export declare function validateStructure(nodes: ValidationNode[], edges: ValidationEdge[], flowType: "flow" | "fragment"): ValidationResult;
|
|
58
|
+
/**
|
|
59
|
+
* Validate that all nodes have their required inputs filled
|
|
60
|
+
*/
|
|
61
|
+
export declare function validateNodeInputs(nodes: ValidationNode[]): NodeInputValidationResult;
|
|
62
|
+
/**
|
|
63
|
+
* Validate that all nodes are properly connected
|
|
64
|
+
*/
|
|
65
|
+
export declare function validateNodeConnections(nodes: ValidationNode[], edges: ValidationEdge[], flowType?: "flow" | "fragment"): ValidationResult;
|
|
66
|
+
/**
|
|
67
|
+
* Run all validations on a flow
|
|
68
|
+
*/
|
|
69
|
+
export declare function validateFlow(nodes: ValidationNode[], edges: ValidationEdge[], flowType?: "flow" | "fragment"): {
|
|
70
|
+
isValid: boolean;
|
|
71
|
+
errors: string[];
|
|
72
|
+
};
|
|
73
|
+
export {};
|
|
74
|
+
//# sourceMappingURL=flow-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-validation.d.ts","sourceRoot":"","sources":["../../src/utils/flow-validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACpC;AAED,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,gBAAgB;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,UAAU,yBAAyB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE;QACN,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,EAAE,CAAC;KACzB,EAAE,CAAC;CACL;AAiGD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,cAAc,EAAE,EACvB,KAAK,EAAE,cAAc,EAAE,GACtB,gBAAgB,CA6DlB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,cAAc,EAAE,EACvB,KAAK,EAAE,cAAc,EAAE,GACtB,gBAAgB,CA4BlB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,cAAc,EAAE,EACvB,KAAK,EAAE,cAAc,EAAE,EACvB,QAAQ,EAAE,MAAM,GAAG,UAAU,GAC5B,gBAAgB,CAKlB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,yBAAyB,CA4DrF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,cAAc,EAAE,EACvB,KAAK,EAAE,cAAc,EAAE,EACvB,QAAQ,GAAE,MAAM,GAAG,UAAmB,GACrC,gBAAgB,CAuGlB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,cAAc,EAAE,EACvB,KAAK,EAAE,cAAc,EAAE,EACvB,QAAQ,GAAE,MAAM,GAAG,UAAmB,GACrC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CA2BxC"}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Validates flow structure, node inputs, and connections.
|
|
5
|
+
* Ported from front-end/src/features/flows/utils/flow-validation.ts
|
|
6
|
+
*/
|
|
7
|
+
// ===========================================
|
|
8
|
+
// Constants
|
|
9
|
+
// ===========================================
|
|
10
|
+
const ROOT_NODE_TYPES = ["start", "schedule"];
|
|
11
|
+
const VALID_FIRST_STEP_TYPES = ["navigate", "callFragment", "apiCall", "generateEmail", "ifCondition", "loop"];
|
|
12
|
+
const FORBIDDEN_FRAGMENT_NODE_TYPES = ["start", "schedule", "callFragment"];
|
|
13
|
+
// Required fields by node type
|
|
14
|
+
const nodeRequiredFields = {
|
|
15
|
+
navigate: [{ key: "url", label: "URL" }],
|
|
16
|
+
click: [{ key: "selector", label: "CSS Selector" }],
|
|
17
|
+
dblclick: [{ key: "selector", label: "CSS Selector" }],
|
|
18
|
+
rightclick: [{ key: "selector", label: "CSS Selector" }],
|
|
19
|
+
hover: [{ key: "selector", label: "CSS Selector" }],
|
|
20
|
+
dragAndDrop: [
|
|
21
|
+
{ key: "sourceSelector", label: "Source Selector" },
|
|
22
|
+
{ key: "targetSelector", label: "Target Selector" },
|
|
23
|
+
],
|
|
24
|
+
type: [
|
|
25
|
+
{ key: "selector", label: "CSS Selector" },
|
|
26
|
+
{ key: "text", label: "Text to Type" },
|
|
27
|
+
],
|
|
28
|
+
fill: [
|
|
29
|
+
{ key: "selector", label: "CSS Selector" },
|
|
30
|
+
{ key: "text", label: "Value" },
|
|
31
|
+
],
|
|
32
|
+
clear: [{ key: "selector", label: "CSS Selector" }],
|
|
33
|
+
press: [{ key: "key", label: "Key" }],
|
|
34
|
+
submit: [{ key: "selector", label: "Form Selector" }],
|
|
35
|
+
check: [{ key: "selector", label: "Checkbox Selector" }],
|
|
36
|
+
uncheck: [{ key: "selector", label: "Checkbox Selector" }],
|
|
37
|
+
select: [
|
|
38
|
+
{ key: "selector", label: "Select Selector" },
|
|
39
|
+
{ key: "value", label: "Option Value" },
|
|
40
|
+
],
|
|
41
|
+
upload: [
|
|
42
|
+
{ key: "selector", label: "File Input Selector" },
|
|
43
|
+
{ key: "filePath", label: "File Path" },
|
|
44
|
+
],
|
|
45
|
+
focus: [{ key: "selector", label: "CSS Selector" }],
|
|
46
|
+
blur: [{ key: "selector", label: "CSS Selector" }],
|
|
47
|
+
scrollIntoView: [{ key: "selector", label: "CSS Selector" }],
|
|
48
|
+
wait: [{ key: "duration", label: "Duration" }],
|
|
49
|
+
waitForElement: [{ key: "selector", label: "CSS Selector" }],
|
|
50
|
+
waitForResponse: [{ key: "urlPattern", label: "URL Pattern" }],
|
|
51
|
+
assertVisible: [{ key: "selector", label: "CSS Selector" }],
|
|
52
|
+
assertText: [
|
|
53
|
+
{ key: "selector", label: "CSS Selector" },
|
|
54
|
+
{ key: "expectedText", label: "Expected Text" },
|
|
55
|
+
],
|
|
56
|
+
assertValue: [
|
|
57
|
+
{ key: "selector", label: "CSS Selector" },
|
|
58
|
+
{ key: "expectedValue", label: "Expected Value" },
|
|
59
|
+
],
|
|
60
|
+
assertVariable: [
|
|
61
|
+
{ key: "leftValue", label: "Left Value" },
|
|
62
|
+
{ key: "rightValue", label: "Right Value" },
|
|
63
|
+
],
|
|
64
|
+
assertTableCell: [
|
|
65
|
+
{ key: "tableSelector", label: "Table Selector" },
|
|
66
|
+
{ key: "findColumnName", label: "Find Row Column" },
|
|
67
|
+
{ key: "findValue", label: "Find Row Value" },
|
|
68
|
+
{ key: "assertColumnName", label: "Assert Column" },
|
|
69
|
+
{ key: "expectedValue", label: "Expected Value" },
|
|
70
|
+
],
|
|
71
|
+
assertCount: [
|
|
72
|
+
{ key: "selector", label: "CSS Selector" },
|
|
73
|
+
{ key: "expectedValue", label: "Expected Value" },
|
|
74
|
+
],
|
|
75
|
+
assertUrl: [{ key: "expectedUrl", label: "Expected URL" }],
|
|
76
|
+
assertTitle: [{ key: "expectedTitle", label: "Expected Title" }],
|
|
77
|
+
schedule: [{ key: "cron", label: "Cron Expression" }],
|
|
78
|
+
callFragment: [{ key: "fragmentId", label: "Fragment" }],
|
|
79
|
+
generateEmail: [{ key: "emailName", label: "Alias Name" }],
|
|
80
|
+
readEmail: [
|
|
81
|
+
{ key: "generatedFrom", label: "Generated Email Reference" },
|
|
82
|
+
{ key: "outputKey", label: "Saved Result Name" },
|
|
83
|
+
],
|
|
84
|
+
extractEmail: [
|
|
85
|
+
{ key: "readSourceNodeId", label: "Read Email Source" },
|
|
86
|
+
{ key: "variableName", label: "Save Match As" },
|
|
87
|
+
],
|
|
88
|
+
extractFromElement: [
|
|
89
|
+
{ key: "selector", label: "Selector" },
|
|
90
|
+
{ key: "saveAs", label: "Save As" },
|
|
91
|
+
],
|
|
92
|
+
ifCondition: [{ key: "saveResultAs", label: "Save Result As" }],
|
|
93
|
+
loop: [{ key: "loopVariableName", label: "Loop Variable Name" }],
|
|
94
|
+
};
|
|
95
|
+
// ===========================================
|
|
96
|
+
// Validation Functions
|
|
97
|
+
// ===========================================
|
|
98
|
+
/**
|
|
99
|
+
* Validate flow structure before saving/running.
|
|
100
|
+
*
|
|
101
|
+
* Rules:
|
|
102
|
+
* 1. Flow MUST start with a 'start' OR 'schedule' node
|
|
103
|
+
* 2. The immediate next node after root MUST be navigate/callFragment/apiCall/generateEmail/ifCondition/loop
|
|
104
|
+
*/
|
|
105
|
+
export function validateFlowStructure(nodes, edges) {
|
|
106
|
+
// Rule 1: Check for root node
|
|
107
|
+
const rootNode = nodes.find((node) => ROOT_NODE_TYPES.includes(node.type));
|
|
108
|
+
if (!rootNode) {
|
|
109
|
+
return {
|
|
110
|
+
isValid: false,
|
|
111
|
+
error: {
|
|
112
|
+
title: "Invalid Flow Structure",
|
|
113
|
+
description: "Flow must start with a Start or Schedule node.",
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// Check if there are nodes beyond root
|
|
118
|
+
if (nodes.length === 1) {
|
|
119
|
+
return {
|
|
120
|
+
isValid: false,
|
|
121
|
+
error: {
|
|
122
|
+
title: "Incomplete Flow",
|
|
123
|
+
description: "Flow must have at least one action after the Start node.",
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// Rule 2: Check first step after root
|
|
128
|
+
const outgoingEdge = edges.find((edge) => edge.source === rootNode.id);
|
|
129
|
+
if (!outgoingEdge) {
|
|
130
|
+
return {
|
|
131
|
+
isValid: false,
|
|
132
|
+
error: {
|
|
133
|
+
title: "Disconnected Start Node",
|
|
134
|
+
description: "Start node must be connected to a Navigate node.",
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const firstStepNode = nodes.find((node) => node.id === outgoingEdge.target);
|
|
139
|
+
if (!firstStepNode) {
|
|
140
|
+
return {
|
|
141
|
+
isValid: false,
|
|
142
|
+
error: {
|
|
143
|
+
title: "Invalid Connection",
|
|
144
|
+
description: "The connection from Start node leads to an invalid node.",
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (!VALID_FIRST_STEP_TYPES.includes(firstStepNode.type)) {
|
|
149
|
+
return {
|
|
150
|
+
isValid: false,
|
|
151
|
+
error: {
|
|
152
|
+
title: "Invalid Flow Structure",
|
|
153
|
+
description: `First step after Start must be Navigate, Call Fragment, API Call, Generate Email, If Condition, or Loop. Currently connected to: ${firstStepNode.type}`,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return { isValid: true };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Validate fragment structure.
|
|
161
|
+
*
|
|
162
|
+
* Rules:
|
|
163
|
+
* 1. Fragment must have at least one action node
|
|
164
|
+
* 2. Fragment CANNOT contain start, schedule, or callFragment nodes
|
|
165
|
+
*/
|
|
166
|
+
export function validateFragmentStructure(nodes, edges) {
|
|
167
|
+
void edges;
|
|
168
|
+
if (nodes.length === 0) {
|
|
169
|
+
return {
|
|
170
|
+
isValid: false,
|
|
171
|
+
error: {
|
|
172
|
+
title: "Empty Fragment",
|
|
173
|
+
description: "Fragment must have at least one action node.",
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
const forbiddenNode = nodes.find((node) => FORBIDDEN_FRAGMENT_NODE_TYPES.includes(node.type));
|
|
178
|
+
if (forbiddenNode) {
|
|
179
|
+
return {
|
|
180
|
+
isValid: false,
|
|
181
|
+
error: {
|
|
182
|
+
title: "Invalid Fragment Structure",
|
|
183
|
+
description: `Fragments cannot contain ${forbiddenNode.type} nodes. Remove the ${forbiddenNode.type} node to continue.`,
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return { isValid: true };
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Validate structure based on flow type
|
|
191
|
+
*/
|
|
192
|
+
export function validateStructure(nodes, edges, flowType) {
|
|
193
|
+
if (flowType === "fragment") {
|
|
194
|
+
return validateFragmentStructure(nodes, edges);
|
|
195
|
+
}
|
|
196
|
+
return validateFlowStructure(nodes, edges);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Validate that all nodes have their required inputs filled
|
|
200
|
+
*/
|
|
201
|
+
export function validateNodeInputs(nodes) {
|
|
202
|
+
const errors = [];
|
|
203
|
+
for (const node of nodes) {
|
|
204
|
+
const requiredFields = nodeRequiredFields[node.type];
|
|
205
|
+
if (!requiredFields)
|
|
206
|
+
continue;
|
|
207
|
+
const data = node.data;
|
|
208
|
+
const missingFields = [];
|
|
209
|
+
for (const field of requiredFields) {
|
|
210
|
+
let value = data[field.key];
|
|
211
|
+
// For selector fields, check selectorEntries first
|
|
212
|
+
if (field.key === "selector") {
|
|
213
|
+
const entries = data.selectorEntries;
|
|
214
|
+
if (entries && Array.isArray(entries) && entries.length > 0) {
|
|
215
|
+
const hasValue = entries.some((e) => e.value && e.value.trim());
|
|
216
|
+
if (hasValue)
|
|
217
|
+
continue;
|
|
218
|
+
value = "";
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
const importedSelectors = data.selectors;
|
|
222
|
+
if (importedSelectors && Array.isArray(importedSelectors) && importedSelectors.some((s) => typeof s === "string" && s.trim())) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
const fallbackValue = typeof data.value === "string" ? data.value : "";
|
|
229
|
+
const fallbackText = typeof data.text === "string" ? data.text : "";
|
|
230
|
+
if ((field.key === "url" || field.key === "expectedUrl") && !value)
|
|
231
|
+
value = fallbackValue || fallbackText;
|
|
232
|
+
if ((field.key === "text" || field.key === "expectedText") && !value)
|
|
233
|
+
value = fallbackValue;
|
|
234
|
+
if ((field.key === "value" || field.key === "expectedValue") && !value)
|
|
235
|
+
value = fallbackText || fallbackValue;
|
|
236
|
+
if (field.key === "key" && !value)
|
|
237
|
+
value = fallbackValue;
|
|
238
|
+
}
|
|
239
|
+
// Check if field is missing, empty string, or just whitespace
|
|
240
|
+
if (value === undefined || value === null || value === "") {
|
|
241
|
+
missingFields.push(field.label);
|
|
242
|
+
}
|
|
243
|
+
else if (typeof value === "string" && value.trim() === "") {
|
|
244
|
+
missingFields.push(field.label);
|
|
245
|
+
}
|
|
246
|
+
else if (typeof value === "number" && (isNaN(value) || value <= 0)) {
|
|
247
|
+
if (field.key === "duration") {
|
|
248
|
+
missingFields.push(field.label);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (missingFields.length > 0) {
|
|
253
|
+
errors.push({
|
|
254
|
+
nodeId: node.id,
|
|
255
|
+
nodeName: node.type,
|
|
256
|
+
missingFields,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
isValid: errors.length === 0,
|
|
262
|
+
errors,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Validate that all nodes are properly connected
|
|
267
|
+
*/
|
|
268
|
+
export function validateNodeConnections(nodes, edges, flowType = "flow") {
|
|
269
|
+
const nodesWithIncoming = new Set(edges.map((e) => e.target));
|
|
270
|
+
const nodesWithOutgoing = new Set(edges.map((e) => e.source));
|
|
271
|
+
const disconnectedNodes = [];
|
|
272
|
+
const unreachableNodes = [];
|
|
273
|
+
let fragmentEntryCount = 0;
|
|
274
|
+
for (const node of nodes) {
|
|
275
|
+
const isRootNode = ROOT_NODE_TYPES.includes(node.type);
|
|
276
|
+
const hasIncoming = nodesWithIncoming.has(node.id);
|
|
277
|
+
const hasOutgoing = nodesWithOutgoing.has(node.id);
|
|
278
|
+
// Check ifCondition has then branch
|
|
279
|
+
if (node.type === "ifCondition") {
|
|
280
|
+
const outgoingEdges = edges.filter((edge) => edge.source === node.id);
|
|
281
|
+
const hasThenBranch = outgoingEdges.some((edge) => {
|
|
282
|
+
const handle = (edge.sourceHandle || "").trim().toLowerCase();
|
|
283
|
+
return handle === "then";
|
|
284
|
+
});
|
|
285
|
+
if (!hasThenBranch) {
|
|
286
|
+
return {
|
|
287
|
+
isValid: false,
|
|
288
|
+
error: {
|
|
289
|
+
title: "If Condition Incomplete",
|
|
290
|
+
description: "If Condition node must have a Then connection.",
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// Check loop has loop branch
|
|
296
|
+
if (node.type === "loop") {
|
|
297
|
+
const outgoingEdges = edges.filter((edge) => edge.source === node.id);
|
|
298
|
+
const hasLoopBranch = outgoingEdges.some((edge) => {
|
|
299
|
+
const handle = (edge.sourceHandle || "").trim().toLowerCase();
|
|
300
|
+
return handle === "loop";
|
|
301
|
+
});
|
|
302
|
+
if (!hasLoopBranch) {
|
|
303
|
+
return {
|
|
304
|
+
isValid: false,
|
|
305
|
+
error: {
|
|
306
|
+
title: "Loop Incomplete",
|
|
307
|
+
description: "Loop node must have a Loop branch connection.",
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (isRootNode)
|
|
313
|
+
continue;
|
|
314
|
+
// Fragment entry navigate check
|
|
315
|
+
const isFragmentEntryNavigate = flowType === "fragment" &&
|
|
316
|
+
node.type === "navigate" &&
|
|
317
|
+
!hasIncoming;
|
|
318
|
+
if (isFragmentEntryNavigate) {
|
|
319
|
+
fragmentEntryCount += 1;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
// Check disconnected nodes
|
|
323
|
+
if (!hasIncoming && !hasOutgoing) {
|
|
324
|
+
disconnectedNodes.push(node.type);
|
|
325
|
+
}
|
|
326
|
+
else if (!hasIncoming) {
|
|
327
|
+
unreachableNodes.push(node.type);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (flowType === "fragment" && fragmentEntryCount > 1) {
|
|
331
|
+
return {
|
|
332
|
+
isValid: false,
|
|
333
|
+
error: {
|
|
334
|
+
title: "Invalid Fragment Entry",
|
|
335
|
+
description: "Fragments can only have one entry Navigate node without incoming connections.",
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
if (disconnectedNodes.length > 0) {
|
|
340
|
+
return {
|
|
341
|
+
isValid: false,
|
|
342
|
+
error: {
|
|
343
|
+
title: "Disconnected Nodes Found",
|
|
344
|
+
description: `The following nodes are not connected to the flow: ${disconnectedNodes.join(", ")}.`,
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
if (unreachableNodes.length > 0) {
|
|
349
|
+
return {
|
|
350
|
+
isValid: false,
|
|
351
|
+
error: {
|
|
352
|
+
title: "Unreachable Nodes Found",
|
|
353
|
+
description: `The following nodes will never execute: ${unreachableNodes.join(", ")}.`,
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
return { isValid: true };
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Run all validations on a flow
|
|
361
|
+
*/
|
|
362
|
+
export function validateFlow(nodes, edges, flowType = "flow") {
|
|
363
|
+
const errors = [];
|
|
364
|
+
// 1. Structure validation
|
|
365
|
+
const structureResult = validateStructure(nodes, edges, flowType);
|
|
366
|
+
if (!structureResult.isValid && structureResult.error) {
|
|
367
|
+
errors.push(`${structureResult.error.title}: ${structureResult.error.description}`);
|
|
368
|
+
}
|
|
369
|
+
// 2. Node input validation
|
|
370
|
+
const inputResult = validateNodeInputs(nodes);
|
|
371
|
+
if (!inputResult.isValid) {
|
|
372
|
+
for (const err of inputResult.errors) {
|
|
373
|
+
errors.push(`Node "${err.nodeName}" (${err.nodeId}): Missing ${err.missingFields.join(", ")}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// 3. Connection validation
|
|
377
|
+
const connectionResult = validateNodeConnections(nodes, edges, flowType);
|
|
378
|
+
if (!connectionResult.isValid && connectionResult.error) {
|
|
379
|
+
errors.push(`${connectionResult.error.title}: ${connectionResult.error.description}`);
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
isValid: errors.length === 0,
|
|
383
|
+
errors,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
//# sourceMappingURL=flow-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-validation.js","sourceRoot":"","sources":["../../src/utils/flow-validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OCR Utility
|
|
3
|
+
*
|
|
4
|
+
* Extract text from images using Tesseract.js
|
|
5
|
+
* Used for reading error messages from test screenshots
|
|
6
|
+
*/
|
|
7
|
+
export interface OcrResult {
|
|
8
|
+
text: string;
|
|
9
|
+
confidence: number;
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Extract text from an image URL
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractTextFromImageUrl(imageUrl: string): Promise<OcrResult>;
|
|
16
|
+
/**
|
|
17
|
+
* Extract text from an image buffer
|
|
18
|
+
*/
|
|
19
|
+
export declare function extractTextFromBuffer(imageBuffer: Buffer): Promise<OcrResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Extract error message from screenshot text
|
|
22
|
+
* Looks for common error patterns
|
|
23
|
+
*/
|
|
24
|
+
export declare function extractErrorMessage(ocrText: string): string | null;
|
|
25
|
+
//# sourceMappingURL=ocr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ocr.d.ts","sourceRoot":"","sources":["../../src/utils/ocr.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAiBlF;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAiBnF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuClE"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OCR Utility
|
|
3
|
+
*
|
|
4
|
+
* Extract text from images using Tesseract.js
|
|
5
|
+
* Used for reading error messages from test screenshots
|
|
6
|
+
*/
|
|
7
|
+
import Tesseract from "tesseract.js";
|
|
8
|
+
/**
|
|
9
|
+
* Extract text from an image URL
|
|
10
|
+
*/
|
|
11
|
+
export async function extractTextFromImageUrl(imageUrl) {
|
|
12
|
+
try {
|
|
13
|
+
const result = await Tesseract.recognize(imageUrl, "eng", {
|
|
14
|
+
logger: () => { }, // Suppress progress logs
|
|
15
|
+
});
|
|
16
|
+
return {
|
|
17
|
+
text: result.data.text.trim(),
|
|
18
|
+
confidence: result.data.confidence,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return {
|
|
23
|
+
text: "",
|
|
24
|
+
confidence: 0,
|
|
25
|
+
error: `OCR failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Extract text from an image buffer
|
|
31
|
+
*/
|
|
32
|
+
export async function extractTextFromBuffer(imageBuffer) {
|
|
33
|
+
try {
|
|
34
|
+
const result = await Tesseract.recognize(imageBuffer, "eng", {
|
|
35
|
+
logger: () => { },
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
text: result.data.text.trim(),
|
|
39
|
+
confidence: result.data.confidence,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return {
|
|
44
|
+
text: "",
|
|
45
|
+
confidence: 0,
|
|
46
|
+
error: `OCR failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Extract error message from screenshot text
|
|
52
|
+
* Looks for common error patterns
|
|
53
|
+
*/
|
|
54
|
+
export function extractErrorMessage(ocrText) {
|
|
55
|
+
const lines = ocrText.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
56
|
+
// Common error patterns
|
|
57
|
+
const errorPatterns = [
|
|
58
|
+
/error[:\s]+(.+)/i,
|
|
59
|
+
/failed[:\s]+(.+)/i,
|
|
60
|
+
/exception[:\s]+(.+)/i,
|
|
61
|
+
/cannot[:\s]+(.+)/i,
|
|
62
|
+
/unable to[:\s]+(.+)/i,
|
|
63
|
+
/invalid[:\s]+(.+)/i,
|
|
64
|
+
/missing[:\s]+(.+)/i,
|
|
65
|
+
/required[:\s]+(.+)/i,
|
|
66
|
+
/not found[:\s]+(.+)/i,
|
|
67
|
+
/timeout[:\s]+(.+)/i,
|
|
68
|
+
/denied[:\s]+(.+)/i,
|
|
69
|
+
/rejected[:\s]+(.+)/i,
|
|
70
|
+
/warning[:\s]+(.+)/i,
|
|
71
|
+
];
|
|
72
|
+
for (const line of lines) {
|
|
73
|
+
for (const pattern of errorPatterns) {
|
|
74
|
+
const match = line.match(pattern);
|
|
75
|
+
if (match) {
|
|
76
|
+
return match[0]; // Return the full matched line
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// If no pattern matches, return first non-empty line that looks like an error
|
|
81
|
+
const suspiciousLines = lines.filter((line) => line.length > 10 &&
|
|
82
|
+
line.length < 500 &&
|
|
83
|
+
!line.match(/^[©®™]/) && // Skip copyright symbols
|
|
84
|
+
!line.match(/^\d+$/) // Skip pure numbers
|
|
85
|
+
);
|
|
86
|
+
return suspiciousLines[0] || null;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=ocr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ocr.js","sourceRoot":"","sources":["../../src/utils/ocr.ts"],"names":[],"mappings":"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"}
|