digitalsee-ai-flow-cli 0.1.7 → 0.1.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.
- package/.github/workflows/ci.yml +24 -0
- package/.github/workflows/publish.yml +21 -0
- package/CHANGELOG.md +84 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +4 -4
- package/dist/api/client.js.map +1 -1
- package/dist/api/flow.d.ts.map +1 -1
- package/dist/api/flow.js +16 -13
- package/dist/api/flow.js.map +1 -1
- package/dist/api/knowledge.d.ts.map +1 -1
- package/dist/api/knowledge.js +3 -3
- package/dist/api/knowledge.js.map +1 -1
- package/dist/api/node.d.ts.map +1 -1
- package/dist/api/node.js +4 -4
- package/dist/api/node.js.map +1 -1
- package/dist/api/variables.d.ts.map +1 -1
- package/dist/api/variables.js +1 -1
- package/dist/api/variables.js.map +1 -1
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +53 -77
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +24 -26
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/flow/account.d.ts.map +1 -1
- package/dist/commands/flow/account.js +17 -16
- package/dist/commands/flow/account.js.map +1 -1
- package/dist/commands/flow/analyze.d.ts.map +1 -1
- package/dist/commands/flow/analyze.js +42 -42
- package/dist/commands/flow/analyze.js.map +1 -1
- package/dist/commands/flow/create.d.ts.map +1 -1
- package/dist/commands/flow/create.js +7 -6
- package/dist/commands/flow/create.js.map +1 -1
- package/dist/commands/flow/edge.d.ts.map +1 -1
- package/dist/commands/flow/edge.js +36 -35
- package/dist/commands/flow/edge.js.map +1 -1
- package/dist/commands/flow/helpers.d.ts.map +1 -1
- package/dist/commands/flow/helpers.js +84 -74
- package/dist/commands/flow/helpers.js.map +1 -1
- package/dist/commands/flow/list.d.ts.map +1 -1
- package/dist/commands/flow/list.js +7 -7
- package/dist/commands/flow/list.js.map +1 -1
- package/dist/commands/flow/node.d.ts.map +1 -1
- package/dist/commands/flow/node.js +152 -139
- package/dist/commands/flow/node.js.map +1 -1
- package/dist/commands/flow/test.d.ts.map +1 -1
- package/dist/commands/flow/test.js +14 -14
- package/dist/commands/flow/test.js.map +1 -1
- package/dist/commands/flow/update.js +5 -5
- package/dist/commands/flow/update.js.map +1 -1
- package/dist/commands/flow/variables.d.ts.map +1 -1
- package/dist/commands/flow/variables.js +45 -43
- package/dist/commands/flow/variables.js.map +1 -1
- package/dist/commands/flow.d.ts.map +1 -1
- package/dist/commands/flow.js +1 -3
- package/dist/commands/flow.js.map +1 -1
- package/dist/commands/knowledge.d.ts.map +1 -1
- package/dist/commands/knowledge.js +67 -70
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/index.js +31 -20
- package/dist/index.js.map +1 -1
- package/dist/services/flowAnalyzer.d.ts.map +1 -1
- package/dist/services/flowAnalyzer.js +256 -203
- package/dist/services/flowAnalyzer.js.map +1 -1
- package/dist/services/skillGenerator.d.ts.map +1 -1
- package/dist/services/skillGenerator.js +33 -85
- package/dist/services/skillGenerator.js.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +12 -10
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/encoding.d.ts.map +1 -1
- package/dist/utils/encoding.js +3 -3
- package/dist/utils/encoding.js.map +1 -1
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +234 -220
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/oauth.d.ts.map +1 -1
- package/dist/utils/oauth.js +54 -78
- package/dist/utils/oauth.js.map +1 -1
- package/dist/utils/output.d.ts.map +1 -1
- package/dist/utils/output.js +20 -16
- package/dist/utils/output.js.map +1 -1
- package/dist/utils/updateCheck.d.ts.map +1 -0
- package/dist/utils/updateCheck.js +138 -0
- package/dist/utils/updateCheck.js.map +1 -0
- package/package.json +15 -7
- package/tsconfig.json +1 -1
- package/dist/utils/auth.d.ts.map +0 -1
- package/dist/utils/auth.js +0 -213
- package/dist/utils/auth.js.map +0 -1
package/dist/utils/format.js
CHANGED
|
@@ -16,25 +16,25 @@ function configSchemaToParams(subParams) {
|
|
|
16
16
|
name: p.name,
|
|
17
17
|
type: inferFieldType(p),
|
|
18
18
|
required: p.required ?? false,
|
|
19
|
-
description: sanitizeDescription(p.description ||
|
|
19
|
+
description: sanitizeDescription(p.description || ''),
|
|
20
20
|
display_name: p.display_name || p.name,
|
|
21
21
|
options: normalizeOptionValues(p.option_value_objects ?? p.option_values),
|
|
22
22
|
default: p.value,
|
|
23
23
|
}));
|
|
24
24
|
}
|
|
25
25
|
function sanitizeDescription(desc) {
|
|
26
|
-
return desc.replace(/\u0000/g,
|
|
26
|
+
return desc.replace(/\u0000/g, '\\u0000');
|
|
27
27
|
}
|
|
28
28
|
function normalizeOptionValues(raw) {
|
|
29
29
|
if (!raw || !Array.isArray(raw) || raw.length === 0)
|
|
30
30
|
return undefined;
|
|
31
31
|
return raw
|
|
32
32
|
.map((item) => {
|
|
33
|
-
if (typeof item ===
|
|
33
|
+
if (typeof item === 'string') {
|
|
34
34
|
const v = item.trim();
|
|
35
35
|
return v ? { key: v, label: v } : null;
|
|
36
36
|
}
|
|
37
|
-
const value = String(item?.value ?? item?.label ??
|
|
37
|
+
const value = String(item?.value ?? item?.label ?? '').trim();
|
|
38
38
|
if (!value)
|
|
39
39
|
return null;
|
|
40
40
|
const label = String(item?.label ?? value).trim() || value;
|
|
@@ -49,77 +49,79 @@ function inferFieldType(param) {
|
|
|
49
49
|
if (param.type) {
|
|
50
50
|
return param.type;
|
|
51
51
|
}
|
|
52
|
-
return
|
|
52
|
+
return 'string';
|
|
53
53
|
}
|
|
54
54
|
function inferScenarios(actionName, description) {
|
|
55
55
|
const scenarios = [];
|
|
56
|
-
const text = `${actionName} ${description ||
|
|
57
|
-
if (text.includes(
|
|
58
|
-
scenarios.push(
|
|
56
|
+
const text = `${actionName} ${description || ''}`.toLowerCase();
|
|
57
|
+
if (text.includes('trigger') || text.includes('webhook')) {
|
|
58
|
+
scenarios.push('作为流程触发器,在外部事件发生时启动工作流');
|
|
59
59
|
}
|
|
60
|
-
if (text.includes(
|
|
61
|
-
scenarios.push(
|
|
60
|
+
if (text.includes('http') || text.includes('api') || text.includes('rest')) {
|
|
61
|
+
scenarios.push('调用外部 HTTP API 接口,实现系统间的数据交互');
|
|
62
62
|
}
|
|
63
|
-
if (text.includes(
|
|
64
|
-
text.includes(
|
|
65
|
-
text.includes(
|
|
66
|
-
text.includes(
|
|
67
|
-
text.includes(
|
|
68
|
-
scenarios.push(
|
|
63
|
+
if (text.includes('database') ||
|
|
64
|
+
text.includes('sql') ||
|
|
65
|
+
text.includes('mysql') ||
|
|
66
|
+
text.includes('postgres') ||
|
|
67
|
+
text.includes('数据')) {
|
|
68
|
+
scenarios.push('连接数据库,执行数据查询或写入操作');
|
|
69
69
|
}
|
|
70
|
-
if (text.includes(
|
|
71
|
-
scenarios.push(
|
|
70
|
+
if (text.includes('email') || text.includes('mail') || text.includes('邮件')) {
|
|
71
|
+
scenarios.push('发送或接收邮件通知');
|
|
72
72
|
}
|
|
73
|
-
if (text.includes(
|
|
74
|
-
scenarios.push(
|
|
73
|
+
if (text.includes('ai') || text.includes('llm') || text.includes('model')) {
|
|
74
|
+
scenarios.push('调用 AI 大模型进行文本生成、分析或推理');
|
|
75
75
|
}
|
|
76
|
-
if (text.includes(
|
|
77
|
-
scenarios.push(
|
|
76
|
+
if (text.includes('condition') || text.includes('条件')) {
|
|
77
|
+
scenarios.push('根据条件判断结果进行流程分支控制');
|
|
78
78
|
}
|
|
79
|
-
if (text.includes(
|
|
80
|
-
scenarios.push(
|
|
79
|
+
if (text.includes('loop') || text.includes('循环')) {
|
|
80
|
+
scenarios.push('对数据集进行批量循环处理');
|
|
81
81
|
}
|
|
82
|
-
if (text.includes(
|
|
83
|
-
scenarios.push(
|
|
82
|
+
if (text.includes('transform') || text.includes('转换') || text.includes('map')) {
|
|
83
|
+
scenarios.push('对数据进行格式转换或映射处理');
|
|
84
84
|
}
|
|
85
|
-
if (text.includes(
|
|
86
|
-
scenarios.push(
|
|
85
|
+
if (text.includes('notification') || text.includes('通知') || text.includes('message')) {
|
|
86
|
+
scenarios.push('发送消息通知或告警');
|
|
87
87
|
}
|
|
88
88
|
if (scenarios.length === 0) {
|
|
89
|
-
scenarios.push(
|
|
89
|
+
scenarios.push('执行自定义业务逻辑操作');
|
|
90
90
|
}
|
|
91
91
|
return scenarios;
|
|
92
92
|
}
|
|
93
93
|
function sanitizeFileName(name) {
|
|
94
94
|
return name
|
|
95
|
-
.replace(/[<>:"/\\|?*]/g,
|
|
96
|
-
.replace(/\s+/g,
|
|
97
|
-
.replace(/^\.+/,
|
|
95
|
+
.replace(/[<>:"/\\|?*]/g, '_')
|
|
96
|
+
.replace(/\s+/g, '_')
|
|
97
|
+
.replace(/^\.+/, '')
|
|
98
98
|
.substring(0, 200);
|
|
99
99
|
}
|
|
100
100
|
function fillConfigSchemaDefaults(schema) {
|
|
101
101
|
const clone = JSON.parse(JSON.stringify(schema));
|
|
102
102
|
function walk(node) {
|
|
103
|
-
if (!node || typeof node !==
|
|
103
|
+
if (!node || typeof node !== 'object')
|
|
104
104
|
return;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
const n = node;
|
|
106
|
+
if (n.sub_params && Array.isArray(n.sub_params)) {
|
|
107
|
+
for (const child of n.sub_params) {
|
|
108
|
+
const c = child;
|
|
109
|
+
if (c.value === undefined || c.value === null) {
|
|
110
|
+
c.value = inferDefaultValue(c);
|
|
109
111
|
}
|
|
110
|
-
|
|
111
|
-
walk(
|
|
112
|
+
c.update = true;
|
|
113
|
+
walk(c);
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
|
-
if (
|
|
115
|
-
if (
|
|
116
|
-
|
|
116
|
+
if (n.value === undefined || n.value === null) {
|
|
117
|
+
if (n.sub_params) {
|
|
118
|
+
n.value = null;
|
|
117
119
|
}
|
|
118
120
|
else {
|
|
119
|
-
|
|
121
|
+
n.value = inferDefaultValue(n);
|
|
120
122
|
}
|
|
121
123
|
}
|
|
122
|
-
|
|
124
|
+
n.update = true;
|
|
123
125
|
}
|
|
124
126
|
walk(clone);
|
|
125
127
|
return clone;
|
|
@@ -127,43 +129,43 @@ function fillConfigSchemaDefaults(schema) {
|
|
|
127
129
|
function inferDefaultValue(param) {
|
|
128
130
|
if (param.value !== undefined && param.value !== null)
|
|
129
131
|
return param.value;
|
|
130
|
-
const type = (param.page_control || param.type ||
|
|
131
|
-
if (type ===
|
|
132
|
+
const type = (param.page_control || param.type || '').toUpperCase();
|
|
133
|
+
if (type === 'SELECT') {
|
|
132
134
|
if (param.multi_valued)
|
|
133
135
|
return [];
|
|
134
136
|
const options = param.option_value_objects ?? param.option_values;
|
|
135
137
|
if (options && Array.isArray(options) && options.length > 0) {
|
|
136
138
|
const first = options[0];
|
|
137
|
-
if (typeof first ===
|
|
139
|
+
if (typeof first === 'string')
|
|
138
140
|
return first;
|
|
139
|
-
if (typeof first ===
|
|
140
|
-
return first.value ?? first.label ??
|
|
141
|
+
if (typeof first === 'object' && first !== null) {
|
|
142
|
+
return first.value ?? first.label ?? '';
|
|
141
143
|
}
|
|
142
144
|
}
|
|
143
145
|
return null;
|
|
144
146
|
}
|
|
145
147
|
switch (type) {
|
|
146
|
-
case
|
|
148
|
+
case 'NUMBER':
|
|
147
149
|
return 0;
|
|
148
|
-
case
|
|
149
|
-
case
|
|
150
|
+
case 'BOOLEAN':
|
|
151
|
+
case 'SWITCH':
|
|
150
152
|
return false;
|
|
151
|
-
case
|
|
152
|
-
case
|
|
153
|
-
case
|
|
153
|
+
case 'DATE':
|
|
154
|
+
case 'TIME':
|
|
155
|
+
case 'DATETIME':
|
|
154
156
|
return null;
|
|
155
|
-
case
|
|
156
|
-
return
|
|
157
|
-
case
|
|
158
|
-
case
|
|
157
|
+
case 'JSON':
|
|
158
|
+
return '{}';
|
|
159
|
+
case 'EXTEND_OBJ':
|
|
160
|
+
case 'FIXED_OBJ':
|
|
159
161
|
return param.sub_params ? null : {};
|
|
160
|
-
case
|
|
161
|
-
case
|
|
162
|
-
case
|
|
163
|
-
case
|
|
164
|
-
case
|
|
162
|
+
case 'TEXTAREA':
|
|
163
|
+
case 'PASSWORD':
|
|
164
|
+
case 'HIDDEN':
|
|
165
|
+
case 'LABEL':
|
|
166
|
+
case 'TEXT':
|
|
165
167
|
default:
|
|
166
|
-
return
|
|
168
|
+
return '';
|
|
167
169
|
}
|
|
168
170
|
}
|
|
169
171
|
function parseVariableRefs(text) {
|
|
@@ -174,7 +176,7 @@ function parseVariableRefs(text) {
|
|
|
174
176
|
refs.push({
|
|
175
177
|
raw: match[0],
|
|
176
178
|
nodeId: match[1],
|
|
177
|
-
path: match[2] ||
|
|
179
|
+
path: match[2] || '',
|
|
178
180
|
});
|
|
179
181
|
}
|
|
180
182
|
return refs;
|
|
@@ -182,12 +184,14 @@ function parseVariableRefs(text) {
|
|
|
182
184
|
function mergeConfigValues(schema, overrides) {
|
|
183
185
|
const clone = JSON.parse(JSON.stringify(schema));
|
|
184
186
|
function walk(node, overrideMap) {
|
|
185
|
-
|
|
187
|
+
const n = node;
|
|
188
|
+
if (!n.sub_params)
|
|
186
189
|
return;
|
|
187
|
-
for (const child of
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
for (const child of n.sub_params) {
|
|
191
|
+
const childName = child.name;
|
|
192
|
+
if (childName && childName in overrideMap) {
|
|
193
|
+
const val = overrideMap[childName];
|
|
194
|
+
if (child.sub_params && typeof val === 'object' && !Array.isArray(val)) {
|
|
191
195
|
walk(child, val);
|
|
192
196
|
}
|
|
193
197
|
else {
|
|
@@ -208,29 +212,30 @@ function mergeConfigValues(schema, overrides) {
|
|
|
208
212
|
function validateNodePropertyValues(schema) {
|
|
209
213
|
const errors = [];
|
|
210
214
|
function walk(node, path) {
|
|
211
|
-
if (!node || typeof node !==
|
|
215
|
+
if (!node || typeof node !== 'object')
|
|
212
216
|
return;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
const n = node;
|
|
218
|
+
if (n.sub_params && Array.isArray(n.sub_params)) {
|
|
219
|
+
for (const child of n.sub_params) {
|
|
220
|
+
const c = child;
|
|
221
|
+
const cName = String(c.name || '(anonymous)');
|
|
222
|
+
const childPath = path ? `${path}.${cName}` : cName;
|
|
223
|
+
checkField(c, childPath);
|
|
224
|
+
walk(c, childPath);
|
|
220
225
|
}
|
|
221
226
|
}
|
|
222
227
|
}
|
|
223
228
|
function checkField(field, fieldPath) {
|
|
224
229
|
const val = field.value;
|
|
225
|
-
const pageControl = (field.page_control || field.type ||
|
|
226
|
-
const isEmpty = val === undefined || val === null || val ===
|
|
230
|
+
const pageControl = String(field.page_control || field.type || '').toUpperCase();
|
|
231
|
+
const isEmpty = val === undefined || val === null || val === '';
|
|
227
232
|
// 1. 检查必填字段
|
|
228
233
|
if (field.required && isEmpty) {
|
|
229
234
|
errors.push({
|
|
230
235
|
field: fieldPath,
|
|
231
|
-
rule:
|
|
236
|
+
rule: 'required',
|
|
232
237
|
message: `必填字段 "${field.display_name} : ${field.name}" 未提供值`,
|
|
233
|
-
sample:
|
|
238
|
+
sample: '',
|
|
234
239
|
});
|
|
235
240
|
}
|
|
236
241
|
// 2. 如果有值,检查值类型是否匹配 page_control
|
|
@@ -241,65 +246,65 @@ function validateNodePropertyValues(schema) {
|
|
|
241
246
|
function checkTypeConstraint(field, fieldPath, pageControl, val) {
|
|
242
247
|
const sample = JSON.stringify(val).substring(0, 80);
|
|
243
248
|
switch (pageControl) {
|
|
244
|
-
case
|
|
249
|
+
case 'SELECT':
|
|
245
250
|
if (field.multi_valued) {
|
|
246
251
|
if (!Array.isArray(val)) {
|
|
247
252
|
errors.push({
|
|
248
253
|
field: fieldPath,
|
|
249
|
-
rule:
|
|
254
|
+
rule: 'type',
|
|
250
255
|
message: `SELECT(multi) 字段 "${field.display_name || field.name}" 期望 array 类型,实际为 ${typeof val}`,
|
|
251
256
|
sample,
|
|
252
257
|
});
|
|
253
258
|
}
|
|
254
259
|
}
|
|
255
260
|
else {
|
|
256
|
-
if (typeof val !==
|
|
261
|
+
if (typeof val !== 'string') {
|
|
257
262
|
errors.push({
|
|
258
263
|
field: fieldPath,
|
|
259
|
-
rule:
|
|
264
|
+
rule: 'type',
|
|
260
265
|
message: `SELECT 字段 "${field.display_name || field.name}" 期望 string 类型,实际为 ${typeof val}`,
|
|
261
266
|
sample,
|
|
262
267
|
});
|
|
263
268
|
}
|
|
264
269
|
}
|
|
265
270
|
break;
|
|
266
|
-
case
|
|
267
|
-
if (typeof val !==
|
|
271
|
+
case 'NUMBER':
|
|
272
|
+
if (typeof val !== 'number') {
|
|
268
273
|
errors.push({
|
|
269
274
|
field: fieldPath,
|
|
270
|
-
rule:
|
|
275
|
+
rule: 'type',
|
|
271
276
|
message: `NUMBER 字段 "${field.display_name || field.name}" 期望 number 类型,实际为 ${typeof val}`,
|
|
272
277
|
sample,
|
|
273
278
|
});
|
|
274
279
|
}
|
|
275
280
|
break;
|
|
276
|
-
case
|
|
277
|
-
case
|
|
278
|
-
if (typeof val !==
|
|
281
|
+
case 'BOOLEAN':
|
|
282
|
+
case 'SWITCH':
|
|
283
|
+
if (typeof val !== 'boolean') {
|
|
279
284
|
errors.push({
|
|
280
285
|
field: fieldPath,
|
|
281
|
-
rule:
|
|
286
|
+
rule: 'type',
|
|
282
287
|
message: `${pageControl} 字段 "${field.display_name || field.name}" 期望 boolean 类型,实际为 ${typeof val}`,
|
|
283
288
|
sample,
|
|
284
289
|
});
|
|
285
290
|
}
|
|
286
291
|
break;
|
|
287
|
-
case
|
|
288
|
-
case
|
|
289
|
-
if (typeof val !==
|
|
292
|
+
case 'EXTEND_OBJ':
|
|
293
|
+
case 'FIXED_OBJ':
|
|
294
|
+
if (typeof val !== 'object' || Array.isArray(val)) {
|
|
290
295
|
errors.push({
|
|
291
296
|
field: fieldPath,
|
|
292
|
-
rule:
|
|
293
|
-
message: `${pageControl} 字段 "${field.display_name || field.name}" 期望 object 类型,实际为 ${Array.isArray(val) ?
|
|
297
|
+
rule: 'type',
|
|
298
|
+
message: `${pageControl} 字段 "${field.display_name || field.name}" 期望 object 类型,实际为 ${Array.isArray(val) ? 'array' : typeof val}`,
|
|
294
299
|
sample,
|
|
295
300
|
});
|
|
296
301
|
}
|
|
297
302
|
break;
|
|
298
|
-
case
|
|
299
|
-
if (typeof val !==
|
|
303
|
+
case 'JSON':
|
|
304
|
+
if (typeof val !== 'string') {
|
|
300
305
|
errors.push({
|
|
301
306
|
field: fieldPath,
|
|
302
|
-
rule:
|
|
307
|
+
rule: 'type',
|
|
303
308
|
message: `JSON 字段 "${field.display_name || field.name}" 期望 JSON 字符串,实际为 ${typeof val}`,
|
|
304
309
|
sample,
|
|
305
310
|
});
|
|
@@ -311,34 +316,34 @@ function validateNodePropertyValues(schema) {
|
|
|
311
316
|
catch {
|
|
312
317
|
errors.push({
|
|
313
318
|
field: fieldPath,
|
|
314
|
-
rule:
|
|
319
|
+
rule: 'format',
|
|
315
320
|
message: `JSON 字段 "${field.display_name || field.name}" 的值不是有效的 JSON`,
|
|
316
321
|
sample: val.substring(0, 80),
|
|
317
322
|
});
|
|
318
323
|
}
|
|
319
324
|
}
|
|
320
325
|
break;
|
|
321
|
-
case
|
|
322
|
-
case
|
|
323
|
-
case
|
|
324
|
-
if (typeof val !==
|
|
326
|
+
case 'DATE':
|
|
327
|
+
case 'TIME':
|
|
328
|
+
case 'DATETIME':
|
|
329
|
+
if (typeof val !== 'string') {
|
|
325
330
|
errors.push({
|
|
326
331
|
field: fieldPath,
|
|
327
|
-
rule:
|
|
332
|
+
rule: 'type',
|
|
328
333
|
message: `${pageControl} 字段 "${field.display_name || field.name}" 期望日期/时间字符串,实际为 ${typeof val}`,
|
|
329
334
|
sample,
|
|
330
335
|
});
|
|
331
336
|
}
|
|
332
337
|
break;
|
|
333
|
-
case
|
|
334
|
-
case
|
|
335
|
-
case
|
|
336
|
-
case
|
|
337
|
-
case
|
|
338
|
-
if (typeof val !==
|
|
338
|
+
case 'TEXT':
|
|
339
|
+
case 'TEXTAREA':
|
|
340
|
+
case 'PASSWORD':
|
|
341
|
+
case 'HIDDEN':
|
|
342
|
+
case 'LABEL':
|
|
343
|
+
if (typeof val !== 'string') {
|
|
339
344
|
errors.push({
|
|
340
345
|
field: fieldPath,
|
|
341
|
-
rule:
|
|
346
|
+
rule: 'type',
|
|
342
347
|
message: `${pageControl} 字段 "${field.display_name || field.name}" 期望 string 类型,实际为 ${typeof val}`,
|
|
343
348
|
sample,
|
|
344
349
|
});
|
|
@@ -348,32 +353,38 @@ function validateNodePropertyValues(schema) {
|
|
|
348
353
|
break;
|
|
349
354
|
}
|
|
350
355
|
}
|
|
351
|
-
walk(schema,
|
|
356
|
+
walk(schema, '');
|
|
352
357
|
return errors;
|
|
353
358
|
}
|
|
354
359
|
const VALID_CONDITION_OPERATORS = new Set([
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
360
|
+
'eq',
|
|
361
|
+
'ne',
|
|
362
|
+
'gt',
|
|
363
|
+
'ge',
|
|
364
|
+
'lt',
|
|
365
|
+
'le',
|
|
366
|
+
'sw',
|
|
367
|
+
'ew',
|
|
368
|
+
'co',
|
|
369
|
+
'nco',
|
|
370
|
+
'in',
|
|
371
|
+
'nin',
|
|
372
|
+
'is_null',
|
|
373
|
+
'is_not_null',
|
|
367
374
|
]);
|
|
375
|
+
const NULL_OPERATORS = new Set(['is_null', 'is_not_null']);
|
|
376
|
+
const LOOP_TYPES = new Set(['NUMBER', 'ARRAY', 'CONDITION']);
|
|
377
|
+
const ERROR_EXEC_OPTIONS = new Set(['END', 'CONTINUE', 'BREAK']);
|
|
378
|
+
const SPECIAL_NODE_TYPES = new Set(['ConditionNode', 'LoopNode', 'SelfLoopNode', 'SwitchNode']);
|
|
368
379
|
function safeSample(val) {
|
|
369
|
-
return (JSON.stringify(val) ??
|
|
380
|
+
return (JSON.stringify(val) ?? '').substring(0, 80);
|
|
370
381
|
}
|
|
371
382
|
function validateOrList(orList, path) {
|
|
372
383
|
const errors = [];
|
|
373
384
|
if (!Array.isArray(orList)) {
|
|
374
385
|
errors.push({
|
|
375
386
|
field: path,
|
|
376
|
-
rule:
|
|
387
|
+
rule: 'type',
|
|
377
388
|
message: `${path} 必须是 array 类型`,
|
|
378
389
|
sample: safeSample(orList),
|
|
379
390
|
});
|
|
@@ -382,18 +393,18 @@ function validateOrList(orList, path) {
|
|
|
382
393
|
if (orList.length === 0) {
|
|
383
394
|
errors.push({
|
|
384
395
|
field: path,
|
|
385
|
-
rule:
|
|
396
|
+
rule: 'required',
|
|
386
397
|
message: `${path} 至少需要 1 个条件组`,
|
|
387
|
-
sample:
|
|
398
|
+
sample: '',
|
|
388
399
|
});
|
|
389
400
|
return errors;
|
|
390
401
|
}
|
|
391
402
|
orList.forEach((orItem, i) => {
|
|
392
403
|
const orPath = `${path}[${i}]`;
|
|
393
|
-
if (!orItem || typeof orItem !==
|
|
404
|
+
if (!orItem || typeof orItem !== 'object') {
|
|
394
405
|
errors.push({
|
|
395
406
|
field: orPath,
|
|
396
|
-
rule:
|
|
407
|
+
rule: 'type',
|
|
397
408
|
message: `${orPath} 必须是 object 类型`,
|
|
398
409
|
sample: safeSample(orItem),
|
|
399
410
|
});
|
|
@@ -403,7 +414,7 @@ function validateOrList(orList, path) {
|
|
|
403
414
|
if (!Array.isArray(andList)) {
|
|
404
415
|
errors.push({
|
|
405
416
|
field: `${orPath}.and_list`,
|
|
406
|
-
rule:
|
|
417
|
+
rule: 'type',
|
|
407
418
|
message: `${orPath}.and_list 必须是 array 类型`,
|
|
408
419
|
sample: safeSample(andList),
|
|
409
420
|
});
|
|
@@ -412,44 +423,44 @@ function validateOrList(orList, path) {
|
|
|
412
423
|
if (andList.length === 0) {
|
|
413
424
|
errors.push({
|
|
414
425
|
field: `${orPath}.and_list`,
|
|
415
|
-
rule:
|
|
426
|
+
rule: 'required',
|
|
416
427
|
message: `${orPath}.and_list 至少需要 1 个条件`,
|
|
417
|
-
sample:
|
|
428
|
+
sample: '',
|
|
418
429
|
});
|
|
419
430
|
return;
|
|
420
431
|
}
|
|
421
432
|
andList.forEach((item, j) => {
|
|
422
433
|
const itemPath = `${orPath}.and_list[${j}]`;
|
|
423
|
-
if (!item || typeof item !==
|
|
434
|
+
if (!item || typeof item !== 'object') {
|
|
424
435
|
errors.push({
|
|
425
436
|
field: itemPath,
|
|
426
|
-
rule:
|
|
437
|
+
rule: 'type',
|
|
427
438
|
message: `${itemPath} 必须是 object 类型`,
|
|
428
439
|
sample: safeSample(item),
|
|
429
440
|
});
|
|
430
441
|
return;
|
|
431
442
|
}
|
|
432
|
-
if (!item.query || typeof item.query !==
|
|
443
|
+
if (!item.query || typeof item.query !== 'string' || !item.query.trim()) {
|
|
433
444
|
errors.push({
|
|
434
445
|
field: `${itemPath}.query`,
|
|
435
|
-
rule:
|
|
446
|
+
rule: 'required',
|
|
436
447
|
message: `${itemPath}.query 是必填字段,须为变量引用`,
|
|
437
|
-
sample:
|
|
448
|
+
sample: '',
|
|
438
449
|
});
|
|
439
450
|
}
|
|
440
|
-
if (!item.operator || typeof item.operator !==
|
|
451
|
+
if (!item.operator || typeof item.operator !== 'string') {
|
|
441
452
|
errors.push({
|
|
442
453
|
field: `${itemPath}.operator`,
|
|
443
|
-
rule:
|
|
454
|
+
rule: 'required',
|
|
444
455
|
message: `${itemPath}.operator 是必填字段`,
|
|
445
|
-
sample:
|
|
456
|
+
sample: '',
|
|
446
457
|
});
|
|
447
458
|
}
|
|
448
459
|
else if (!VALID_CONDITION_OPERATORS.has(item.operator)) {
|
|
449
460
|
errors.push({
|
|
450
461
|
field: `${itemPath}.operator`,
|
|
451
|
-
rule:
|
|
452
|
-
message: `${itemPath}.operator 无效: "${item.operator}",有效值: ${[...VALID_CONDITION_OPERATORS].join(
|
|
462
|
+
rule: 'type',
|
|
463
|
+
message: `${itemPath}.operator 无效: "${item.operator}",有效值: ${[...VALID_CONDITION_OPERATORS].join(', ')}`,
|
|
453
464
|
sample: item.operator,
|
|
454
465
|
});
|
|
455
466
|
}
|
|
@@ -458,9 +469,9 @@ function validateOrList(orList, path) {
|
|
|
458
469
|
(item.value === undefined || item.value === null)) {
|
|
459
470
|
errors.push({
|
|
460
471
|
field: `${itemPath}.value`,
|
|
461
|
-
rule:
|
|
472
|
+
rule: 'required',
|
|
462
473
|
message: `${itemPath}.value 在运算符为 "${item.operator}" 时必须提供`,
|
|
463
|
-
sample:
|
|
474
|
+
sample: '',
|
|
464
475
|
});
|
|
465
476
|
}
|
|
466
477
|
});
|
|
@@ -477,22 +488,22 @@ function validateSpecialNodeType(nodeType, data) {
|
|
|
477
488
|
const props = data.properties || {};
|
|
478
489
|
const errors = [];
|
|
479
490
|
switch (nodeType) {
|
|
480
|
-
case
|
|
491
|
+
case 'ConditionNode': {
|
|
481
492
|
if (props.or_list === undefined || props.or_list === null) {
|
|
482
493
|
errors.push({
|
|
483
|
-
field:
|
|
484
|
-
rule:
|
|
485
|
-
message:
|
|
486
|
-
sample:
|
|
494
|
+
field: 'properties.or_list',
|
|
495
|
+
rule: 'required',
|
|
496
|
+
message: 'ConditionNode 缺少必填字段 properties.or_list',
|
|
497
|
+
sample: '',
|
|
487
498
|
});
|
|
488
499
|
}
|
|
489
500
|
else {
|
|
490
|
-
errors.push(...validateOrList(props.or_list,
|
|
501
|
+
errors.push(...validateOrList(props.or_list, 'properties.or_list'));
|
|
491
502
|
}
|
|
492
503
|
break;
|
|
493
504
|
}
|
|
494
|
-
case
|
|
495
|
-
case
|
|
505
|
+
case 'LoopNode':
|
|
506
|
+
case 'SelfLoopNode': {
|
|
496
507
|
// LoopNode 字段在 properties 下双份存在:直接属性 + sub_params[].value
|
|
497
508
|
// CLI --data 只填充 sub_params,API 返回时两者都有,需要从两处查找
|
|
498
509
|
const loopField = (name) => {
|
|
@@ -506,125 +517,125 @@ function validateSpecialNodeType(nodeType, data) {
|
|
|
506
517
|
}
|
|
507
518
|
return undefined;
|
|
508
519
|
};
|
|
509
|
-
const loopType = loopField(
|
|
520
|
+
const loopType = loopField('type');
|
|
510
521
|
if (!loopType || !LOOP_TYPES.has(loopType)) {
|
|
511
522
|
errors.push({
|
|
512
|
-
field:
|
|
513
|
-
rule:
|
|
523
|
+
field: 'properties.type',
|
|
524
|
+
rule: 'required',
|
|
514
525
|
message: `循环节点 properties.type 必须是 NUMBER / ARRAY / CONDITION 之一,当前为 "${loopType}"`,
|
|
515
|
-
sample: loopType
|
|
526
|
+
sample: loopType || '',
|
|
516
527
|
});
|
|
517
528
|
}
|
|
518
|
-
if (loopType ===
|
|
519
|
-
const startVal = loopField(
|
|
520
|
-
const endVal = loopField(
|
|
521
|
-
if (typeof startVal !==
|
|
529
|
+
if (loopType === 'NUMBER') {
|
|
530
|
+
const startVal = loopField('start');
|
|
531
|
+
const endVal = loopField('end');
|
|
532
|
+
if (typeof startVal !== 'number') {
|
|
522
533
|
errors.push({
|
|
523
|
-
field:
|
|
524
|
-
rule:
|
|
534
|
+
field: 'properties.start',
|
|
535
|
+
rule: 'type',
|
|
525
536
|
message: `NUMBER 循环的 properties.start 必须是 number 类型,当前为 ${typeof startVal}`,
|
|
526
537
|
sample: String(startVal),
|
|
527
538
|
});
|
|
528
539
|
}
|
|
529
540
|
else if (startVal < 1) {
|
|
530
541
|
errors.push({
|
|
531
|
-
field:
|
|
532
|
-
rule:
|
|
542
|
+
field: 'properties.start',
|
|
543
|
+
rule: 'type',
|
|
533
544
|
message: `NUMBER 循环的 properties.start 必须 >= 1,当前为 ${startVal}`,
|
|
534
545
|
sample: String(startVal),
|
|
535
546
|
});
|
|
536
547
|
}
|
|
537
|
-
if (typeof endVal !==
|
|
548
|
+
if (typeof endVal !== 'number') {
|
|
538
549
|
errors.push({
|
|
539
|
-
field:
|
|
540
|
-
rule:
|
|
550
|
+
field: 'properties.end',
|
|
551
|
+
rule: 'type',
|
|
541
552
|
message: `NUMBER 循环的 properties.end 必须是 number 类型,当前为 ${typeof endVal}`,
|
|
542
553
|
sample: String(endVal),
|
|
543
554
|
});
|
|
544
555
|
}
|
|
545
|
-
else if (typeof startVal ===
|
|
556
|
+
else if (typeof startVal === 'number' && endVal < startVal) {
|
|
546
557
|
errors.push({
|
|
547
|
-
field:
|
|
548
|
-
rule:
|
|
558
|
+
field: 'properties.end',
|
|
559
|
+
rule: 'type',
|
|
549
560
|
message: `NUMBER 循环的 properties.end (${endVal}) 必须 >= properties.start (${startVal})`,
|
|
550
561
|
sample: String(endVal),
|
|
551
562
|
});
|
|
552
563
|
}
|
|
553
564
|
}
|
|
554
|
-
if (loopType ===
|
|
555
|
-
const exprVal = loopField(
|
|
556
|
-
if (!exprVal || typeof exprVal !==
|
|
565
|
+
if (loopType === 'ARRAY' || loopType === 'CONDITION') {
|
|
566
|
+
const exprVal = loopField('expression');
|
|
567
|
+
if (!exprVal || typeof exprVal !== 'string' || !exprVal.trim()) {
|
|
557
568
|
errors.push({
|
|
558
|
-
field:
|
|
559
|
-
rule:
|
|
569
|
+
field: 'properties.expression',
|
|
570
|
+
rule: 'required',
|
|
560
571
|
message: `${loopType} 循环的 properties.expression 是必填字段`,
|
|
561
|
-
sample: exprVal ? String(exprVal).substring(0, 80) :
|
|
572
|
+
sample: exprVal ? String(exprVal).substring(0, 80) : '',
|
|
562
573
|
});
|
|
563
574
|
}
|
|
564
575
|
}
|
|
565
|
-
const ascVal = loopField(
|
|
566
|
-
if (typeof ascVal !==
|
|
576
|
+
const ascVal = loopField('asc');
|
|
577
|
+
if (typeof ascVal !== 'boolean') {
|
|
567
578
|
errors.push({
|
|
568
|
-
field:
|
|
569
|
-
rule:
|
|
579
|
+
field: 'properties.asc',
|
|
580
|
+
rule: 'type',
|
|
570
581
|
message: `properties.asc 必须是 boolean 类型,当前为 ${typeof ascVal}`,
|
|
571
582
|
sample: String(ascVal),
|
|
572
583
|
});
|
|
573
584
|
}
|
|
574
|
-
const errExecVal = loopField(
|
|
585
|
+
const errExecVal = loopField('errorExec');
|
|
575
586
|
if (!errExecVal || !ERROR_EXEC_OPTIONS.has(errExecVal)) {
|
|
576
587
|
errors.push({
|
|
577
|
-
field:
|
|
578
|
-
rule:
|
|
588
|
+
field: 'properties.errorExec',
|
|
589
|
+
rule: 'required',
|
|
579
590
|
message: `properties.errorExec 必须是 END / CONTINUE / BREAK 之一,当前为 "${errExecVal}"`,
|
|
580
|
-
sample: errExecVal ? String(errExecVal) :
|
|
591
|
+
sample: errExecVal ? String(errExecVal) : '',
|
|
581
592
|
});
|
|
582
593
|
}
|
|
583
594
|
break;
|
|
584
595
|
}
|
|
585
|
-
case
|
|
596
|
+
case 'SwitchNode': {
|
|
586
597
|
const branches = props.branches;
|
|
587
598
|
if (!Array.isArray(branches)) {
|
|
588
599
|
errors.push({
|
|
589
|
-
field:
|
|
590
|
-
rule:
|
|
600
|
+
field: 'properties.branches',
|
|
601
|
+
rule: 'type',
|
|
591
602
|
message: `properties.branches 必须是 array 类型,当前为 ${typeof branches}`,
|
|
592
603
|
sample: safeSample(branches),
|
|
593
604
|
});
|
|
594
605
|
}
|
|
595
606
|
else if (branches.length < 2 || branches.length > 10) {
|
|
596
607
|
errors.push({
|
|
597
|
-
field:
|
|
598
|
-
rule:
|
|
608
|
+
field: 'properties.branches',
|
|
609
|
+
rule: 'type',
|
|
599
610
|
message: `properties.branches 需要 2-10 个分支,当前为 ${branches.length} 个`,
|
|
600
|
-
sample:
|
|
611
|
+
sample: '',
|
|
601
612
|
});
|
|
602
613
|
}
|
|
603
614
|
else {
|
|
604
615
|
const seenIds = new Set();
|
|
605
616
|
branches.forEach((branch, i) => {
|
|
606
617
|
const brPath = `properties.branches[${i}]`;
|
|
607
|
-
if (!branch || typeof branch !==
|
|
618
|
+
if (!branch || typeof branch !== 'object') {
|
|
608
619
|
errors.push({
|
|
609
620
|
field: brPath,
|
|
610
|
-
rule:
|
|
621
|
+
rule: 'type',
|
|
611
622
|
message: `${brPath} 必须是 object 类型`,
|
|
612
623
|
sample: safeSample(branch),
|
|
613
624
|
});
|
|
614
625
|
return;
|
|
615
626
|
}
|
|
616
|
-
if (!branch.id || typeof branch.id !==
|
|
627
|
+
if (!branch.id || typeof branch.id !== 'string' || !branch.id.trim()) {
|
|
617
628
|
errors.push({
|
|
618
629
|
field: `${brPath}.id`,
|
|
619
|
-
rule:
|
|
630
|
+
rule: 'required',
|
|
620
631
|
message: `${brPath}.id 是必填字段`,
|
|
621
|
-
sample:
|
|
632
|
+
sample: '',
|
|
622
633
|
});
|
|
623
634
|
}
|
|
624
635
|
else if (seenIds.has(branch.id)) {
|
|
625
636
|
errors.push({
|
|
626
637
|
field: `${brPath}.id`,
|
|
627
|
-
rule:
|
|
638
|
+
rule: 'type',
|
|
628
639
|
message: `分支 id "${branch.id}" 重复`,
|
|
629
640
|
sample: branch.id,
|
|
630
641
|
});
|
|
@@ -632,20 +643,20 @@ function validateSpecialNodeType(nodeType, data) {
|
|
|
632
643
|
else {
|
|
633
644
|
seenIds.add(branch.id);
|
|
634
645
|
}
|
|
635
|
-
if (!branch.name || typeof branch.name !==
|
|
646
|
+
if (!branch.name || typeof branch.name !== 'string' || !branch.name.trim()) {
|
|
636
647
|
errors.push({
|
|
637
648
|
field: `${brPath}.name`,
|
|
638
|
-
rule:
|
|
649
|
+
rule: 'required',
|
|
639
650
|
message: `${brPath}.name 是必填字段`,
|
|
640
|
-
sample:
|
|
651
|
+
sample: '',
|
|
641
652
|
});
|
|
642
653
|
}
|
|
643
654
|
if (!branch.or_list) {
|
|
644
655
|
errors.push({
|
|
645
656
|
field: `${brPath}.or_list`,
|
|
646
|
-
rule:
|
|
657
|
+
rule: 'required',
|
|
647
658
|
message: `${brPath}.or_list 是必填字段`,
|
|
648
|
-
sample:
|
|
659
|
+
sample: '',
|
|
649
660
|
});
|
|
650
661
|
}
|
|
651
662
|
else {
|
|
@@ -660,18 +671,20 @@ function validateSpecialNodeType(nodeType, data) {
|
|
|
660
671
|
}
|
|
661
672
|
function isPlainObject(value) {
|
|
662
673
|
return (value !== null &&
|
|
663
|
-
typeof value ===
|
|
674
|
+
typeof value === 'object' &&
|
|
664
675
|
!Array.isArray(value) &&
|
|
665
|
-
Object.prototype.toString.call(value) ===
|
|
676
|
+
Object.prototype.toString.call(value) === '[object Object]');
|
|
666
677
|
}
|
|
667
678
|
function deepMerge(target, ...sources) {
|
|
679
|
+
const tgt = target;
|
|
668
680
|
for (const source of sources) {
|
|
669
|
-
if (source == null || typeof source !==
|
|
681
|
+
if (source == null || typeof source !== 'object' || Array.isArray(source)) {
|
|
670
682
|
continue;
|
|
671
683
|
}
|
|
672
|
-
const
|
|
673
|
-
|
|
674
|
-
|
|
684
|
+
const src = source;
|
|
685
|
+
const subParams = tgt.sub_params;
|
|
686
|
+
for (const key of Object.keys(src)) {
|
|
687
|
+
const srcVal = src[key];
|
|
675
688
|
// sub_params-aware merge: 匹配 name 并原地修改 target 的 sub_params 中的 value
|
|
676
689
|
if (Array.isArray(subParams)) {
|
|
677
690
|
const matched = subParams.find((item) => item.name === key);
|
|
@@ -685,15 +698,16 @@ function deepMerge(target, ...sources) {
|
|
|
685
698
|
continue;
|
|
686
699
|
}
|
|
687
700
|
}
|
|
688
|
-
const tgtVal =
|
|
701
|
+
const tgtVal = tgt[key];
|
|
689
702
|
// sub_params 数组按 name 合并,避免整体替换导致数据丢失
|
|
690
|
-
if (key ===
|
|
703
|
+
if (key === 'sub_params' && Array.isArray(tgtVal) && Array.isArray(srcVal)) {
|
|
704
|
+
const tgtArr = tgtVal;
|
|
691
705
|
for (const srcItem of srcVal) {
|
|
692
|
-
if (!srcItem || typeof srcItem.name !==
|
|
693
|
-
|
|
706
|
+
if (!srcItem || typeof srcItem.name !== 'string') {
|
|
707
|
+
tgtArr.push(srcItem);
|
|
694
708
|
continue;
|
|
695
709
|
}
|
|
696
|
-
const matched =
|
|
710
|
+
const matched = tgtArr.find((t) => t.name === srcItem.name);
|
|
697
711
|
if (matched) {
|
|
698
712
|
if (matched.sub_params && isPlainObject(srcItem.value)) {
|
|
699
713
|
deepMerge(matched, srcItem.value);
|
|
@@ -705,15 +719,15 @@ function deepMerge(target, ...sources) {
|
|
|
705
719
|
matched.update = srcItem.update;
|
|
706
720
|
}
|
|
707
721
|
else {
|
|
708
|
-
|
|
722
|
+
tgtArr.push(srcItem);
|
|
709
723
|
}
|
|
710
724
|
}
|
|
711
725
|
}
|
|
712
726
|
else if (isPlainObject(tgtVal) && isPlainObject(srcVal)) {
|
|
713
|
-
|
|
727
|
+
tgt[key] = deepMerge({ ...tgtVal }, srcVal);
|
|
714
728
|
}
|
|
715
729
|
else {
|
|
716
|
-
|
|
730
|
+
tgt[key] = srcVal;
|
|
717
731
|
}
|
|
718
732
|
}
|
|
719
733
|
}
|