feat(web): 增加分支节点,增加流程检查的测试
This commit is contained in:
@@ -6,7 +6,8 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"test": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^6.0.0",
|
||||
@@ -41,6 +42,7 @@
|
||||
"sass": "^1.89.2",
|
||||
"typescript": "~5.8.3",
|
||||
"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:
|
||||
specifier: ^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:
|
||||
|
||||
@@ -499,6 +502,9 @@ packages:
|
||||
resolution: {integrity: sha512-CZFX7UZVN9VopGbjTx4UXaXsi9ewoM1buL0kY7j1ftYdSs7p2spv9opxFjHlQ/QGTgh4UqufYqJJ0WKLml7b6w==}
|
||||
engines: {node: '>=4.0'}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.0':
|
||||
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
|
||||
|
||||
'@kurkle/color@0.3.4':
|
||||
resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==}
|
||||
|
||||
@@ -865,6 +871,9 @@ packages:
|
||||
'@swc/types@0.1.23':
|
||||
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':
|
||||
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
|
||||
|
||||
@@ -961,6 +970,9 @@ packages:
|
||||
'@types/debug@4.1.12':
|
||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||
|
||||
'@types/deep-eql@4.0.2':
|
||||
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||
|
||||
'@types/estree-jsx@1.0.5':
|
||||
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
|
||||
|
||||
@@ -1033,6 +1045,35 @@ packages:
|
||||
peerDependencies:
|
||||
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':
|
||||
resolution: {integrity: sha512-uvIPQIZdf8tt0mDWvhkEpg/7t5E/e/KE4RWjNczAEhEYA+uvLc+4A5kIPJqCjJJbVHfMiAojT5JOB5mB7/EgFw==}
|
||||
peerDependencies:
|
||||
@@ -1162,6 +1203,10 @@ packages:
|
||||
assert@2.0.0:
|
||||
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:
|
||||
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
|
||||
|
||||
@@ -1234,6 +1279,10 @@ packages:
|
||||
resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==}
|
||||
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:
|
||||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1260,6 +1309,10 @@ packages:
|
||||
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
chai@5.2.0:
|
||||
resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
chainsaw@0.1.0:
|
||||
resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==}
|
||||
|
||||
@@ -1293,6 +1346,10 @@ packages:
|
||||
resolution: {integrity: sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==}
|
||||
engines: {pnpm: '>=8'}
|
||||
|
||||
check-error@2.1.1:
|
||||
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
chevrotain-allstar@0.3.1:
|
||||
resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==}
|
||||
peerDependencies:
|
||||
@@ -1616,6 +1673,10 @@ packages:
|
||||
resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
deep-eql@5.0.2:
|
||||
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
deep-is@0.1.4:
|
||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||
|
||||
@@ -1705,6 +1766,9 @@ packages:
|
||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-module-lexer@1.7.0:
|
||||
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1748,6 +1812,9 @@ packages:
|
||||
estree-util-is-identifier-name@3.0.0:
|
||||
resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
|
||||
esutils@2.0.3:
|
||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -1756,6 +1823,10 @@ packages:
|
||||
resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==}
|
||||
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:
|
||||
resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==}
|
||||
|
||||
@@ -2058,6 +2129,9 @@ packages:
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
js-tokens@9.0.1:
|
||||
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
|
||||
|
||||
jsbarcode@3.12.1:
|
||||
resolution: {integrity: sha512-QZQSqIknC2Rr/YOUyOkCBqsoiBAOTYK+7yNN3JsqfoUtJtkazxNw1dmPpxuv7VVvqW13kA3/mKiLq+s/e3o9hQ==}
|
||||
|
||||
@@ -2179,6 +2253,12 @@ packages:
|
||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||
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:
|
||||
resolution: {integrity: sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww==}
|
||||
|
||||
@@ -2518,6 +2598,10 @@ packages:
|
||||
pathe@2.0.3:
|
||||
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:
|
||||
resolution: {integrity: sha512-gzfnt1qc4yA+U46golPGYtU4WM2ssqP2MvFjKga8GEKOrEnzRPrA/9jogLLPYHiA3sGBPJ+p7BdAq+ytmw3jEg==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3105,6 +3189,9 @@ packages:
|
||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
siginfo@2.0.0:
|
||||
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||
|
||||
signal-exit@3.0.7:
|
||||
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
||||
|
||||
@@ -3141,6 +3228,12 @@ packages:
|
||||
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
|
||||
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:
|
||||
resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==}
|
||||
|
||||
@@ -3167,6 +3260,9 @@ packages:
|
||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
strip-literal@3.0.0:
|
||||
resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
|
||||
|
||||
style-to-js@1.1.17:
|
||||
resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==}
|
||||
|
||||
@@ -3210,9 +3306,15 @@ packages:
|
||||
tiny-invariant@1.3.3:
|
||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||
|
||||
tinybench@2.9.0:
|
||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||
|
||||
tinycolor2@1.6.0:
|
||||
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:
|
||||
resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
|
||||
|
||||
@@ -3223,6 +3325,18 @@ packages:
|
||||
tinymce@6.8.6:
|
||||
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:
|
||||
resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
|
||||
engines: {node: '>=14.14'}
|
||||
@@ -3369,6 +3483,11 @@ packages:
|
||||
react: ^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:
|
||||
resolution: {integrity: sha512-sf4JFlG1iUPl7bLXHGOy+bKWOQUFyXzJFWa+n2S2xMMvyfM+V9R40HhpZoIF1eAjifArM1SF7fbSFIaTuUIbPA==}
|
||||
|
||||
@@ -3412,6 +3531,34 @@ packages:
|
||||
yaml:
|
||||
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:
|
||||
resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@@ -3454,6 +3601,11 @@ packages:
|
||||
engines: {node: '>= 8'}
|
||||
hasBin: true
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||
engines: {node: '>=8'}
|
||||
hasBin: true
|
||||
|
||||
wide-align@1.1.5:
|
||||
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
|
||||
|
||||
@@ -4007,6 +4159,8 @@ snapshots:
|
||||
|
||||
'@javascript-obfuscator/estraverse@5.4.0': {}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.0': {}
|
||||
|
||||
'@kurkle/color@0.3.4': {}
|
||||
|
||||
'@lightenna/react-mermaid-diagram@1.0.20(mermaid@11.7.0)(react@18.3.1)':
|
||||
@@ -4291,6 +4445,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@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-axis@3.0.6':
|
||||
@@ -4412,6 +4570,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/ms': 2.1.0
|
||||
|
||||
'@types/deep-eql@4.0.2': {}
|
||||
|
||||
'@types/estree-jsx@1.0.5':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
@@ -4476,6 +4636,48 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- '@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)':
|
||||
dependencies:
|
||||
'@xyflow/system': 0.0.63
|
||||
@@ -4800,6 +5002,8 @@ snapshots:
|
||||
object-is: 1.1.6
|
||||
util: 0.12.5
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
async@3.2.6: {}
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
@@ -4870,6 +5074,8 @@ snapshots:
|
||||
|
||||
buffers@0.1.1: {}
|
||||
|
||||
cac@6.7.14: {}
|
||||
|
||||
call-bind-apply-helpers@1.0.2:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
@@ -4906,6 +5112,14 @@ snapshots:
|
||||
adler-32: 1.3.1
|
||||
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:
|
||||
dependencies:
|
||||
traverse: 0.3.9
|
||||
@@ -4933,6 +5147,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@kurkle/color': 0.3.4
|
||||
|
||||
check-error@2.1.1: {}
|
||||
|
||||
chevrotain-allstar@0.3.1(chevrotain@11.0.3):
|
||||
dependencies:
|
||||
chevrotain: 11.0.3
|
||||
@@ -5267,6 +5483,8 @@ snapshots:
|
||||
mimic-response: 2.1.0
|
||||
optional: true
|
||||
|
||||
deep-eql@5.0.2: {}
|
||||
|
||||
deep-is@0.1.4: {}
|
||||
|
||||
define-data-property@1.1.4:
|
||||
@@ -5361,6 +5579,8 @@ snapshots:
|
||||
|
||||
es-errors@1.3.0: {}
|
||||
|
||||
es-module-lexer@1.7.0: {}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
@@ -5421,6 +5641,10 @@ snapshots:
|
||||
|
||||
estree-util-is-identifier-name@3.0.0: {}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
|
||||
esutils@2.0.3: {}
|
||||
|
||||
exceljs@4.4.0:
|
||||
@@ -5435,6 +5659,8 @@ snapshots:
|
||||
unzipper: 0.10.14
|
||||
uuid: 8.3.2
|
||||
|
||||
expect-type@1.2.1: {}
|
||||
|
||||
exsolve@1.0.5: {}
|
||||
|
||||
extend@3.0.2: {}
|
||||
@@ -5771,6 +5997,8 @@ snapshots:
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-tokens@9.0.1: {}
|
||||
|
||||
jsbarcode@3.12.1: {}
|
||||
|
||||
json2mq@0.2.0:
|
||||
@@ -5877,6 +6105,12 @@ snapshots:
|
||||
dependencies:
|
||||
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-dir@3.1.0:
|
||||
@@ -6359,6 +6593,8 @@ snapshots:
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
pathval@2.0.1: {}
|
||||
|
||||
pdfjs-dist@4.3.136:
|
||||
optionalDependencies:
|
||||
canvas: 2.11.2
|
||||
@@ -7130,6 +7366,8 @@ snapshots:
|
||||
|
||||
shebang-regex@3.0.0: {}
|
||||
|
||||
siginfo@2.0.0: {}
|
||||
|
||||
signal-exit@3.0.7:
|
||||
optional: true
|
||||
|
||||
@@ -7164,6 +7402,10 @@ snapshots:
|
||||
dependencies:
|
||||
frac: 1.1.2
|
||||
|
||||
stackback@0.0.2: {}
|
||||
|
||||
std-env@3.9.0: {}
|
||||
|
||||
string-convert@0.2.1: {}
|
||||
|
||||
string-template@1.0.0: {}
|
||||
@@ -7197,6 +7439,10 @@ snapshots:
|
||||
ansi-regex: 5.0.1
|
||||
optional: true
|
||||
|
||||
strip-literal@3.0.0:
|
||||
dependencies:
|
||||
js-tokens: 9.0.1
|
||||
|
||||
style-to-js@1.1.17:
|
||||
dependencies:
|
||||
style-to-object: 1.0.9
|
||||
@@ -7255,8 +7501,12 @@ snapshots:
|
||||
|
||||
tiny-invariant@1.3.3: {}
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
||||
tinycolor2@1.6.0: {}
|
||||
|
||||
tinyexec@0.3.2: {}
|
||||
|
||||
tinyexec@1.0.1: {}
|
||||
|
||||
tinyglobby@0.2.14:
|
||||
@@ -7266,6 +7516,12 @@ snapshots:
|
||||
|
||||
tinymce@6.8.6: {}
|
||||
|
||||
tinypool@1.1.1: {}
|
||||
|
||||
tinyrainbow@2.0.0: {}
|
||||
|
||||
tinyspy@4.0.3: {}
|
||||
|
||||
tmp@0.2.3: {}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
@@ -7418,6 +7674,27 @@ snapshots:
|
||||
react-dom: 18.3.1(react@18.3.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:
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
@@ -7435,6 +7712,47 @@ snapshots:
|
||||
fsevents: 2.3.3
|
||||
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-languageserver-protocol@3.17.5:
|
||||
@@ -7479,6 +7797,11 @@ snapshots:
|
||||
dependencies:
|
||||
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:
|
||||
dependencies:
|
||||
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 {expect, test} from 'vitest'
|
||||
import {type Connection, type Node} from '@xyflow/react'
|
||||
import {
|
||||
atLeastOneEndNodeError,
|
||||
atLeastOneStartNodeError,
|
||||
checkAddConnection,
|
||||
checkAddNode,
|
||||
checkSave,
|
||||
hasCycleError,
|
||||
hasRedundantEdgeError,
|
||||
multiEndNodeError,
|
||||
multiStartNodeError,
|
||||
nodeToSelfError,
|
||||
sourceNodeNotFoundError,
|
||||
startNodeToEndNodeError,
|
||||
targetNodeNotFoundError
|
||||
} from './FlowChecker.tsx'
|
||||
import {uuid} from 'licia'
|
||||
|
||||
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": "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}')
|
||||
// language=JSON
|
||||
checkAddConnection(JSON.parse('{\n "source": "1eJtMoJWs6",\n "sourceHandle": null,\n "target": "Wyqg_bXILg",\n "targetHandle": null\n}'), nodes, edges)
|
||||
}).toThrowError(hasRedundantEdgeError())
|
||||
})
|
||||
|
||||
/* check save */
|
||||
test(atLeastOneStartNodeError().message, () => {
|
||||
expect(() => checkSave([], [], {})).toThrowError(atLeastOneStartNodeError())
|
||||
})
|
||||
|
||||
test(atLeastOneEndNodeError().message, () => {
|
||||
expect(() => checkSave([createStartNode(uuid())], [], {})).toThrowError(atLeastOneEndNodeError())
|
||||
})
|
||||
113
service-web/client/src/pages/ai/flow/FlowChecker.tsx
Normal file
113
service-web/client/src/pages/ai/flow/FlowChecker.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import {find, findIdx, isEqual, lpad, toStr} from 'licia'
|
||||
import {type Connection, type Edge, getOutgoers, type Node} from '@xyflow/react'
|
||||
|
||||
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, '禁止出现冗余边')
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// 禁止流程出现环,必须是有向无环图
|
||||
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 nodeToSelfError()
|
||||
} else if (hasCycle(targetNode)) {
|
||||
throw hasCycleError()
|
||||
}
|
||||
|
||||
let outgoers = [targetNode, ...getOutgoers(sourceNode, nodes, edges)]
|
||||
if (outgoers.length > 1 && findIdx(outgoers, (node: Node) => isEqual(node.type, 'end-node')) > -1) {
|
||||
throw nodeNotOnlyToEndNode()
|
||||
}
|
||||
|
||||
/*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('出现冗余边')
|
||||
}*/
|
||||
}
|
||||
|
||||
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,25 +1,13 @@
|
||||
import {PlusCircleFilled, SaveFilled} from '@ant-design/icons'
|
||||
import {
|
||||
Background,
|
||||
BackgroundVariant,
|
||||
type Connection,
|
||||
Controls,
|
||||
type Edge,
|
||||
getOutgoers,
|
||||
MiniMap,
|
||||
type Node,
|
||||
type NodeProps,
|
||||
ReactFlow,
|
||||
} from '@xyflow/react'
|
||||
import {Background, BackgroundVariant, Controls, MiniMap, type NodeProps, ReactFlow,} from '@xyflow/react'
|
||||
import {useMount} from 'ahooks'
|
||||
import type {Schema} from 'amis'
|
||||
import {Button, Drawer, Dropdown, message, Space} from 'antd'
|
||||
import {arrToMap, find, findIdx, isEqual, isNil, randomId} from 'licia'
|
||||
import {type JSX, useState} from 'react'
|
||||
import {arrToMap, find, isEqual, isNil, randomId} from 'licia'
|
||||
import {type JSX, type MemoExoticComponent, useState} from 'react'
|
||||
import styled from 'styled-components'
|
||||
import '@xyflow/react/dist/style.css'
|
||||
import {amisRender, commonInfo, horizontalFormOptions} from '../../../util/amis.tsx'
|
||||
import {buildEL} from './ElParser.tsx'
|
||||
import CodeNode from './node/CodeNode.tsx'
|
||||
import EndNode from './node/EndNode.tsx'
|
||||
import KnowledgeNode from './node/KnowledgeNode.tsx'
|
||||
@@ -27,6 +15,8 @@ import LlmNode from './node/LlmNode.tsx'
|
||||
import StartNode from './node/StartNode.tsx'
|
||||
import {useDataStore} from './store/DataStore.ts'
|
||||
import {useFlowStore} from './store/FlowStore.ts'
|
||||
import SwitchNode from './node/SwitchNode.tsx'
|
||||
import {checkAddConnection, checkAddNode, checkSave} from './FlowChecker.tsx'
|
||||
|
||||
const FlowableDiv = styled.div`
|
||||
height: 100%;
|
||||
@@ -70,7 +60,7 @@ function FlowEditor() {
|
||||
const [nodeDef] = useState<{
|
||||
key: string,
|
||||
name: string,
|
||||
component: (props: NodeProps) => JSX.Element
|
||||
component: MemoExoticComponent<(props: NodeProps) => JSX.Element>
|
||||
}[]>([
|
||||
{
|
||||
key: 'start-node',
|
||||
@@ -97,13 +87,17 @@ function FlowEditor() {
|
||||
name: '代码执行',
|
||||
component: CodeNode,
|
||||
},
|
||||
{
|
||||
key: 'switch-node',
|
||||
name: '条件分支',
|
||||
component: SwitchNode,
|
||||
},
|
||||
])
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const {data, setData, getDataById, setDataById} = useDataStore()
|
||||
const {
|
||||
nodes,
|
||||
getNodeById,
|
||||
addNode,
|
||||
removeNode,
|
||||
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操作到主流程
|
||||
const initialNodeHandlers = {
|
||||
getDataById,
|
||||
@@ -268,10 +189,11 @@ function FlowEditor() {
|
||||
useMount(() => {
|
||||
// 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 initialNodes = initialData['nodes'] ?? []
|
||||
let initialEdges = initialData['edges'] ?? []
|
||||
// let initialData: any = {}
|
||||
let initialNodes = initialData?.nodes ?? []
|
||||
let initialEdges = initialData?.edges ?? []
|
||||
|
||||
let initialNodeData = initialData['data'] ?? {}
|
||||
let initialNodeData = initialData?.data ?? {}
|
||||
setData(initialNodeData)
|
||||
|
||||
for (let node of initialNodes) {
|
||||
@@ -290,7 +212,10 @@ function FlowEditor() {
|
||||
items: nodeDef.map(def => ({key: def.key, label: def.name})),
|
||||
onClick: ({key}) => {
|
||||
try {
|
||||
checkNode(key)
|
||||
if (commonInfo.debug) {
|
||||
console.info('Add', key, JSON.stringify({nodes, edges, data}))
|
||||
}
|
||||
checkAddNode(key, nodes, edges)
|
||||
addNode({
|
||||
id: randomId(10),
|
||||
type: key,
|
||||
@@ -299,7 +224,7 @@ function FlowEditor() {
|
||||
})
|
||||
} catch (e) {
|
||||
// @ts-ignore
|
||||
messageApi.error(e.message)
|
||||
messageApi.error(e.toString())
|
||||
}
|
||||
},
|
||||
}}
|
||||
@@ -311,13 +236,15 @@ function FlowEditor() {
|
||||
</Dropdown>
|
||||
<Button type="primary" onClick={() => {
|
||||
try {
|
||||
if (commonInfo.debug) {
|
||||
console.info('Save', JSON.stringify({nodes, edges, data}))
|
||||
}
|
||||
checkSave(nodes, edges, data)
|
||||
let saveData = {nodes, edges, data}
|
||||
console.log(JSON.stringify(saveData, null, 2))
|
||||
console.log(buildEL(nodes, edges))
|
||||
// let saveData = {nodes, edges, data}
|
||||
// console.log(buildEL(nodes, edges))
|
||||
} catch (e) {
|
||||
// @ts-ignore
|
||||
messageApi.error(e.message)
|
||||
messageApi.error(e.toString())
|
||||
}
|
||||
}}>
|
||||
<SaveFilled/>
|
||||
@@ -341,11 +268,14 @@ function FlowEditor() {
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={(connection) => {
|
||||
try {
|
||||
checkConnection(connection)
|
||||
if (commonInfo.debug) {
|
||||
console.info('Connection', JSON.stringify(connection), JSON.stringify({nodes, edges, data}))
|
||||
}
|
||||
checkAddConnection(connection, nodes, edges)
|
||||
onConnect(connection)
|
||||
} catch (e) {
|
||||
// @ts-ignore
|
||||
messageApi.error(e.message)
|
||||
messageApi.error(e.toString())
|
||||
}
|
||||
}}
|
||||
// @ts-ignore
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type {NodeProps} from '@xyflow/react'
|
||||
import AmisNode, {inputsFormColumns, outputsFormColumns} from './AmisNode.tsx'
|
||||
import React from 'react'
|
||||
|
||||
const CodeNode = (props: NodeProps) => AmisNode({
|
||||
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 AmisNode, {outputsFormColumns} from './AmisNode.tsx'
|
||||
import React from 'react'
|
||||
|
||||
const EndNode = (props: NodeProps) => AmisNode({
|
||||
nodeProps: props,
|
||||
@@ -9,4 +10,4 @@ const EndNode = (props: NodeProps) => AmisNode({
|
||||
columnSchema: outputsFormColumns(true),
|
||||
})
|
||||
|
||||
export default EndNode
|
||||
export default React.memo(EndNode)
|
||||
@@ -1,6 +1,7 @@
|
||||
import type {NodeProps} from '@xyflow/react'
|
||||
import {commonInfo} from '../../../../util/amis.tsx'
|
||||
import AmisNode, {inputsFormColumns, outputsFormColumns} from './AmisNode.tsx'
|
||||
import React from 'react'
|
||||
|
||||
const KnowledgeNode = (props: NodeProps) => AmisNode({
|
||||
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 {Tag} from 'antd'
|
||||
import AmisNode, {inputsFormColumns, outputsFormColumns} from './AmisNode.tsx'
|
||||
import React from 'react'
|
||||
|
||||
const modelMap: Record<string, string> = {
|
||||
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 {Tag} from 'antd'
|
||||
import {each} from 'licia'
|
||||
import type {JSX} from 'react'
|
||||
import React, {type JSX} from 'react'
|
||||
import {horizontalFormOptions} from '../../../../util/amis.tsx'
|
||||
import AmisNode from './AmisNode.tsx'
|
||||
|
||||
@@ -18,7 +18,7 @@ const StartNode = (props: NodeProps) => AmisNode({
|
||||
defaultNodeDescription: '定义输入变量',
|
||||
extraNodeDescription: nodeData => {
|
||||
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) => {
|
||||
variables.push(
|
||||
<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