Compare commits
6 Commits
local-deve
...
68e54d5110
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68e54d5110 | ||
|
|
5f133fbfc3 | ||
|
|
d28fbbbba8 | ||
|
|
67f41c08a0 | ||
| 111ca49815 | |||
| 779fd0eb18 |
@@ -0,0 +1,19 @@
|
|||||||
|
package com.lanyuanxiaoyao.service.ai.web.engine;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程图中的边
|
||||||
|
*
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250630
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||||
|
public class FlowEdge {
|
||||||
|
@EqualsAndHashCode.Include
|
||||||
|
private String id;
|
||||||
|
private String source;
|
||||||
|
private String target;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.lanyuanxiaoyao.service.ai.web.engine;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程执行器
|
||||||
|
*
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250630
|
||||||
|
*/
|
||||||
|
public class FlowExecutor {
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.lanyuanxiaoyao.service.ai.web.engine;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.eclipse.collections.api.set.ImmutableSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程图中的节点
|
||||||
|
*
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250630
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||||
|
public class FlowNode {
|
||||||
|
@EqualsAndHashCode.Include
|
||||||
|
private String id;
|
||||||
|
private ImmutableSet<String> sourceHandlers;
|
||||||
|
private ImmutableSet<String> targetHandlers;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.lanyuanxiaoyao.service.ai.web.engine.store;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据存储
|
||||||
|
*
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250630
|
||||||
|
*/
|
||||||
|
public interface FlowStore {
|
||||||
|
}
|
||||||
@@ -6,7 +6,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"test": "vitest run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^6.0.0",
|
"@ant-design/icons": "^6.0.0",
|
||||||
@@ -41,6 +42,7 @@
|
|||||||
"sass": "^1.89.2",
|
"sass": "^1.89.2",
|
||||||
"typescript": "~5.8.3",
|
"typescript": "~5.8.3",
|
||||||
"vite": "^7.0.0",
|
"vite": "^7.0.0",
|
||||||
"vite-plugin-javascript-obfuscator": "^3.1.0"
|
"vite-plugin-javascript-obfuscator": "^3.1.0",
|
||||||
|
"vitest": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
323
service-web/client/pnpm-lock.yaml
generated
323
service-web/client/pnpm-lock.yaml
generated
@@ -102,6 +102,9 @@ importers:
|
|||||||
vite-plugin-javascript-obfuscator:
|
vite-plugin-javascript-obfuscator:
|
||||||
specifier: ^3.1.0
|
specifier: ^3.1.0
|
||||||
version: 3.1.0
|
version: 3.1.0
|
||||||
|
vitest:
|
||||||
|
specifier: ^3.2.4
|
||||||
|
version: 3.2.4(@types/debug@4.1.12)(sass@1.89.2)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -499,6 +502,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-CZFX7UZVN9VopGbjTx4UXaXsi9ewoM1buL0kY7j1ftYdSs7p2spv9opxFjHlQ/QGTgh4UqufYqJJ0WKLml7b6w==}
|
resolution: {integrity: sha512-CZFX7UZVN9VopGbjTx4UXaXsi9ewoM1buL0kY7j1ftYdSs7p2spv9opxFjHlQ/QGTgh4UqufYqJJ0WKLml7b6w==}
|
||||||
engines: {node: '>=4.0'}
|
engines: {node: '>=4.0'}
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.5.0':
|
||||||
|
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
|
||||||
|
|
||||||
'@kurkle/color@0.3.4':
|
'@kurkle/color@0.3.4':
|
||||||
resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==}
|
resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==}
|
||||||
|
|
||||||
@@ -865,6 +871,9 @@ packages:
|
|||||||
'@swc/types@0.1.23':
|
'@swc/types@0.1.23':
|
||||||
resolution: {integrity: sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==}
|
resolution: {integrity: sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==}
|
||||||
|
|
||||||
|
'@types/chai@5.2.2':
|
||||||
|
resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
|
||||||
|
|
||||||
'@types/d3-array@3.2.1':
|
'@types/d3-array@3.2.1':
|
||||||
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
|
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
|
||||||
|
|
||||||
@@ -961,6 +970,9 @@ packages:
|
|||||||
'@types/debug@4.1.12':
|
'@types/debug@4.1.12':
|
||||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||||
|
|
||||||
|
'@types/deep-eql@4.0.2':
|
||||||
|
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||||
|
|
||||||
'@types/estree-jsx@1.0.5':
|
'@types/estree-jsx@1.0.5':
|
||||||
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
|
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
|
||||||
|
|
||||||
@@ -1033,6 +1045,35 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^4 || ^5 || ^6 || ^7.0.0-beta.0
|
vite: ^4 || ^5 || ^6 || ^7.0.0-beta.0
|
||||||
|
|
||||||
|
'@vitest/expect@3.2.4':
|
||||||
|
resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==}
|
||||||
|
|
||||||
|
'@vitest/mocker@3.2.4':
|
||||||
|
resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==}
|
||||||
|
peerDependencies:
|
||||||
|
msw: ^2.4.9
|
||||||
|
vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
msw:
|
||||||
|
optional: true
|
||||||
|
vite:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@vitest/pretty-format@3.2.4':
|
||||||
|
resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==}
|
||||||
|
|
||||||
|
'@vitest/runner@3.2.4':
|
||||||
|
resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==}
|
||||||
|
|
||||||
|
'@vitest/snapshot@3.2.4':
|
||||||
|
resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==}
|
||||||
|
|
||||||
|
'@vitest/spy@3.2.4':
|
||||||
|
resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==}
|
||||||
|
|
||||||
|
'@vitest/utils@3.2.4':
|
||||||
|
resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==}
|
||||||
|
|
||||||
'@xyflow/react@12.7.1':
|
'@xyflow/react@12.7.1':
|
||||||
resolution: {integrity: sha512-uvIPQIZdf8tt0mDWvhkEpg/7t5E/e/KE4RWjNczAEhEYA+uvLc+4A5kIPJqCjJJbVHfMiAojT5JOB5mB7/EgFw==}
|
resolution: {integrity: sha512-uvIPQIZdf8tt0mDWvhkEpg/7t5E/e/KE4RWjNczAEhEYA+uvLc+4A5kIPJqCjJJbVHfMiAojT5JOB5mB7/EgFw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1162,6 +1203,10 @@ packages:
|
|||||||
assert@2.0.0:
|
assert@2.0.0:
|
||||||
resolution: {integrity: sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==}
|
resolution: {integrity: sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==}
|
||||||
|
|
||||||
|
assertion-error@2.0.1:
|
||||||
|
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
async@3.2.6:
|
async@3.2.6:
|
||||||
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
|
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
|
||||||
|
|
||||||
@@ -1234,6 +1279,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==}
|
resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==}
|
||||||
engines: {node: '>=0.2.0'}
|
engines: {node: '>=0.2.0'}
|
||||||
|
|
||||||
|
cac@6.7.14:
|
||||||
|
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
call-bind-apply-helpers@1.0.2:
|
call-bind-apply-helpers@1.0.2:
|
||||||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1260,6 +1309,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
||||||
engines: {node: '>=0.8'}
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
|
chai@5.2.0:
|
||||||
|
resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
chainsaw@0.1.0:
|
chainsaw@0.1.0:
|
||||||
resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==}
|
resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==}
|
||||||
|
|
||||||
@@ -1293,6 +1346,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==}
|
resolution: {integrity: sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==}
|
||||||
engines: {pnpm: '>=8'}
|
engines: {pnpm: '>=8'}
|
||||||
|
|
||||||
|
check-error@2.1.1:
|
||||||
|
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
|
||||||
|
engines: {node: '>= 16'}
|
||||||
|
|
||||||
chevrotain-allstar@0.3.1:
|
chevrotain-allstar@0.3.1:
|
||||||
resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==}
|
resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1616,6 +1673,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==}
|
resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
deep-eql@5.0.2:
|
||||||
|
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
deep-is@0.1.4:
|
deep-is@0.1.4:
|
||||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||||
|
|
||||||
@@ -1705,6 +1766,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
es-module-lexer@1.7.0:
|
||||||
|
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
|
||||||
|
|
||||||
es-object-atoms@1.1.1:
|
es-object-atoms@1.1.1:
|
||||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1748,6 +1812,9 @@ packages:
|
|||||||
estree-util-is-identifier-name@3.0.0:
|
estree-util-is-identifier-name@3.0.0:
|
||||||
resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
|
resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
|
||||||
|
|
||||||
|
estree-walker@3.0.3:
|
||||||
|
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||||
|
|
||||||
esutils@2.0.3:
|
esutils@2.0.3:
|
||||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -1756,6 +1823,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==}
|
resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==}
|
||||||
engines: {node: '>=8.3.0'}
|
engines: {node: '>=8.3.0'}
|
||||||
|
|
||||||
|
expect-type@1.2.1:
|
||||||
|
resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
exsolve@1.0.5:
|
exsolve@1.0.5:
|
||||||
resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==}
|
resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==}
|
||||||
|
|
||||||
@@ -2058,6 +2129,9 @@ packages:
|
|||||||
js-tokens@4.0.0:
|
js-tokens@4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
|
js-tokens@9.0.1:
|
||||||
|
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
|
||||||
|
|
||||||
jsbarcode@3.12.1:
|
jsbarcode@3.12.1:
|
||||||
resolution: {integrity: sha512-QZQSqIknC2Rr/YOUyOkCBqsoiBAOTYK+7yNN3JsqfoUtJtkazxNw1dmPpxuv7VVvqW13kA3/mKiLq+s/e3o9hQ==}
|
resolution: {integrity: sha512-QZQSqIknC2Rr/YOUyOkCBqsoiBAOTYK+7yNN3JsqfoUtJtkazxNw1dmPpxuv7VVvqW13kA3/mKiLq+s/e3o9hQ==}
|
||||||
|
|
||||||
@@ -2179,6 +2253,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
loupe@3.1.4:
|
||||||
|
resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==}
|
||||||
|
|
||||||
|
magic-string@0.30.17:
|
||||||
|
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
||||||
|
|
||||||
make-cancellable-promise@1.3.2:
|
make-cancellable-promise@1.3.2:
|
||||||
resolution: {integrity: sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww==}
|
resolution: {integrity: sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww==}
|
||||||
|
|
||||||
@@ -2518,6 +2598,10 @@ packages:
|
|||||||
pathe@2.0.3:
|
pathe@2.0.3:
|
||||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||||
|
|
||||||
|
pathval@2.0.1:
|
||||||
|
resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==}
|
||||||
|
engines: {node: '>= 14.16'}
|
||||||
|
|
||||||
pdfjs-dist@4.3.136:
|
pdfjs-dist@4.3.136:
|
||||||
resolution: {integrity: sha512-gzfnt1qc4yA+U46golPGYtU4WM2ssqP2MvFjKga8GEKOrEnzRPrA/9jogLLPYHiA3sGBPJ+p7BdAq+ytmw3jEg==}
|
resolution: {integrity: sha512-gzfnt1qc4yA+U46golPGYtU4WM2ssqP2MvFjKga8GEKOrEnzRPrA/9jogLLPYHiA3sGBPJ+p7BdAq+ytmw3jEg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -3105,6 +3189,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
siginfo@2.0.0:
|
||||||
|
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||||
|
|
||||||
signal-exit@3.0.7:
|
signal-exit@3.0.7:
|
||||||
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
||||||
|
|
||||||
@@ -3141,6 +3228,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
|
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
|
||||||
engines: {node: '>=0.8'}
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
|
stackback@0.0.2:
|
||||||
|
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||||
|
|
||||||
|
std-env@3.9.0:
|
||||||
|
resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==}
|
||||||
|
|
||||||
string-convert@0.2.1:
|
string-convert@0.2.1:
|
||||||
resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==}
|
resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==}
|
||||||
|
|
||||||
@@ -3167,6 +3260,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
strip-literal@3.0.0:
|
||||||
|
resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
|
||||||
|
|
||||||
style-to-js@1.1.17:
|
style-to-js@1.1.17:
|
||||||
resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==}
|
resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==}
|
||||||
|
|
||||||
@@ -3210,9 +3306,15 @@ packages:
|
|||||||
tiny-invariant@1.3.3:
|
tiny-invariant@1.3.3:
|
||||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||||
|
|
||||||
|
tinybench@2.9.0:
|
||||||
|
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||||
|
|
||||||
tinycolor2@1.6.0:
|
tinycolor2@1.6.0:
|
||||||
resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
|
resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
|
||||||
|
|
||||||
|
tinyexec@0.3.2:
|
||||||
|
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
|
||||||
|
|
||||||
tinyexec@1.0.1:
|
tinyexec@1.0.1:
|
||||||
resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
|
resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
|
||||||
|
|
||||||
@@ -3223,6 +3325,18 @@ packages:
|
|||||||
tinymce@6.8.6:
|
tinymce@6.8.6:
|
||||||
resolution: {integrity: sha512-++XYEs8lKWvZxDCjrr8Baiw7KiikraZ5JkLMg6EdnUVNKJui0IsrAADj5MsyUeFkcEryfn2jd3p09H7REvewyg==}
|
resolution: {integrity: sha512-++XYEs8lKWvZxDCjrr8Baiw7KiikraZ5JkLMg6EdnUVNKJui0IsrAADj5MsyUeFkcEryfn2jd3p09H7REvewyg==}
|
||||||
|
|
||||||
|
tinypool@1.1.1:
|
||||||
|
resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==}
|
||||||
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
|
|
||||||
|
tinyrainbow@2.0.0:
|
||||||
|
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
tinyspy@4.0.3:
|
||||||
|
resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
tmp@0.2.3:
|
tmp@0.2.3:
|
||||||
resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
|
resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
|
||||||
engines: {node: '>=14.14'}
|
engines: {node: '>=14.14'}
|
||||||
@@ -3369,6 +3483,11 @@ packages:
|
|||||||
react: ^15.0.0 || ^16.0.0 || ^17.0.0
|
react: ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||||
react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0
|
react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||||
|
|
||||||
|
vite-node@3.2.4:
|
||||||
|
resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
|
||||||
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
vite-plugin-javascript-obfuscator@3.1.0:
|
vite-plugin-javascript-obfuscator@3.1.0:
|
||||||
resolution: {integrity: sha512-sf4JFlG1iUPl7bLXHGOy+bKWOQUFyXzJFWa+n2S2xMMvyfM+V9R40HhpZoIF1eAjifArM1SF7fbSFIaTuUIbPA==}
|
resolution: {integrity: sha512-sf4JFlG1iUPl7bLXHGOy+bKWOQUFyXzJFWa+n2S2xMMvyfM+V9R40HhpZoIF1eAjifArM1SF7fbSFIaTuUIbPA==}
|
||||||
|
|
||||||
@@ -3412,6 +3531,34 @@ packages:
|
|||||||
yaml:
|
yaml:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
vitest@3.2.4:
|
||||||
|
resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
|
||||||
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@edge-runtime/vm': '*'
|
||||||
|
'@types/debug': ^4.1.12
|
||||||
|
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||||
|
'@vitest/browser': 3.2.4
|
||||||
|
'@vitest/ui': 3.2.4
|
||||||
|
happy-dom: '*'
|
||||||
|
jsdom: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@edge-runtime/vm':
|
||||||
|
optional: true
|
||||||
|
'@types/debug':
|
||||||
|
optional: true
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
'@vitest/browser':
|
||||||
|
optional: true
|
||||||
|
'@vitest/ui':
|
||||||
|
optional: true
|
||||||
|
happy-dom:
|
||||||
|
optional: true
|
||||||
|
jsdom:
|
||||||
|
optional: true
|
||||||
|
|
||||||
vscode-jsonrpc@8.2.0:
|
vscode-jsonrpc@8.2.0:
|
||||||
resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==}
|
resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
@@ -3454,6 +3601,11 @@ packages:
|
|||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
why-is-node-running@2.3.0:
|
||||||
|
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
wide-align@1.1.5:
|
wide-align@1.1.5:
|
||||||
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
|
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
|
||||||
|
|
||||||
@@ -4007,6 +4159,8 @@ snapshots:
|
|||||||
|
|
||||||
'@javascript-obfuscator/estraverse@5.4.0': {}
|
'@javascript-obfuscator/estraverse@5.4.0': {}
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.5.0': {}
|
||||||
|
|
||||||
'@kurkle/color@0.3.4': {}
|
'@kurkle/color@0.3.4': {}
|
||||||
|
|
||||||
'@lightenna/react-mermaid-diagram@1.0.20(mermaid@11.7.0)(react@18.3.1)':
|
'@lightenna/react-mermaid-diagram@1.0.20(mermaid@11.7.0)(react@18.3.1)':
|
||||||
@@ -4291,6 +4445,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@swc/counter': 0.1.3
|
'@swc/counter': 0.1.3
|
||||||
|
|
||||||
|
'@types/chai@5.2.2':
|
||||||
|
dependencies:
|
||||||
|
'@types/deep-eql': 4.0.2
|
||||||
|
|
||||||
'@types/d3-array@3.2.1': {}
|
'@types/d3-array@3.2.1': {}
|
||||||
|
|
||||||
'@types/d3-axis@3.0.6':
|
'@types/d3-axis@3.0.6':
|
||||||
@@ -4412,6 +4570,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/ms': 2.1.0
|
'@types/ms': 2.1.0
|
||||||
|
|
||||||
|
'@types/deep-eql@4.0.2': {}
|
||||||
|
|
||||||
'@types/estree-jsx@1.0.5':
|
'@types/estree-jsx@1.0.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree': 1.0.8
|
'@types/estree': 1.0.8
|
||||||
@@ -4476,6 +4636,48 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@swc/helpers'
|
- '@swc/helpers'
|
||||||
|
|
||||||
|
'@vitest/expect@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
'@types/chai': 5.2.2
|
||||||
|
'@vitest/spy': 3.2.4
|
||||||
|
'@vitest/utils': 3.2.4
|
||||||
|
chai: 5.2.0
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
|
'@vitest/mocker@3.2.4(vite@7.0.0(sass@1.89.2))':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/spy': 3.2.4
|
||||||
|
estree-walker: 3.0.3
|
||||||
|
magic-string: 0.30.17
|
||||||
|
optionalDependencies:
|
||||||
|
vite: 7.0.0(sass@1.89.2)
|
||||||
|
|
||||||
|
'@vitest/pretty-format@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
|
'@vitest/runner@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/utils': 3.2.4
|
||||||
|
pathe: 2.0.3
|
||||||
|
strip-literal: 3.0.0
|
||||||
|
|
||||||
|
'@vitest/snapshot@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/pretty-format': 3.2.4
|
||||||
|
magic-string: 0.30.17
|
||||||
|
pathe: 2.0.3
|
||||||
|
|
||||||
|
'@vitest/spy@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
tinyspy: 4.0.3
|
||||||
|
|
||||||
|
'@vitest/utils@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/pretty-format': 3.2.4
|
||||||
|
loupe: 3.1.4
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
'@xyflow/react@12.7.1(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
'@xyflow/react@12.7.1(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@xyflow/system': 0.0.63
|
'@xyflow/system': 0.0.63
|
||||||
@@ -4800,6 +5002,8 @@ snapshots:
|
|||||||
object-is: 1.1.6
|
object-is: 1.1.6
|
||||||
util: 0.12.5
|
util: 0.12.5
|
||||||
|
|
||||||
|
assertion-error@2.0.1: {}
|
||||||
|
|
||||||
async@3.2.6: {}
|
async@3.2.6: {}
|
||||||
|
|
||||||
asynckit@0.4.0: {}
|
asynckit@0.4.0: {}
|
||||||
@@ -4870,6 +5074,8 @@ snapshots:
|
|||||||
|
|
||||||
buffers@0.1.1: {}
|
buffers@0.1.1: {}
|
||||||
|
|
||||||
|
cac@6.7.14: {}
|
||||||
|
|
||||||
call-bind-apply-helpers@1.0.2:
|
call-bind-apply-helpers@1.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
@@ -4906,6 +5112,14 @@ snapshots:
|
|||||||
adler-32: 1.3.1
|
adler-32: 1.3.1
|
||||||
crc-32: 1.2.2
|
crc-32: 1.2.2
|
||||||
|
|
||||||
|
chai@5.2.0:
|
||||||
|
dependencies:
|
||||||
|
assertion-error: 2.0.1
|
||||||
|
check-error: 2.1.1
|
||||||
|
deep-eql: 5.0.2
|
||||||
|
loupe: 3.1.4
|
||||||
|
pathval: 2.0.1
|
||||||
|
|
||||||
chainsaw@0.1.0:
|
chainsaw@0.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
traverse: 0.3.9
|
traverse: 0.3.9
|
||||||
@@ -4933,6 +5147,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@kurkle/color': 0.3.4
|
'@kurkle/color': 0.3.4
|
||||||
|
|
||||||
|
check-error@2.1.1: {}
|
||||||
|
|
||||||
chevrotain-allstar@0.3.1(chevrotain@11.0.3):
|
chevrotain-allstar@0.3.1(chevrotain@11.0.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
chevrotain: 11.0.3
|
chevrotain: 11.0.3
|
||||||
@@ -5267,6 +5483,8 @@ snapshots:
|
|||||||
mimic-response: 2.1.0
|
mimic-response: 2.1.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
deep-eql@5.0.2: {}
|
||||||
|
|
||||||
deep-is@0.1.4: {}
|
deep-is@0.1.4: {}
|
||||||
|
|
||||||
define-data-property@1.1.4:
|
define-data-property@1.1.4:
|
||||||
@@ -5361,6 +5579,8 @@ snapshots:
|
|||||||
|
|
||||||
es-errors@1.3.0: {}
|
es-errors@1.3.0: {}
|
||||||
|
|
||||||
|
es-module-lexer@1.7.0: {}
|
||||||
|
|
||||||
es-object-atoms@1.1.1:
|
es-object-atoms@1.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
@@ -5421,6 +5641,10 @@ snapshots:
|
|||||||
|
|
||||||
estree-util-is-identifier-name@3.0.0: {}
|
estree-util-is-identifier-name@3.0.0: {}
|
||||||
|
|
||||||
|
estree-walker@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.8
|
||||||
|
|
||||||
esutils@2.0.3: {}
|
esutils@2.0.3: {}
|
||||||
|
|
||||||
exceljs@4.4.0:
|
exceljs@4.4.0:
|
||||||
@@ -5435,6 +5659,8 @@ snapshots:
|
|||||||
unzipper: 0.10.14
|
unzipper: 0.10.14
|
||||||
uuid: 8.3.2
|
uuid: 8.3.2
|
||||||
|
|
||||||
|
expect-type@1.2.1: {}
|
||||||
|
|
||||||
exsolve@1.0.5: {}
|
exsolve@1.0.5: {}
|
||||||
|
|
||||||
extend@3.0.2: {}
|
extend@3.0.2: {}
|
||||||
@@ -5771,6 +5997,8 @@ snapshots:
|
|||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
|
|
||||||
|
js-tokens@9.0.1: {}
|
||||||
|
|
||||||
jsbarcode@3.12.1: {}
|
jsbarcode@3.12.1: {}
|
||||||
|
|
||||||
json2mq@0.2.0:
|
json2mq@0.2.0:
|
||||||
@@ -5877,6 +6105,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
js-tokens: 4.0.0
|
js-tokens: 4.0.0
|
||||||
|
|
||||||
|
loupe@3.1.4: {}
|
||||||
|
|
||||||
|
magic-string@0.30.17:
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/sourcemap-codec': 1.5.0
|
||||||
|
|
||||||
make-cancellable-promise@1.3.2: {}
|
make-cancellable-promise@1.3.2: {}
|
||||||
|
|
||||||
make-dir@3.1.0:
|
make-dir@3.1.0:
|
||||||
@@ -6359,6 +6593,8 @@ snapshots:
|
|||||||
|
|
||||||
pathe@2.0.3: {}
|
pathe@2.0.3: {}
|
||||||
|
|
||||||
|
pathval@2.0.1: {}
|
||||||
|
|
||||||
pdfjs-dist@4.3.136:
|
pdfjs-dist@4.3.136:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
canvas: 2.11.2
|
canvas: 2.11.2
|
||||||
@@ -7130,6 +7366,8 @@ snapshots:
|
|||||||
|
|
||||||
shebang-regex@3.0.0: {}
|
shebang-regex@3.0.0: {}
|
||||||
|
|
||||||
|
siginfo@2.0.0: {}
|
||||||
|
|
||||||
signal-exit@3.0.7:
|
signal-exit@3.0.7:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -7164,6 +7402,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
frac: 1.1.2
|
frac: 1.1.2
|
||||||
|
|
||||||
|
stackback@0.0.2: {}
|
||||||
|
|
||||||
|
std-env@3.9.0: {}
|
||||||
|
|
||||||
string-convert@0.2.1: {}
|
string-convert@0.2.1: {}
|
||||||
|
|
||||||
string-template@1.0.0: {}
|
string-template@1.0.0: {}
|
||||||
@@ -7197,6 +7439,10 @@ snapshots:
|
|||||||
ansi-regex: 5.0.1
|
ansi-regex: 5.0.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
strip-literal@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
js-tokens: 9.0.1
|
||||||
|
|
||||||
style-to-js@1.1.17:
|
style-to-js@1.1.17:
|
||||||
dependencies:
|
dependencies:
|
||||||
style-to-object: 1.0.9
|
style-to-object: 1.0.9
|
||||||
@@ -7255,8 +7501,12 @@ snapshots:
|
|||||||
|
|
||||||
tiny-invariant@1.3.3: {}
|
tiny-invariant@1.3.3: {}
|
||||||
|
|
||||||
|
tinybench@2.9.0: {}
|
||||||
|
|
||||||
tinycolor2@1.6.0: {}
|
tinycolor2@1.6.0: {}
|
||||||
|
|
||||||
|
tinyexec@0.3.2: {}
|
||||||
|
|
||||||
tinyexec@1.0.1: {}
|
tinyexec@1.0.1: {}
|
||||||
|
|
||||||
tinyglobby@0.2.14:
|
tinyglobby@0.2.14:
|
||||||
@@ -7266,6 +7516,12 @@ snapshots:
|
|||||||
|
|
||||||
tinymce@6.8.6: {}
|
tinymce@6.8.6: {}
|
||||||
|
|
||||||
|
tinypool@1.1.1: {}
|
||||||
|
|
||||||
|
tinyrainbow@2.0.0: {}
|
||||||
|
|
||||||
|
tinyspy@4.0.3: {}
|
||||||
|
|
||||||
tmp@0.2.3: {}
|
tmp@0.2.3: {}
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
@@ -7418,6 +7674,27 @@ snapshots:
|
|||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
redux: 4.2.1
|
redux: 4.2.1
|
||||||
|
|
||||||
|
vite-node@3.2.4(sass@1.89.2):
|
||||||
|
dependencies:
|
||||||
|
cac: 6.7.14
|
||||||
|
debug: 4.4.1
|
||||||
|
es-module-lexer: 1.7.0
|
||||||
|
pathe: 2.0.3
|
||||||
|
vite: 7.0.0(sass@1.89.2)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/node'
|
||||||
|
- jiti
|
||||||
|
- less
|
||||||
|
- lightningcss
|
||||||
|
- sass
|
||||||
|
- sass-embedded
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
- terser
|
||||||
|
- tsx
|
||||||
|
- yaml
|
||||||
|
|
||||||
vite-plugin-javascript-obfuscator@3.1.0:
|
vite-plugin-javascript-obfuscator@3.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
anymatch: 3.1.3
|
anymatch: 3.1.3
|
||||||
@@ -7435,6 +7712,47 @@ snapshots:
|
|||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
sass: 1.89.2
|
sass: 1.89.2
|
||||||
|
|
||||||
|
vitest@3.2.4(@types/debug@4.1.12)(sass@1.89.2):
|
||||||
|
dependencies:
|
||||||
|
'@types/chai': 5.2.2
|
||||||
|
'@vitest/expect': 3.2.4
|
||||||
|
'@vitest/mocker': 3.2.4(vite@7.0.0(sass@1.89.2))
|
||||||
|
'@vitest/pretty-format': 3.2.4
|
||||||
|
'@vitest/runner': 3.2.4
|
||||||
|
'@vitest/snapshot': 3.2.4
|
||||||
|
'@vitest/spy': 3.2.4
|
||||||
|
'@vitest/utils': 3.2.4
|
||||||
|
chai: 5.2.0
|
||||||
|
debug: 4.4.1
|
||||||
|
expect-type: 1.2.1
|
||||||
|
magic-string: 0.30.17
|
||||||
|
pathe: 2.0.3
|
||||||
|
picomatch: 4.0.2
|
||||||
|
std-env: 3.9.0
|
||||||
|
tinybench: 2.9.0
|
||||||
|
tinyexec: 0.3.2
|
||||||
|
tinyglobby: 0.2.14
|
||||||
|
tinypool: 1.1.1
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
vite: 7.0.0(sass@1.89.2)
|
||||||
|
vite-node: 3.2.4(sass@1.89.2)
|
||||||
|
why-is-node-running: 2.3.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/debug': 4.1.12
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- jiti
|
||||||
|
- less
|
||||||
|
- lightningcss
|
||||||
|
- msw
|
||||||
|
- sass
|
||||||
|
- sass-embedded
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
- terser
|
||||||
|
- tsx
|
||||||
|
- yaml
|
||||||
|
|
||||||
vscode-jsonrpc@8.2.0: {}
|
vscode-jsonrpc@8.2.0: {}
|
||||||
|
|
||||||
vscode-languageserver-protocol@3.17.5:
|
vscode-languageserver-protocol@3.17.5:
|
||||||
@@ -7479,6 +7797,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
|
|
||||||
|
why-is-node-running@2.3.0:
|
||||||
|
dependencies:
|
||||||
|
siginfo: 2.0.0
|
||||||
|
stackback: 0.0.2
|
||||||
|
|
||||||
wide-align@1.1.5:
|
wide-align@1.1.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
string-width: 4.2.3
|
string-width: 4.2.3
|
||||||
|
|||||||
113
service-web/client/src/pages/ai/flow/FlowChecker.test.tsx
Normal file
113
service-web/client/src/pages/ai/flow/FlowChecker.test.tsx
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import {type Connection, type Node} from '@xyflow/react'
|
||||||
|
import {uuid} from 'licia'
|
||||||
|
import {expect, test} from 'vitest'
|
||||||
|
import {
|
||||||
|
atLeastOneEndNodeError,
|
||||||
|
atLeastOneStartNodeError,
|
||||||
|
checkAddConnection,
|
||||||
|
checkAddNode,
|
||||||
|
checkSave,
|
||||||
|
hasCycleError,
|
||||||
|
hasRedundantEdgeError,
|
||||||
|
multiEndNodeError,
|
||||||
|
multiStartNodeError,
|
||||||
|
nodeToSelfError,
|
||||||
|
sourceNodeNotFoundError,
|
||||||
|
startNodeToEndNodeError,
|
||||||
|
targetNodeNotFoundError,
|
||||||
|
} from './FlowChecker.tsx'
|
||||||
|
|
||||||
|
const createNode = (id: string, type: string): Node => {
|
||||||
|
return {
|
||||||
|
data: {},
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
},
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createStartNode = (id: string): Node => createNode(id, 'start-node')
|
||||||
|
const createEndNode = (id: string): Node => createNode(id, 'end-node')
|
||||||
|
|
||||||
|
const createConnection = function (source: string, target: string, sourceHandle: string | null = null, targetHandle: string | null = null): Connection {
|
||||||
|
return {
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
sourceHandle,
|
||||||
|
targetHandle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check add node */
|
||||||
|
|
||||||
|
test(multiStartNodeError().message, () => {
|
||||||
|
expect(() => checkAddNode('start-node', [createStartNode(uuid())], [])).toThrowError(multiStartNodeError())
|
||||||
|
})
|
||||||
|
|
||||||
|
test(multiEndNodeError().message, () => {
|
||||||
|
expect(() => checkAddNode('end-node', [createEndNode(uuid())], [])).toThrowError(multiEndNodeError())
|
||||||
|
})
|
||||||
|
|
||||||
|
/* check add connection */
|
||||||
|
test(sourceNodeNotFoundError().message, () => {
|
||||||
|
expect(() => checkAddConnection(createConnection('a', 'b'), [], []))
|
||||||
|
})
|
||||||
|
|
||||||
|
test(targetNodeNotFoundError().message, () => {
|
||||||
|
expect(() => checkAddConnection(createConnection('a', 'b'), [createStartNode('a')], []))
|
||||||
|
})
|
||||||
|
|
||||||
|
test(startNodeToEndNodeError().message, () => {
|
||||||
|
expect(() => checkAddConnection(
|
||||||
|
createConnection('a', 'b'),
|
||||||
|
[createStartNode('a'), createEndNode('b')],
|
||||||
|
[]
|
||||||
|
))
|
||||||
|
})
|
||||||
|
|
||||||
|
test(nodeToSelfError().message, () => {
|
||||||
|
expect(() => {
|
||||||
|
// language=JSON
|
||||||
|
const {
|
||||||
|
nodes,
|
||||||
|
edges
|
||||||
|
} = JSON.parse('{\n "nodes": [\n {\n "id": "P14abHl4uY",\n "type": "start-node",\n "position": {\n "x": 100,\n "y": 100\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 82\n }\n },\n {\n "id": "3YDRebKqCX",\n "type": "end-node",\n "position": {\n "x": 773.3027344262372,\n "y": 101.42648884412338\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 74\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "YXJ91nHVaz",\n "type": "llm-node",\n "position": {\n "x": 430.94541183662506,\n "y": 101.42648884412338\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 74\n },\n "selected": true,\n "dragging": false\n }\n ],\n "edges": [\n {\n "source": "P14abHl4uY",\n "target": "YXJ91nHVaz",\n "id": "xy-edge__P14abHl4uY-YXJ91nHVaz"\n },\n {\n "source": "YXJ91nHVaz",\n "target": "3YDRebKqCX",\n "id": "xy-edge__YXJ91nHVaz-3YDRebKqCX"\n }\n ],\n "data": {}\n}')
|
||||||
|
checkAddConnection(createConnection('YXJ91nHVaz', 'YXJ91nHVaz'), nodes, edges)
|
||||||
|
}).toThrowError(nodeToSelfError())
|
||||||
|
})
|
||||||
|
|
||||||
|
test(hasCycleError().message, () => {
|
||||||
|
expect(() => {
|
||||||
|
// language=JSON
|
||||||
|
const {
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
} = JSON.parse('{\n "nodes": [\n {\n "id": "-DKfXm7r3f",\n "type": "start-node",\n "position": {\n "x": -75.45812782717618,\n "y": 14.410669352596976\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 82\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "2uL3Hw2CAW",\n "type": "end-node",\n "position": {\n "x": 734.7875356349059,\n "y": -1.2807079327602473\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 74\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "yp-yYfKUzC",\n "type": "llm-node",\n "position": {\n "x": 338.2236369686051,\n "y": -92.5759939566568\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 74\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "N4HQPN-NYZ",\n "type": "llm-node",\n "position": {\n "x": 332.51768159211156,\n "y": 114.26488844123382\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 74\n },\n "selected": true,\n "dragging": false\n }\n ],\n "edges": [\n {\n "source": "-DKfXm7r3f",\n "target": "yp-yYfKUzC",\n "id": "xy-edge__-DKfXm7r3f-yp-yYfKUzC"\n },\n {\n "source": "yp-yYfKUzC",\n "target": "2uL3Hw2CAW",\n "id": "xy-edge__yp-yYfKUzC-2uL3Hw2CAW"\n },\n {\n "source": "-DKfXm7r3f",\n "target": "N4HQPN-NYZ",\n "id": "xy-edge__-DKfXm7r3f-N4HQPN-NYZ"\n },\n {\n "source": "N4HQPN-NYZ",\n "target": "yp-yYfKUzC",\n "id": "xy-edge__N4HQPN-NYZ-yp-yYfKUzC"\n }\n ],\n "data": {}\n}')
|
||||||
|
// language=JSON
|
||||||
|
checkAddConnection(JSON.parse('{\n "source": "yp-yYfKUzC",\n "sourceHandle": null,\n "target": "N4HQPN-NYZ",\n "targetHandle": null\n}'), nodes, edges)
|
||||||
|
}).toThrowError(hasCycleError())
|
||||||
|
})
|
||||||
|
|
||||||
|
test(hasRedundantEdgeError().message, () => {
|
||||||
|
expect(() => {
|
||||||
|
// language=JSON
|
||||||
|
const {
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
} = JSON.parse('{\n "nodes": [\n {\n "id": "TCxPixrdkI",\n "type": "start-node",\n "position": {\n "x": -256,\n "y": 109.5\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 83\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "tGs78_ietp",\n "type": "llm-node",\n "position": {\n "x": 108,\n "y": -2.5\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 105\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "OeZdaU7LpY",\n "type": "llm-node",\n "position": {\n "x": 111,\n "y": 196\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 105\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "LjfoCYZo-E",\n "type": "knowledge-node",\n "position": {\n "x": 497.62196259607214,\n "y": -10.792497317791003\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": true,\n "dragging": false\n },\n {\n "id": "sQM_22GYB5",\n "type": "end-node",\n "position": {\n "x": 874.3164534765615,\n "y": 151.70316541496913\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "KpMH_xc3ZZ",\n "type": "llm-node",\n "position": {\n "x": 529.6286840434341,\n "y": 150.4721376669937\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n }\n ],\n "edges": [\n {\n "source": "TCxPixrdkI",\n "sourceHandle": "source",\n "target": "tGs78_ietp",\n "targetHandle": "target",\n "id": "xy-edge__TCxPixrdkIsource-tGs78_ietptarget"\n },\n {\n "source": "TCxPixrdkI",\n "sourceHandle": "source",\n "target": "OeZdaU7LpY",\n "targetHandle": "target",\n "id": "xy-edge__TCxPixrdkIsource-OeZdaU7LpYtarget"\n },\n {\n "source": "tGs78_ietp",\n "sourceHandle": "source",\n "target": "LjfoCYZo-E",\n "targetHandle": "target",\n "id": "xy-edge__tGs78_ietpsource-LjfoCYZo-Etarget"\n },\n {\n "source": "LjfoCYZo-E",\n "sourceHandle": "source",\n "target": "KpMH_xc3ZZ",\n "targetHandle": "target",\n "id": "xy-edge__LjfoCYZo-Esource-KpMH_xc3ZZtarget"\n },\n {\n "source": "OeZdaU7LpY",\n "sourceHandle": "source",\n "target": "KpMH_xc3ZZ",\n "targetHandle": "target",\n "id": "xy-edge__OeZdaU7LpYsource-KpMH_xc3ZZtarget"\n },\n {\n "source": "KpMH_xc3ZZ",\n "sourceHandle": "source",\n "target": "sQM_22GYB5",\n "targetHandle": "target",\n "id": "xy-edge__KpMH_xc3ZZsource-sQM_22GYB5target"\n }\n ],\n "data": {\n "tGs78_ietp": {\n "model": "qwen3",\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "systemPrompt": "你是个聪明人"\n },\n "OeZdaU7LpY": {\n "model": "qwen3",\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "systemPrompt": "你也是个聪明人"\n }\n }\n}')
|
||||||
|
// language=JSON
|
||||||
|
checkAddConnection(JSON.parse('{\n "source": "OeZdaU7LpY",\n "sourceHandle": "source",\n "target": "LjfoCYZo-E",\n "targetHandle": "target"\n}'), nodes, edges)
|
||||||
|
}).toThrowError(hasRedundantEdgeError())
|
||||||
|
})
|
||||||
|
|
||||||
|
/* check save */
|
||||||
|
test(atLeastOneStartNodeError().message, () => {
|
||||||
|
expect(() => checkSave([], [], {})).toThrowError(atLeastOneStartNodeError())
|
||||||
|
})
|
||||||
|
|
||||||
|
test(atLeastOneEndNodeError().message, () => {
|
||||||
|
expect(() => checkSave([createStartNode(uuid())], [], {})).toThrowError(atLeastOneEndNodeError())
|
||||||
|
})
|
||||||
274
service-web/client/src/pages/ai/flow/FlowChecker.tsx
Normal file
274
service-web/client/src/pages/ai/flow/FlowChecker.tsx
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
import {type Connection, type Edge, getConnectedEdges, getIncomers, getOutgoers, type Node} from '@xyflow/react'
|
||||||
|
import {clone, find, findIdx, isEqual, lpad, toStr, uuid} from 'licia'
|
||||||
|
|
||||||
|
export class CheckError extends Error {
|
||||||
|
readonly id: string
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
id: number,
|
||||||
|
message: string,
|
||||||
|
) {
|
||||||
|
super(message)
|
||||||
|
this.id = `E${lpad(toStr(id), 6, '0')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
public toString(): string {
|
||||||
|
return `${this.id}: ${this.message}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const multiStartNodeError = () => new CheckError(100, '只能存在1个开始节点')
|
||||||
|
export const multiEndNodeError = () => new CheckError(101, '只能存在1个结束节点')
|
||||||
|
|
||||||
|
const getNodeById = (id: string, nodes: Node[]) => find(nodes, (n: Node) => isEqual(n.id, id))
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export const checkAddNode: (type: string, nodes: Node[], edges: Edge[]) => void = (type, nodes, edges) => {
|
||||||
|
if (isEqual(type, 'start-node') && findIdx(nodes, (node: Node) => isEqual(type, node.type)) > -1) {
|
||||||
|
throw multiStartNodeError()
|
||||||
|
}
|
||||||
|
if (isEqual(type, 'end-node') && findIdx(nodes, (node: Node) => isEqual(type, node.type)) > -1) {
|
||||||
|
throw multiEndNodeError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sourceNodeNotFoundError = () => new CheckError(200, '连线起始节点未找到')
|
||||||
|
export const targetNodeNotFoundError = () => new CheckError(201, '连线目标节点未找到')
|
||||||
|
export const startNodeToEndNodeError = () => new CheckError(202, '开始节点不能直连结束节点')
|
||||||
|
export const nodeToSelfError = () => new CheckError(203, '节点不能直连自身')
|
||||||
|
export const hasCycleError = () => new CheckError(204, '禁止流程循环')
|
||||||
|
export const nodeNotOnlyToEndNode = () => new CheckError(206, '直连结束节点的节点不允许连接其他节点')
|
||||||
|
export const hasRedundantEdgeError = () => new CheckError(207, '禁止出现冗余边')
|
||||||
|
|
||||||
|
const hasCycle = (sourceNode: Node, targetNode: Node, nodes: Node[], edges: Edge[], visited = new Set<string>()) => {
|
||||||
|
if (visited.has(targetNode.id)) return false
|
||||||
|
visited.add(targetNode.id)
|
||||||
|
for (const outgoer of getOutgoers(targetNode, nodes, edges)) {
|
||||||
|
if (isEqual(outgoer.id, sourceNode.id)) return true
|
||||||
|
if (hasCycle(sourceNode, outgoer, nodes, edges, visited)) return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 摘自Dify的流程合法性判断 */
|
||||||
|
|
||||||
|
type ParallelInfoItem = {
|
||||||
|
parallelNodeId: string
|
||||||
|
depth: number
|
||||||
|
isBranch?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeParallelInfo = {
|
||||||
|
parallelNodeId: string
|
||||||
|
edgeHandleId: string
|
||||||
|
depth: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeHandle = {
|
||||||
|
node: Node
|
||||||
|
handle: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeStreamInfo = {
|
||||||
|
upstreamNodes: Set<string>
|
||||||
|
downstreamEdges: Set<string>
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupBy = (array: Record<string, any>[], iteratee: string) => {
|
||||||
|
const result: Record<string, any[]> = {}
|
||||||
|
for (const item of array) {
|
||||||
|
// 获取属性值并转换为字符串键
|
||||||
|
const key = item[iteratee]
|
||||||
|
if (!result[key]) {
|
||||||
|
result[key] = []
|
||||||
|
}
|
||||||
|
result[key].push(item)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export const getParallelInfo = (nodes: Node[], edges: Edge[], parentNodeId?: string) => {
|
||||||
|
// 等到有子图的时候再考虑
|
||||||
|
/*if (parentNodeId) {
|
||||||
|
const parentNode = nodes.find(node => node.id === parentNodeId)
|
||||||
|
if (!parentNode)
|
||||||
|
throw new Error('Parent node not found')
|
||||||
|
|
||||||
|
startNode = nodes.find(node => node.id === (parentNode.data as (IterationNodeType | LoopNodeType)).start_node_id)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
startNode = nodes.find(node => isEqual(node.type, 'start_node'))
|
||||||
|
}*/
|
||||||
|
let startNode = nodes.find(node => isEqual(node.type, 'start-node'))
|
||||||
|
if (!startNode)
|
||||||
|
throw new Error('Start node not found')
|
||||||
|
|
||||||
|
const parallelList = [] as ParallelInfoItem[]
|
||||||
|
const nextNodeHandles = [{node: startNode, handle: 'source'}]
|
||||||
|
let hasAbnormalEdges = false
|
||||||
|
|
||||||
|
const traverse = (firstNodeHandle: NodeHandle) => {
|
||||||
|
const nodeEdgesSet = {} as Record<string, Set<string>>
|
||||||
|
const totalEdgesSet = new Set<string>()
|
||||||
|
const nextHandles = [firstNodeHandle]
|
||||||
|
const streamInfo = {} as Record<string, NodeStreamInfo>
|
||||||
|
const parallelListItem = {
|
||||||
|
parallelNodeId: '',
|
||||||
|
depth: 0,
|
||||||
|
} as ParallelInfoItem
|
||||||
|
const nodeParallelInfoMap = {} as Record<string, NodeParallelInfo>
|
||||||
|
nodeParallelInfoMap[firstNodeHandle.node.id] = {
|
||||||
|
parallelNodeId: '',
|
||||||
|
edgeHandleId: '',
|
||||||
|
depth: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
while (nextHandles.length) {
|
||||||
|
const currentNodeHandle = nextHandles.shift()!
|
||||||
|
const {node: currentNode, handle: currentHandle = 'source'} = currentNodeHandle
|
||||||
|
const currentNodeHandleKey = currentNode.id
|
||||||
|
const connectedEdges = edges.filter(edge => edge.source === currentNode.id && edge.sourceHandle === currentHandle)
|
||||||
|
const connectedEdgesLength = connectedEdges.length
|
||||||
|
const outgoers = nodes.filter(node => connectedEdges.some(edge => edge.target === node.id))
|
||||||
|
const incomers = getIncomers(currentNode, nodes, edges)
|
||||||
|
|
||||||
|
if (!streamInfo[currentNodeHandleKey]) {
|
||||||
|
streamInfo[currentNodeHandleKey] = {
|
||||||
|
upstreamNodes: new Set<string>(),
|
||||||
|
downstreamEdges: new Set<string>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeEdgesSet[currentNodeHandleKey]?.size > 0 && incomers.length > 1) {
|
||||||
|
const newSet = new Set<string>()
|
||||||
|
for (const item of totalEdgesSet) {
|
||||||
|
if (!streamInfo[currentNodeHandleKey].downstreamEdges.has(item))
|
||||||
|
newSet.add(item)
|
||||||
|
}
|
||||||
|
if (isEqual(nodeEdgesSet[currentNodeHandleKey], newSet)) {
|
||||||
|
parallelListItem.depth = nodeParallelInfoMap[currentNode.id].depth
|
||||||
|
nextNodeHandles.push({node: currentNode, handle: currentHandle})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeParallelInfoMap[currentNode.id].depth > parallelListItem.depth)
|
||||||
|
parallelListItem.depth = nodeParallelInfoMap[currentNode.id].depth
|
||||||
|
|
||||||
|
outgoers.forEach((outgoer) => {
|
||||||
|
const outgoerConnectedEdges = getConnectedEdges([outgoer], edges).filter(edge => edge.source === outgoer.id)
|
||||||
|
const sourceEdgesGroup = groupBy(outgoerConnectedEdges, 'sourceHandle')
|
||||||
|
const incomers = getIncomers(outgoer, nodes, edges)
|
||||||
|
|
||||||
|
if (outgoers.length > 1 && incomers.length > 1)
|
||||||
|
hasAbnormalEdges = true
|
||||||
|
|
||||||
|
Object.keys(sourceEdgesGroup).forEach((sourceHandle) => {
|
||||||
|
nextHandles.push({node: outgoer, handle: sourceHandle})
|
||||||
|
})
|
||||||
|
if (!outgoerConnectedEdges.length)
|
||||||
|
nextHandles.push({node: outgoer, handle: 'source'})
|
||||||
|
|
||||||
|
const outgoerKey = outgoer.id
|
||||||
|
if (!nodeEdgesSet[outgoerKey])
|
||||||
|
nodeEdgesSet[outgoerKey] = new Set<string>()
|
||||||
|
|
||||||
|
if (nodeEdgesSet[currentNodeHandleKey]) {
|
||||||
|
for (const item of nodeEdgesSet[currentNodeHandleKey])
|
||||||
|
nodeEdgesSet[outgoerKey].add(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!streamInfo[outgoerKey]) {
|
||||||
|
streamInfo[outgoerKey] = {
|
||||||
|
upstreamNodes: new Set<string>(),
|
||||||
|
downstreamEdges: new Set<string>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nodeParallelInfoMap[outgoer.id]) {
|
||||||
|
nodeParallelInfoMap[outgoer.id] = {
|
||||||
|
...nodeParallelInfoMap[currentNode.id],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connectedEdgesLength > 1) {
|
||||||
|
const edge = connectedEdges.find(edge => edge.target === outgoer.id)!
|
||||||
|
nodeEdgesSet[outgoerKey].add(edge.id)
|
||||||
|
totalEdgesSet.add(edge.id)
|
||||||
|
|
||||||
|
streamInfo[currentNodeHandleKey].downstreamEdges.add(edge.id)
|
||||||
|
streamInfo[outgoerKey].upstreamNodes.add(currentNodeHandleKey)
|
||||||
|
|
||||||
|
for (const item of streamInfo[currentNodeHandleKey].upstreamNodes)
|
||||||
|
streamInfo[item].downstreamEdges.add(edge.id)
|
||||||
|
|
||||||
|
if (!parallelListItem.parallelNodeId)
|
||||||
|
parallelListItem.parallelNodeId = currentNode.id
|
||||||
|
|
||||||
|
const prevDepth = nodeParallelInfoMap[currentNode.id].depth + 1
|
||||||
|
const currentDepth = nodeParallelInfoMap[outgoer.id].depth
|
||||||
|
|
||||||
|
nodeParallelInfoMap[outgoer.id].depth = Math.max(prevDepth, currentDepth)
|
||||||
|
} else {
|
||||||
|
for (const item of streamInfo[currentNodeHandleKey].upstreamNodes)
|
||||||
|
streamInfo[outgoerKey].upstreamNodes.add(item)
|
||||||
|
|
||||||
|
nodeParallelInfoMap[outgoer.id].depth = nodeParallelInfoMap[currentNode.id].depth
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
parallelList.push(parallelListItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
while (nextNodeHandles.length) {
|
||||||
|
const nodeHandle = nextNodeHandles.shift()!
|
||||||
|
traverse(nodeHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
parallelList,
|
||||||
|
hasAbnormalEdges,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const checkAddConnection: (connection: Connection, nodes: Node[], edges: Edge[]) => void = (connection, nodes, edges) => {
|
||||||
|
let sourceNode = getNodeById(connection.source, nodes)
|
||||||
|
if (!sourceNode) {
|
||||||
|
throw sourceNodeNotFoundError()
|
||||||
|
}
|
||||||
|
let targetNode = getNodeById(connection.target, nodes)
|
||||||
|
if (!targetNode) {
|
||||||
|
throw targetNodeNotFoundError()
|
||||||
|
}
|
||||||
|
// 禁止短路整个流程
|
||||||
|
if (isEqual('start-node', sourceNode.type) && isEqual('end-node', targetNode.type)) {
|
||||||
|
throw startNodeToEndNodeError()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁止流程出现环,必须是有向无环图
|
||||||
|
if (isEqual(sourceNode.id, targetNode.id)) {
|
||||||
|
throw nodeToSelfError()
|
||||||
|
} else if (hasCycle(sourceNode, targetNode, nodes, edges)) {
|
||||||
|
throw hasCycleError()
|
||||||
|
}
|
||||||
|
|
||||||
|
let newEdges = [...clone(edges), {...connection, id: uuid()}]
|
||||||
|
let {hasAbnormalEdges} = getParallelInfo(nodes, newEdges)
|
||||||
|
if (hasAbnormalEdges) {
|
||||||
|
throw hasRedundantEdgeError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const atLeastOneStartNodeError = () => new CheckError(300, '至少存在1个开始节点')
|
||||||
|
export const atLeastOneEndNodeError = () => new CheckError(301, '至少存在1个结束节点')
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export const checkSave: (nodes: Node[], edges: Edge[], data: any) => void = (nodes, edges, data) => {
|
||||||
|
if (nodes.filter(n => isEqual('start-node', n.type)).length < 1) {
|
||||||
|
throw atLeastOneStartNodeError()
|
||||||
|
}
|
||||||
|
if (nodes.filter(n => isEqual('end-node', n.type)).length < 1) {
|
||||||
|
throw atLeastOneEndNodeError()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +1,20 @@
|
|||||||
import {PlusCircleFilled, SaveFilled} from '@ant-design/icons'
|
import {PlusCircleFilled, SaveFilled} from '@ant-design/icons'
|
||||||
import {
|
import {Background, BackgroundVariant, Controls, MiniMap, type NodeProps, ReactFlow} from '@xyflow/react'
|
||||||
Background,
|
|
||||||
BackgroundVariant,
|
|
||||||
type Connection,
|
|
||||||
Controls,
|
|
||||||
type Edge,
|
|
||||||
getOutgoers,
|
|
||||||
MiniMap,
|
|
||||||
type Node,
|
|
||||||
type NodeProps,
|
|
||||||
ReactFlow,
|
|
||||||
} from '@xyflow/react'
|
|
||||||
import {useMount} from 'ahooks'
|
import {useMount} from 'ahooks'
|
||||||
import type {Schema} from 'amis'
|
import type {Schema} from 'amis'
|
||||||
import {Button, Drawer, Dropdown, message, Space} from 'antd'
|
import {Button, Drawer, Dropdown, message, Space} from 'antd'
|
||||||
import {arrToMap, find, findIdx, isEqual, isNil, randomId} from 'licia'
|
import {arrToMap, find, isEqual, isNil, randomId} from 'licia'
|
||||||
import {type JSX, useState} from 'react'
|
import {type JSX, type MemoExoticComponent, useState} from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import '@xyflow/react/dist/style.css'
|
import '@xyflow/react/dist/style.css'
|
||||||
import {amisRender, commonInfo, horizontalFormOptions} from '../../../util/amis.tsx'
|
import {amisRender, commonInfo, horizontalFormOptions} from '../../../util/amis.tsx'
|
||||||
import {buildEL} from './ElParser.tsx'
|
import {checkAddConnection, checkAddNode, checkSave} from './FlowChecker.tsx'
|
||||||
import CodeNode from './node/CodeNode.tsx'
|
import CodeNode from './node/CodeNode.tsx'
|
||||||
import EndNode from './node/EndNode.tsx'
|
import EndNode from './node/EndNode.tsx'
|
||||||
import KnowledgeNode from './node/KnowledgeNode.tsx'
|
import KnowledgeNode from './node/KnowledgeNode.tsx'
|
||||||
import LlmNode from './node/LlmNode.tsx'
|
import LlmNode from './node/LlmNode.tsx'
|
||||||
import StartNode from './node/StartNode.tsx'
|
import StartNode from './node/StartNode.tsx'
|
||||||
|
import SwitchNode from './node/SwitchNode.tsx'
|
||||||
import {useDataStore} from './store/DataStore.ts'
|
import {useDataStore} from './store/DataStore.ts'
|
||||||
import {useFlowStore} from './store/FlowStore.ts'
|
import {useFlowStore} from './store/FlowStore.ts'
|
||||||
|
|
||||||
@@ -70,7 +60,7 @@ function FlowEditor() {
|
|||||||
const [nodeDef] = useState<{
|
const [nodeDef] = useState<{
|
||||||
key: string,
|
key: string,
|
||||||
name: string,
|
name: string,
|
||||||
component: (props: NodeProps) => JSX.Element
|
component: MemoExoticComponent<(props: NodeProps) => JSX.Element>
|
||||||
}[]>([
|
}[]>([
|
||||||
{
|
{
|
||||||
key: 'start-node',
|
key: 'start-node',
|
||||||
@@ -97,13 +87,17 @@ function FlowEditor() {
|
|||||||
name: '代码执行',
|
name: '代码执行',
|
||||||
component: CodeNode,
|
component: CodeNode,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'switch-node',
|
||||||
|
name: '条件分支',
|
||||||
|
component: SwitchNode,
|
||||||
|
},
|
||||||
])
|
])
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
const {data, setData, getDataById, setDataById} = useDataStore()
|
const {data, setData, getDataById, setDataById} = useDataStore()
|
||||||
const {
|
const {
|
||||||
nodes,
|
nodes,
|
||||||
getNodeById,
|
|
||||||
addNode,
|
addNode,
|
||||||
removeNode,
|
removeNode,
|
||||||
setNodes,
|
setNodes,
|
||||||
@@ -184,79 +178,6 @@ function FlowEditor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkNode = (type: string) => {
|
|
||||||
if (isEqual(type, 'start-node') && findIdx(nodes, (node: Node) => isEqual(type, node.type)) > -1) {
|
|
||||||
throw new Error('只能存在1个开始节点')
|
|
||||||
}
|
|
||||||
if (isEqual(type, 'end-node') && findIdx(nodes, (node: Node) => isEqual(type, node.type)) > -1) {
|
|
||||||
throw new Error('只能存在1个结束节点')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkConnection = (connection: Connection) => {
|
|
||||||
let sourceNode = getNodeById(connection.source)
|
|
||||||
if (!sourceNode) {
|
|
||||||
throw new Error('连线起始节点未找到')
|
|
||||||
}
|
|
||||||
let targetNode = getNodeById(connection.target)
|
|
||||||
if (!targetNode) {
|
|
||||||
throw new Error('连线目标节点未找到')
|
|
||||||
}
|
|
||||||
// 禁止短路整个流程
|
|
||||||
if (isEqual('start-node', sourceNode.type) && isEqual('end-node', targetNode.type)) {
|
|
||||||
throw new Error('开始节点不能直连结束节点')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 禁止流程出现环,必须是有向无环图
|
|
||||||
const hasCycle = (node: Node, visited = new Set<string>()) => {
|
|
||||||
if (visited.has(node.id)) return false
|
|
||||||
visited.add(node.id)
|
|
||||||
for (const outgoer of getOutgoers(node, nodes, edges)) {
|
|
||||||
if (isEqual(outgoer.id, sourceNode?.id)) return true
|
|
||||||
if (hasCycle(outgoer, visited)) return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isEqual(sourceNode.id, targetNode.id)) {
|
|
||||||
throw new Error('节点不能直连自身')
|
|
||||||
} else if (hasCycle(targetNode)) {
|
|
||||||
throw new Error('禁止流程循环')
|
|
||||||
}
|
|
||||||
|
|
||||||
/*const hasRedundant = (source: Node, target: Node) => {
|
|
||||||
const visited = new Set<string>()
|
|
||||||
const queue = new Queue<Node>()
|
|
||||||
queue.enqueue(source)
|
|
||||||
visited.add(source.id)
|
|
||||||
while (queue.size > 0) {
|
|
||||||
const current = queue.dequeue()!
|
|
||||||
console.log(current.id)
|
|
||||||
for (const incomer of getIncomers(current, nodes, edges)) {
|
|
||||||
if (isEqual(incomer.id, target.id)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (!visited.has(incomer.id)) {
|
|
||||||
visited.add(incomer.id)
|
|
||||||
queue.enqueue(incomer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (hasRedundant(sourceNode, targetNode)) {
|
|
||||||
throw new Error('出现冗余边')
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const checkSave = (nodes: Node[], edges: Edge[], data: any) => {
|
|
||||||
if (nodes.filter(n => isEqual('start-node', n.type)).length < 1) {
|
|
||||||
throw new Error('至少存在1个开始节点')
|
|
||||||
}
|
|
||||||
if (nodes.filter(n => isEqual('end-node', n.type)).length < 1) {
|
|
||||||
throw new Error('至少存在1个结束节点')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 用于透传node操作到主流程
|
// 用于透传node操作到主流程
|
||||||
const initialNodeHandlers = {
|
const initialNodeHandlers = {
|
||||||
getDataById,
|
getDataById,
|
||||||
@@ -267,11 +188,12 @@ function FlowEditor() {
|
|||||||
|
|
||||||
useMount(() => {
|
useMount(() => {
|
||||||
// language=JSON
|
// language=JSON
|
||||||
let initialData = JSON.parse('{\n "nodes": [\n {\n "id": "ldoKAzHnKF",\n "type": "llm-node",\n "position": {\n "x": 207,\n "y": -38\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 105\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "1eJtMoJWs6",\n "type": "llm-node",\n "position": {\n "x": 207,\n "y": 172.5\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 105\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "7e5vQLDGTl",\n "type": "start-node",\n "position": {\n "x": -162.3520537805597,\n "y": 67.84901301708827\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 105\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "Wyqg_bXILg",\n "type": "knowledge-node",\n "position": {\n "x": 560.402133595296,\n "y": -38.892263766178665\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "7DaF-0G-yv",\n "type": "llm-node",\n "position": {\n "x": 634.9924233956513,\n "y": 172.01821084172227\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "mymIbw_W6k",\n "type": "end-node",\n "position": {\n "x": 953.9302142661356,\n "y": 172.0182108417223\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n }\n ],\n "edges": [\n {\n "source": "7e5vQLDGTl",\n "target": "ldoKAzHnKF",\n "id": "xy-edge__7e5vQLDGTl-ldoKAzHnKF"\n },\n {\n "source": "ldoKAzHnKF",\n "target": "Wyqg_bXILg",\n "id": "xy-edge__ldoKAzHnKF-Wyqg_bXILg"\n },\n {\n "source": "7e5vQLDGTl",\n "target": "1eJtMoJWs6",\n "id": "xy-edge__7e5vQLDGTl-1eJtMoJWs6"\n },\n {\n "source": "Wyqg_bXILg",\n "target": "7DaF-0G-yv",\n "id": "xy-edge__Wyqg_bXILg-7DaF-0G-yv"\n },\n {\n "source": "1eJtMoJWs6",\n "target": "7DaF-0G-yv",\n "id": "xy-edge__1eJtMoJWs6-7DaF-0G-yv"\n },\n {\n "source": "7DaF-0G-yv",\n "target": "mymIbw_W6k",\n "id": "xy-edge__7DaF-0G-yv-mymIbw_W6k"\n }\n ],\n "data": {\n "7e5vQLDGTl": {\n "inputs": {\n "question": {\n "type": "text",\n "description": "问题"\n }\n }\n },\n "ldoKAzHnKF": {\n "model": "qwen3",\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "systemPrompt": "你是个聪明人"\n },\n "1eJtMoJWs6": {\n "model": "deepseek",\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "systemPrompt": "你也是个好人"\n }\n }\n}')
|
let initialData = JSON.parse('{"nodes":[{"id":"TCxPixrdkI","type":"start-node","position":{"x":-256,"y":109.5},"data":{},"measured":{"width":256,"height":83},"selected":false,"dragging":false},{"id":"tGs78_ietp","type":"llm-node","position":{"x":108,"y":-2.5},"data":{},"measured":{"width":256,"height":105},"selected":false,"dragging":false},{"id":"OeZdaU7LpY","type":"llm-node","position":{"x":111,"y":196},"data":{},"measured":{"width":256,"height":105},"selected":false,"dragging":false},{"id":"LjfoCYZo-E","type":"knowledge-node","position":{"x":497.62196259607214,"y":-10.792497317791003},"data":{},"measured":{"width":256,"height":75},"selected":false,"dragging":false},{"id":"sQM_22GYB5","type":"end-node","position":{"x":874.3164534765615,"y":151.70316541496913},"data":{},"measured":{"width":256,"height":75},"selected":false,"dragging":false},{"id":"KpMH_xc3ZZ","type":"llm-node","position":{"x":529.6286840434341,"y":150.4721376669937},"data":{},"measured":{"width":256,"height":75},"selected":false,"dragging":false},{"id":"pOrR6EMVbe","type":"switch-node","position":{"x":110.33793030183864,"y":373.9551529987239},"data":{},"measured":{"width":256,"height":157},"selected":false,"dragging":false}],"edges":[{"source":"TCxPixrdkI","sourceHandle":"source","target":"tGs78_ietp","targetHandle":"target","id":"xy-edge__TCxPixrdkIsource-tGs78_ietptarget"},{"source":"TCxPixrdkI","sourceHandle":"source","target":"OeZdaU7LpY","targetHandle":"target","id":"xy-edge__TCxPixrdkIsource-OeZdaU7LpYtarget"},{"source":"tGs78_ietp","sourceHandle":"source","target":"LjfoCYZo-E","targetHandle":"target","id":"xy-edge__tGs78_ietpsource-LjfoCYZo-Etarget"},{"source":"LjfoCYZo-E","sourceHandle":"source","target":"KpMH_xc3ZZ","targetHandle":"target","id":"xy-edge__LjfoCYZo-Esource-KpMH_xc3ZZtarget"},{"source":"OeZdaU7LpY","sourceHandle":"source","target":"KpMH_xc3ZZ","targetHandle":"target","id":"xy-edge__OeZdaU7LpYsource-KpMH_xc3ZZtarget"},{"source":"KpMH_xc3ZZ","sourceHandle":"source","target":"sQM_22GYB5","targetHandle":"target","id":"xy-edge__KpMH_xc3ZZsource-sQM_22GYB5target"},{"source":"TCxPixrdkI","sourceHandle":"source","target":"pOrR6EMVbe","id":"xy-edge__TCxPixrdkIsource-pOrR6EMVbe"},{"source":"pOrR6EMVbe","sourceHandle":"3","target":"sQM_22GYB5","targetHandle":"target","id":"xy-edge__pOrR6EMVbe3-sQM_22GYB5target"},{"source":"pOrR6EMVbe","sourceHandle":"1","target":"KpMH_xc3ZZ","targetHandle":"target","id":"xy-edge__pOrR6EMVbe1-KpMH_xc3ZZtarget"}],"data":{"tGs78_ietp":{"model":"qwen3","outputs":{"text":{"type":"string"}},"systemPrompt":"你是个聪明人"},"OeZdaU7LpY":{"model":"qwen3","outputs":{"text":{"type":"string"}},"systemPrompt":"你也是个聪明人"}}}')
|
||||||
let initialNodes = initialData['nodes'] ?? []
|
// let initialData: any = {}
|
||||||
let initialEdges = initialData['edges'] ?? []
|
let initialNodes = initialData?.nodes ?? []
|
||||||
|
let initialEdges = initialData?.edges ?? []
|
||||||
|
|
||||||
let initialNodeData = initialData['data'] ?? {}
|
let initialNodeData = initialData?.data ?? {}
|
||||||
setData(initialNodeData)
|
setData(initialNodeData)
|
||||||
|
|
||||||
for (let node of initialNodes) {
|
for (let node of initialNodes) {
|
||||||
@@ -290,7 +212,10 @@ function FlowEditor() {
|
|||||||
items: nodeDef.map(def => ({key: def.key, label: def.name})),
|
items: nodeDef.map(def => ({key: def.key, label: def.name})),
|
||||||
onClick: ({key}) => {
|
onClick: ({key}) => {
|
||||||
try {
|
try {
|
||||||
checkNode(key)
|
if (commonInfo.debug) {
|
||||||
|
console.info('Add', key, JSON.stringify({nodes, edges, data}))
|
||||||
|
}
|
||||||
|
checkAddNode(key, nodes, edges)
|
||||||
addNode({
|
addNode({
|
||||||
id: randomId(10),
|
id: randomId(10),
|
||||||
type: key,
|
type: key,
|
||||||
@@ -299,7 +224,7 @@ function FlowEditor() {
|
|||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
messageApi.error(e.message)
|
messageApi.error(e.toString())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
@@ -311,13 +236,15 @@ function FlowEditor() {
|
|||||||
</Dropdown>
|
</Dropdown>
|
||||||
<Button type="primary" onClick={() => {
|
<Button type="primary" onClick={() => {
|
||||||
try {
|
try {
|
||||||
|
if (commonInfo.debug) {
|
||||||
|
console.info('Save', JSON.stringify({nodes, edges, data}))
|
||||||
|
}
|
||||||
checkSave(nodes, edges, data)
|
checkSave(nodes, edges, data)
|
||||||
let saveData = {nodes, edges, data}
|
// let saveData = {nodes, edges, data}
|
||||||
console.log(JSON.stringify(saveData, null, 2))
|
// console.log(buildEL(nodes, edges))
|
||||||
console.log(buildEL(nodes, edges))
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
messageApi.error(e.message)
|
messageApi.error(e.toString())
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<SaveFilled/>
|
<SaveFilled/>
|
||||||
@@ -341,11 +268,14 @@ function FlowEditor() {
|
|||||||
onEdgesChange={onEdgesChange}
|
onEdgesChange={onEdgesChange}
|
||||||
onConnect={(connection) => {
|
onConnect={(connection) => {
|
||||||
try {
|
try {
|
||||||
checkConnection(connection)
|
if (commonInfo.debug) {
|
||||||
|
console.info('Connection', JSON.stringify(connection), JSON.stringify({nodes, edges, data}))
|
||||||
|
}
|
||||||
|
checkAddConnection(connection, nodes, edges)
|
||||||
onConnect(connection)
|
onConnect(connection)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
messageApi.error(e.message)
|
messageApi.error(e.toString())
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
@@ -187,9 +187,9 @@ const AmisNode: (props: AmisNodeProps) => JSX.Element = ({
|
|||||||
{isNil(handlers)
|
{isNil(handlers)
|
||||||
? <>
|
? <>
|
||||||
{isEqual(type, 'start') || isEqual(type, 'normal')
|
{isEqual(type, 'start') || isEqual(type, 'normal')
|
||||||
? <Handle type="source" position={Position.Right}/> : undefined}
|
? <Handle type="source" position={Position.Right} id="source"/> : undefined}
|
||||||
{isEqual(type, 'end') || isEqual(type, 'normal')
|
{isEqual(type, 'end') || isEqual(type, 'normal')
|
||||||
? <Handle type="target" position={Position.Left}/> : undefined}
|
? <Handle type="target" position={Position.Left} id="target"/> : undefined}
|
||||||
</>
|
</>
|
||||||
: handlers?.(nodeData)}
|
: handlers?.(nodeData)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type {NodeProps} from '@xyflow/react'
|
import type {NodeProps} from '@xyflow/react'
|
||||||
import AmisNode, {inputsFormColumns, outputsFormColumns} from './AmisNode.tsx'
|
import AmisNode, {inputsFormColumns, outputsFormColumns} from './AmisNode.tsx'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
const CodeNode = (props: NodeProps) => AmisNode({
|
const CodeNode = (props: NodeProps) => AmisNode({
|
||||||
nodeProps: props,
|
nodeProps: props,
|
||||||
@@ -48,4 +49,4 @@ const CodeNode = (props: NodeProps) => AmisNode({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
export default CodeNode
|
export default React.memo(CodeNode)
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import type {NodeProps} from '@xyflow/react'
|
import type {NodeProps} from '@xyflow/react'
|
||||||
import AmisNode, {outputsFormColumns} from './AmisNode.tsx'
|
import AmisNode, {outputsFormColumns} from './AmisNode.tsx'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
const EndNode = (props: NodeProps) => AmisNode({
|
const EndNode = (props: NodeProps) => AmisNode({
|
||||||
nodeProps: props,
|
nodeProps: props,
|
||||||
@@ -9,4 +10,4 @@ const EndNode = (props: NodeProps) => AmisNode({
|
|||||||
columnSchema: outputsFormColumns(true),
|
columnSchema: outputsFormColumns(true),
|
||||||
})
|
})
|
||||||
|
|
||||||
export default EndNode
|
export default React.memo(EndNode)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import type {NodeProps} from '@xyflow/react'
|
import type {NodeProps} from '@xyflow/react'
|
||||||
import {commonInfo} from '../../../../util/amis.tsx'
|
import {commonInfo} from '../../../../util/amis.tsx'
|
||||||
import AmisNode, {inputsFormColumns, outputsFormColumns} from './AmisNode.tsx'
|
import AmisNode, {inputsFormColumns, outputsFormColumns} from './AmisNode.tsx'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
const KnowledgeNode = (props: NodeProps) => AmisNode({
|
const KnowledgeNode = (props: NodeProps) => AmisNode({
|
||||||
nodeProps: props,
|
nodeProps: props,
|
||||||
@@ -62,4 +63,4 @@ const KnowledgeNode = (props: NodeProps) => AmisNode({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
export default KnowledgeNode
|
export default React.memo(KnowledgeNode)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import type {NodeProps} from '@xyflow/react'
|
import type {NodeProps} from '@xyflow/react'
|
||||||
import {Tag} from 'antd'
|
import {Tag} from 'antd'
|
||||||
import AmisNode, {inputsFormColumns, outputsFormColumns} from './AmisNode.tsx'
|
import AmisNode, {inputsFormColumns, outputsFormColumns} from './AmisNode.tsx'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
const modelMap: Record<string, string> = {
|
const modelMap: Record<string, string> = {
|
||||||
qwen3: 'Qwen3',
|
qwen3: 'Qwen3',
|
||||||
@@ -47,4 +48,4 @@ const LlmNode = (props: NodeProps) => AmisNode({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
export default LlmNode
|
export default React.memo(LlmNode)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import type {NodeProps} from '@xyflow/react'
|
import type {NodeProps} from '@xyflow/react'
|
||||||
import {Tag} from 'antd'
|
import {Tag} from 'antd'
|
||||||
import {each} from 'licia'
|
import {each} from 'licia'
|
||||||
import type {JSX} from 'react'
|
import React, {type JSX} from 'react'
|
||||||
import {horizontalFormOptions} from '../../../../util/amis.tsx'
|
import {horizontalFormOptions} from '../../../../util/amis.tsx'
|
||||||
import AmisNode from './AmisNode.tsx'
|
import AmisNode from './AmisNode.tsx'
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ const StartNode = (props: NodeProps) => AmisNode({
|
|||||||
defaultNodeDescription: '定义输入变量',
|
defaultNodeDescription: '定义输入变量',
|
||||||
extraNodeDescription: nodeData => {
|
extraNodeDescription: nodeData => {
|
||||||
const variables: JSX.Element[] = []
|
const variables: JSX.Element[] = []
|
||||||
const inputs = (nodeData['inputs'] ?? {}) as Record<string, { type: string, description: string }>
|
const inputs = (nodeData?.inputs ?? {}) as Record<string, { type: string, description: string }>
|
||||||
each(inputs, (value, key) => {
|
each(inputs, (value, key) => {
|
||||||
variables.push(
|
variables.push(
|
||||||
<div className="mt-1 flex justify-between" key={key}>
|
<div className="mt-1 flex justify-between" key={key}>
|
||||||
@@ -65,4 +65,4 @@ const StartNode = (props: NodeProps) => AmisNode({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
export default StartNode
|
export default React.memo(StartNode)
|
||||||
53
service-web/client/src/pages/ai/flow/node/SwitchNode.tsx
Normal file
53
service-web/client/src/pages/ai/flow/node/SwitchNode.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import {Handle, type NodeProps, Position} from '@xyflow/react'
|
||||||
|
import AmisNode from './AmisNode.tsx'
|
||||||
|
import {Tag} from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const cases = [
|
||||||
|
{
|
||||||
|
index: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const SwitchNode = (props: NodeProps) => AmisNode({
|
||||||
|
nodeProps: props,
|
||||||
|
type: 'normal',
|
||||||
|
defaultNodeName: '分支节点',
|
||||||
|
defaultNodeDescription: '根据不同的情况前往不同的分支',
|
||||||
|
columnSchema: [],
|
||||||
|
extraNodeDescription: nodeData => {
|
||||||
|
return (
|
||||||
|
<div className="mt-2">
|
||||||
|
{cases.map(item => (
|
||||||
|
<div key={item.index} className="mt-1">
|
||||||
|
<Tag className="m-0" color="blue">分支 {item.index}</Tag>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
handlers: nodeData => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Handle type="target" position={Position.Left}/>
|
||||||
|
{cases.map((item, index) => (
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
position={Position.Right}
|
||||||
|
key={item.index}
|
||||||
|
id={`${item.index}`}
|
||||||
|
style={{top: 85 + (25 * index)}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default React.memo(SwitchNode)
|
||||||
Reference in New Issue
Block a user