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.
Files changed (91) hide show
  1. package/.github/workflows/ci.yml +24 -0
  2. package/.github/workflows/publish.yml +21 -0
  3. package/CHANGELOG.md +84 -0
  4. package/dist/api/client.d.ts.map +1 -1
  5. package/dist/api/client.js +4 -4
  6. package/dist/api/client.js.map +1 -1
  7. package/dist/api/flow.d.ts.map +1 -1
  8. package/dist/api/flow.js +16 -13
  9. package/dist/api/flow.js.map +1 -1
  10. package/dist/api/knowledge.d.ts.map +1 -1
  11. package/dist/api/knowledge.js +3 -3
  12. package/dist/api/knowledge.js.map +1 -1
  13. package/dist/api/node.d.ts.map +1 -1
  14. package/dist/api/node.js +4 -4
  15. package/dist/api/node.js.map +1 -1
  16. package/dist/api/variables.d.ts.map +1 -1
  17. package/dist/api/variables.js +1 -1
  18. package/dist/api/variables.js.map +1 -1
  19. package/dist/commands/auth.d.ts.map +1 -1
  20. package/dist/commands/auth.js +53 -77
  21. package/dist/commands/auth.js.map +1 -1
  22. package/dist/commands/config.d.ts.map +1 -1
  23. package/dist/commands/config.js +24 -26
  24. package/dist/commands/config.js.map +1 -1
  25. package/dist/commands/flow/account.d.ts.map +1 -1
  26. package/dist/commands/flow/account.js +17 -16
  27. package/dist/commands/flow/account.js.map +1 -1
  28. package/dist/commands/flow/analyze.d.ts.map +1 -1
  29. package/dist/commands/flow/analyze.js +42 -42
  30. package/dist/commands/flow/analyze.js.map +1 -1
  31. package/dist/commands/flow/create.d.ts.map +1 -1
  32. package/dist/commands/flow/create.js +7 -6
  33. package/dist/commands/flow/create.js.map +1 -1
  34. package/dist/commands/flow/edge.d.ts.map +1 -1
  35. package/dist/commands/flow/edge.js +36 -35
  36. package/dist/commands/flow/edge.js.map +1 -1
  37. package/dist/commands/flow/helpers.d.ts.map +1 -1
  38. package/dist/commands/flow/helpers.js +84 -74
  39. package/dist/commands/flow/helpers.js.map +1 -1
  40. package/dist/commands/flow/list.d.ts.map +1 -1
  41. package/dist/commands/flow/list.js +7 -7
  42. package/dist/commands/flow/list.js.map +1 -1
  43. package/dist/commands/flow/node.d.ts.map +1 -1
  44. package/dist/commands/flow/node.js +152 -139
  45. package/dist/commands/flow/node.js.map +1 -1
  46. package/dist/commands/flow/test.d.ts.map +1 -1
  47. package/dist/commands/flow/test.js +14 -14
  48. package/dist/commands/flow/test.js.map +1 -1
  49. package/dist/commands/flow/update.js +5 -5
  50. package/dist/commands/flow/update.js.map +1 -1
  51. package/dist/commands/flow/variables.d.ts.map +1 -1
  52. package/dist/commands/flow/variables.js +45 -43
  53. package/dist/commands/flow/variables.js.map +1 -1
  54. package/dist/commands/flow.d.ts.map +1 -1
  55. package/dist/commands/flow.js +1 -3
  56. package/dist/commands/flow.js.map +1 -1
  57. package/dist/commands/knowledge.d.ts.map +1 -1
  58. package/dist/commands/knowledge.js +67 -70
  59. package/dist/commands/knowledge.js.map +1 -1
  60. package/dist/index.js +31 -20
  61. package/dist/index.js.map +1 -1
  62. package/dist/services/flowAnalyzer.d.ts.map +1 -1
  63. package/dist/services/flowAnalyzer.js +256 -203
  64. package/dist/services/flowAnalyzer.js.map +1 -1
  65. package/dist/services/skillGenerator.d.ts.map +1 -1
  66. package/dist/services/skillGenerator.js +33 -85
  67. package/dist/services/skillGenerator.js.map +1 -1
  68. package/dist/types/index.d.ts.map +1 -1
  69. package/dist/utils/config.d.ts.map +1 -1
  70. package/dist/utils/config.js +12 -10
  71. package/dist/utils/config.js.map +1 -1
  72. package/dist/utils/encoding.d.ts.map +1 -1
  73. package/dist/utils/encoding.js +3 -3
  74. package/dist/utils/encoding.js.map +1 -1
  75. package/dist/utils/format.d.ts.map +1 -1
  76. package/dist/utils/format.js +234 -220
  77. package/dist/utils/format.js.map +1 -1
  78. package/dist/utils/oauth.d.ts.map +1 -1
  79. package/dist/utils/oauth.js +54 -78
  80. package/dist/utils/oauth.js.map +1 -1
  81. package/dist/utils/output.d.ts.map +1 -1
  82. package/dist/utils/output.js +20 -16
  83. package/dist/utils/output.js.map +1 -1
  84. package/dist/utils/updateCheck.d.ts.map +1 -0
  85. package/dist/utils/updateCheck.js +138 -0
  86. package/dist/utils/updateCheck.js.map +1 -0
  87. package/package.json +15 -7
  88. package/tsconfig.json +1 -1
  89. package/dist/utils/auth.d.ts.map +0 -1
  90. package/dist/utils/auth.js +0 -213
  91. package/dist/utils/auth.js.map +0 -1
@@ -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, "\\u0000");
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 === "string") {
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 ?? "").trim();
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 "string";
52
+ return 'string';
53
53
  }
54
54
  function inferScenarios(actionName, description) {
55
55
  const scenarios = [];
56
- const text = `${actionName} ${description || ""}`.toLowerCase();
57
- if (text.includes("trigger") || text.includes("webhook")) {
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("http") || text.includes("api") || text.includes("rest")) {
61
- scenarios.push("调用外部 HTTP API 接口,实现系统间的数据交互");
60
+ if (text.includes('http') || text.includes('api') || text.includes('rest')) {
61
+ scenarios.push('调用外部 HTTP API 接口,实现系统间的数据交互');
62
62
  }
63
- if (text.includes("database") ||
64
- text.includes("sql") ||
65
- text.includes("mysql") ||
66
- text.includes("postgres") ||
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("email") || text.includes("mail") || text.includes("邮件")) {
71
- scenarios.push("发送或接收邮件通知");
70
+ if (text.includes('email') || text.includes('mail') || text.includes('邮件')) {
71
+ scenarios.push('发送或接收邮件通知');
72
72
  }
73
- if (text.includes("ai") || text.includes("llm") || text.includes("model")) {
74
- scenarios.push("调用 AI 大模型进行文本生成、分析或推理");
73
+ if (text.includes('ai') || text.includes('llm') || text.includes('model')) {
74
+ scenarios.push('调用 AI 大模型进行文本生成、分析或推理');
75
75
  }
76
- if (text.includes("condition") || text.includes("条件")) {
77
- scenarios.push("根据条件判断结果进行流程分支控制");
76
+ if (text.includes('condition') || text.includes('条件')) {
77
+ scenarios.push('根据条件判断结果进行流程分支控制');
78
78
  }
79
- if (text.includes("loop") || text.includes("循环")) {
80
- scenarios.push("对数据集进行批量循环处理");
79
+ if (text.includes('loop') || text.includes('循环')) {
80
+ scenarios.push('对数据集进行批量循环处理');
81
81
  }
82
- if (text.includes("transform") || text.includes("转换") || text.includes("map")) {
83
- scenarios.push("对数据进行格式转换或映射处理");
82
+ if (text.includes('transform') || text.includes('转换') || text.includes('map')) {
83
+ scenarios.push('对数据进行格式转换或映射处理');
84
84
  }
85
- if (text.includes("notification") || text.includes("通知") || text.includes("message")) {
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 !== "object")
103
+ if (!node || typeof node !== 'object')
104
104
  return;
105
- if (node.sub_params && Array.isArray(node.sub_params)) {
106
- for (const child of node.sub_params) {
107
- if (child.value === undefined || child.value === null) {
108
- child.value = inferDefaultValue(child);
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
- child.update = true;
111
- walk(child);
112
+ c.update = true;
113
+ walk(c);
112
114
  }
113
115
  }
114
- if (node.value === undefined || node.value === null) {
115
- if (node.sub_params) {
116
- node.value = null;
116
+ if (n.value === undefined || n.value === null) {
117
+ if (n.sub_params) {
118
+ n.value = null;
117
119
  }
118
120
  else {
119
- node.value = inferDefaultValue(node);
121
+ n.value = inferDefaultValue(n);
120
122
  }
121
123
  }
122
- node.update = true;
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 || "").toUpperCase();
131
- if (type === "SELECT") {
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 === "string")
139
+ if (typeof first === 'string')
138
140
  return first;
139
- if (typeof first === "object" && first !== null) {
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 "NUMBER":
148
+ case 'NUMBER':
147
149
  return 0;
148
- case "BOOLEAN":
149
- case "SWITCH":
150
+ case 'BOOLEAN':
151
+ case 'SWITCH':
150
152
  return false;
151
- case "DATE":
152
- case "TIME":
153
- case "DATETIME":
153
+ case 'DATE':
154
+ case 'TIME':
155
+ case 'DATETIME':
154
156
  return null;
155
- case "JSON":
156
- return "{}";
157
- case "EXTEND_OBJ":
158
- case "FIXED_OBJ":
157
+ case 'JSON':
158
+ return '{}';
159
+ case 'EXTEND_OBJ':
160
+ case 'FIXED_OBJ':
159
161
  return param.sub_params ? null : {};
160
- case "TEXTAREA":
161
- case "PASSWORD":
162
- case "HIDDEN":
163
- case "LABEL":
164
- case "TEXT":
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
- if (!node?.sub_params)
187
+ const n = node;
188
+ if (!n.sub_params)
186
189
  return;
187
- for (const child of node.sub_params) {
188
- if (child.name && child.name in overrideMap) {
189
- const val = overrideMap[child.name];
190
- if (child.sub_params && typeof val === "object" && !Array.isArray(val)) {
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 !== "object")
215
+ if (!node || typeof node !== 'object')
212
216
  return;
213
- if (node.sub_params && Array.isArray(node.sub_params)) {
214
- for (const child of node.sub_params) {
215
- const childPath = path
216
- ? `${path}.${child.name || "(anonymous)"}`
217
- : (child.name || "(anonymous)");
218
- checkField(child, childPath);
219
- walk(child, childPath);
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 || "").toUpperCase();
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: "required",
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 "SELECT":
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: "type",
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 !== "string") {
261
+ if (typeof val !== 'string') {
257
262
  errors.push({
258
263
  field: fieldPath,
259
- rule: "type",
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 "NUMBER":
267
- if (typeof val !== "number") {
271
+ case 'NUMBER':
272
+ if (typeof val !== 'number') {
268
273
  errors.push({
269
274
  field: fieldPath,
270
- rule: "type",
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 "BOOLEAN":
277
- case "SWITCH":
278
- if (typeof val !== "boolean") {
281
+ case 'BOOLEAN':
282
+ case 'SWITCH':
283
+ if (typeof val !== 'boolean') {
279
284
  errors.push({
280
285
  field: fieldPath,
281
- rule: "type",
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 "EXTEND_OBJ":
288
- case "FIXED_OBJ":
289
- if (typeof val !== "object" || Array.isArray(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: "type",
293
- message: `${pageControl} 字段 "${field.display_name || field.name}" 期望 object 类型,实际为 ${Array.isArray(val) ? "array" : typeof 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 "JSON":
299
- if (typeof val !== "string") {
303
+ case 'JSON':
304
+ if (typeof val !== 'string') {
300
305
  errors.push({
301
306
  field: fieldPath,
302
- rule: "type",
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: "format",
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 "DATE":
322
- case "TIME":
323
- case "DATETIME":
324
- if (typeof val !== "string") {
326
+ case 'DATE':
327
+ case 'TIME':
328
+ case 'DATETIME':
329
+ if (typeof val !== 'string') {
325
330
  errors.push({
326
331
  field: fieldPath,
327
- rule: "type",
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 "TEXT":
334
- case "TEXTAREA":
335
- case "PASSWORD":
336
- case "HIDDEN":
337
- case "LABEL":
338
- if (typeof val !== "string") {
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: "type",
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
- "eq", "ne", "gt", "ge", "lt", "le",
356
- "sw", "ew", "co", "nco", "in", "nin",
357
- "is_null", "is_not_null",
358
- ]);
359
- const NULL_OPERATORS = new Set(["is_null", "is_not_null"]);
360
- const LOOP_TYPES = new Set(["NUMBER", "ARRAY", "CONDITION"]);
361
- const ERROR_EXEC_OPTIONS = new Set(["END", "CONTINUE", "BREAK"]);
362
- const SPECIAL_NODE_TYPES = new Set([
363
- "ConditionNode",
364
- "LoopNode",
365
- "SelfLoopNode",
366
- "SwitchNode",
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) ?? "").substring(0, 80);
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: "type",
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: "required",
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 !== "object") {
404
+ if (!orItem || typeof orItem !== 'object') {
394
405
  errors.push({
395
406
  field: orPath,
396
- rule: "type",
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: "type",
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: "required",
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 !== "object") {
434
+ if (!item || typeof item !== 'object') {
424
435
  errors.push({
425
436
  field: itemPath,
426
- rule: "type",
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 !== "string" || !item.query.trim()) {
443
+ if (!item.query || typeof item.query !== 'string' || !item.query.trim()) {
433
444
  errors.push({
434
445
  field: `${itemPath}.query`,
435
- rule: "required",
446
+ rule: 'required',
436
447
  message: `${itemPath}.query 是必填字段,须为变量引用`,
437
- sample: "",
448
+ sample: '',
438
449
  });
439
450
  }
440
- if (!item.operator || typeof item.operator !== "string") {
451
+ if (!item.operator || typeof item.operator !== 'string') {
441
452
  errors.push({
442
453
  field: `${itemPath}.operator`,
443
- rule: "required",
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: "type",
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: "required",
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 "ConditionNode": {
491
+ case 'ConditionNode': {
481
492
  if (props.or_list === undefined || props.or_list === null) {
482
493
  errors.push({
483
- field: "properties.or_list",
484
- rule: "required",
485
- message: "ConditionNode 缺少必填字段 properties.or_list",
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, "properties.or_list"));
501
+ errors.push(...validateOrList(props.or_list, 'properties.or_list'));
491
502
  }
492
503
  break;
493
504
  }
494
- case "LoopNode":
495
- case "SelfLoopNode": {
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("type");
520
+ const loopType = loopField('type');
510
521
  if (!loopType || !LOOP_TYPES.has(loopType)) {
511
522
  errors.push({
512
- field: "properties.type",
513
- rule: "required",
523
+ field: 'properties.type',
524
+ rule: 'required',
514
525
  message: `循环节点 properties.type 必须是 NUMBER / ARRAY / CONDITION 之一,当前为 "${loopType}"`,
515
- sample: loopType ? String(loopType) : "",
526
+ sample: loopType || '',
516
527
  });
517
528
  }
518
- if (loopType === "NUMBER") {
519
- const startVal = loopField("start");
520
- const endVal = loopField("end");
521
- if (typeof startVal !== "number") {
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: "properties.start",
524
- rule: "type",
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: "properties.start",
532
- rule: "type",
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 !== "number") {
548
+ if (typeof endVal !== 'number') {
538
549
  errors.push({
539
- field: "properties.end",
540
- rule: "type",
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 === "number" && endVal < startVal) {
556
+ else if (typeof startVal === 'number' && endVal < startVal) {
546
557
  errors.push({
547
- field: "properties.end",
548
- rule: "type",
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 === "ARRAY" || loopType === "CONDITION") {
555
- const exprVal = loopField("expression");
556
- if (!exprVal || typeof exprVal !== "string" || !exprVal.trim()) {
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: "properties.expression",
559
- rule: "required",
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("asc");
566
- if (typeof ascVal !== "boolean") {
576
+ const ascVal = loopField('asc');
577
+ if (typeof ascVal !== 'boolean') {
567
578
  errors.push({
568
- field: "properties.asc",
569
- rule: "type",
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("errorExec");
585
+ const errExecVal = loopField('errorExec');
575
586
  if (!errExecVal || !ERROR_EXEC_OPTIONS.has(errExecVal)) {
576
587
  errors.push({
577
- field: "properties.errorExec",
578
- rule: "required",
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 "SwitchNode": {
596
+ case 'SwitchNode': {
586
597
  const branches = props.branches;
587
598
  if (!Array.isArray(branches)) {
588
599
  errors.push({
589
- field: "properties.branches",
590
- rule: "type",
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: "properties.branches",
598
- rule: "type",
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 !== "object") {
618
+ if (!branch || typeof branch !== 'object') {
608
619
  errors.push({
609
620
  field: brPath,
610
- rule: "type",
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 !== "string" || !branch.id.trim()) {
627
+ if (!branch.id || typeof branch.id !== 'string' || !branch.id.trim()) {
617
628
  errors.push({
618
629
  field: `${brPath}.id`,
619
- rule: "required",
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: "type",
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 !== "string" || !branch.name.trim()) {
646
+ if (!branch.name || typeof branch.name !== 'string' || !branch.name.trim()) {
636
647
  errors.push({
637
648
  field: `${brPath}.name`,
638
- rule: "required",
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: "required",
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 === "object" &&
674
+ typeof value === 'object' &&
664
675
  !Array.isArray(value) &&
665
- Object.prototype.toString.call(value) === "[object Object]");
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 !== "object" || Array.isArray(source)) {
681
+ if (source == null || typeof source !== 'object' || Array.isArray(source)) {
670
682
  continue;
671
683
  }
672
- const subParams = target.sub_params;
673
- for (const key of Object.keys(source)) {
674
- const srcVal = source[key];
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 = target[key];
701
+ const tgtVal = tgt[key];
689
702
  // sub_params 数组按 name 合并,避免整体替换导致数据丢失
690
- if (key === "sub_params" && Array.isArray(tgtVal) && Array.isArray(srcVal)) {
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 !== "string") {
693
- tgtVal.push(srcItem);
706
+ if (!srcItem || typeof srcItem.name !== 'string') {
707
+ tgtArr.push(srcItem);
694
708
  continue;
695
709
  }
696
- const matched = tgtVal.find((t) => t.name === srcItem.name);
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
- tgtVal.push(srcItem);
722
+ tgtArr.push(srcItem);
709
723
  }
710
724
  }
711
725
  }
712
726
  else if (isPlainObject(tgtVal) && isPlainObject(srcVal)) {
713
- target[key] = deepMerge({ ...tgtVal }, srcVal);
727
+ tgt[key] = deepMerge({ ...tgtVal }, srcVal);
714
728
  }
715
729
  else {
716
- target[key] = srcVal;
730
+ tgt[key] = srcVal;
717
731
  }
718
732
  }
719
733
  }