mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-26 01:34:07 +00:00
[Manager] Compatibility Detection Logic (#4348)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
192
package-lock.json
generated
192
package-lock.json
generated
@@ -445,6 +445,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.5.0.tgz",
|
||||
"integrity": "sha512-dKnk2xlAyC7rvTkpkHmu+Qy/2Zc3Vm/l8PtNyIOGDBtXPY3kThfU4ORNEp3V7SXw5XSOb+tOJaUYpfquPzL/Tg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"package-manager-detector": "^0.2.5",
|
||||
"tinyexec": "^0.3.1"
|
||||
@@ -566,6 +567,7 @@
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -619,6 +621,7 @@
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -649,6 +652,7 @@
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -908,9 +912,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.27.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
|
||||
"integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
|
||||
"version": "7.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz",
|
||||
"integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -2333,6 +2337,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz",
|
||||
"integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@antfu/install-pkg": "^1.0.0",
|
||||
"@antfu/utils": "^8.1.0",
|
||||
@@ -2349,6 +2354,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
|
||||
"integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"package-manager-detector": "^1.3.0",
|
||||
"tinyexec": "^1.0.1"
|
||||
@@ -2362,6 +2368,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz",
|
||||
"integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
@@ -2370,13 +2377,15 @@
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz",
|
||||
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@iconify/utils/node_modules/debug": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
@@ -2394,6 +2403,7 @@
|
||||
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz",
|
||||
"integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mlly": "^1.7.4",
|
||||
"pkg-types": "^2.0.1",
|
||||
@@ -2410,19 +2420,22 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz",
|
||||
"integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@iconify/utils/node_modules/pathe": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
||||
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@iconify/utils/node_modules/pkg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"confbox": "^0.2.2",
|
||||
"exsolve": "^1.0.7",
|
||||
@@ -2433,7 +2446,8 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz",
|
||||
"integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@inkjs/ui": {
|
||||
"version": "1.0.0",
|
||||
@@ -2542,6 +2556,18 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/eslint-plugin-vue-i18n/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/eslint-plugin-vue-i18n/node_modules/synckit": {
|
||||
"version": "0.9.3",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.3.tgz",
|
||||
@@ -4635,7 +4661,8 @@
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz",
|
||||
"integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/stats.js": {
|
||||
"version": "0.17.3",
|
||||
@@ -6089,15 +6116,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/broker-factory": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.7.tgz",
|
||||
"integrity": "sha512-RxbMXWq/Qvw9aLZMvuooMtVTm2/SV9JEpxpBbMuFhYAnDaZxctbJ+1b9ucHxADk/eQNqDijvWQjLVARqExAeyg==",
|
||||
"version": "3.1.8",
|
||||
"resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.8.tgz",
|
||||
"integrity": "sha512-xmVnYN0FZtynhPUmAnN+/MFRdbDi3syCuxWV7o7s78FcIN0pjDtn9mUrVqEgdjQkbfojRhlPWbYbXJkMCyddrg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"fast-unique-numbers": "^9.0.22",
|
||||
"tslib": "^2.8.1",
|
||||
"worker-factory": "^7.0.43"
|
||||
"worker-factory": "^7.0.44"
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
@@ -6667,7 +6694,8 @@
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
|
||||
"integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/config-chain": {
|
||||
"version": "1.1.13",
|
||||
@@ -7841,6 +7869,18 @@
|
||||
"eslint": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-compat-utils/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-prettier": {
|
||||
"version": "10.1.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz",
|
||||
@@ -8389,7 +8429,8 @@
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz",
|
||||
"integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
@@ -8412,53 +8453,53 @@
|
||||
}
|
||||
},
|
||||
"node_modules/extendable-media-recorder": {
|
||||
"version": "9.2.27",
|
||||
"resolved": "https://registry.npmjs.org/extendable-media-recorder/-/extendable-media-recorder-9.2.27.tgz",
|
||||
"integrity": "sha512-2X+Ixi1cxLek0Cj9x9atmhQ+apG+LwJpP2p3ypP8Pxau0poDnicrg7FTfPVQV5PW/3DHFm/eQ16vbgo5Yk3HGQ==",
|
||||
"version": "9.2.28",
|
||||
"resolved": "https://registry.npmjs.org/extendable-media-recorder/-/extendable-media-recorder-9.2.28.tgz",
|
||||
"integrity": "sha512-OIltlqo8rIUOcPn5c6aZ0qCvcTpXDnBgts8eRXK1VQKCI1omYBgZZyzkJ5vrQnB8xR34+GNOFs1Z+srq6tQsdQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"media-encoder-host": "^9.0.20",
|
||||
"media-encoder-host": "^9.0.21",
|
||||
"multi-buffer-data-view": "^6.0.22",
|
||||
"recorder-audio-worklet": "^6.0.48",
|
||||
"recorder-audio-worklet": "^6.0.49",
|
||||
"standardized-audio-context": "^25.3.77",
|
||||
"subscribable-things": "^2.1.53",
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/extendable-media-recorder-wav-encoder": {
|
||||
"version": "7.0.129",
|
||||
"resolved": "https://registry.npmjs.org/extendable-media-recorder-wav-encoder/-/extendable-media-recorder-wav-encoder-7.0.129.tgz",
|
||||
"integrity": "sha512-/wqM2hnzvLy/iUlg/EU3JIF8MJcidy8I77Z7CCm5+CVEClDfcs6bH9PgghuisndwKTaud0Dh48RTD83gkfEjCw==",
|
||||
"version": "7.0.130",
|
||||
"resolved": "https://registry.npmjs.org/extendable-media-recorder-wav-encoder/-/extendable-media-recorder-wav-encoder-7.0.130.tgz",
|
||||
"integrity": "sha512-tVroIOesnMarsm+iIRiWUEYgmQj/lGqeMVNwJla7/tTVkX3ZPamh0NW59deILANwDIJq9lARLQmsUhyDWW57PA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"extendable-media-recorder-wav-encoder-broker": "^7.0.119",
|
||||
"extendable-media-recorder-wav-encoder-worker": "^8.0.116",
|
||||
"extendable-media-recorder-wav-encoder-broker": "^7.0.120",
|
||||
"extendable-media-recorder-wav-encoder-worker": "^8.0.117",
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/extendable-media-recorder-wav-encoder-broker": {
|
||||
"version": "7.0.119",
|
||||
"resolved": "https://registry.npmjs.org/extendable-media-recorder-wav-encoder-broker/-/extendable-media-recorder-wav-encoder-broker-7.0.119.tgz",
|
||||
"integrity": "sha512-BLrFOnqFLpsmmNpSk/TfjNs4j6ImCSGtoryIpRlqNu5S/Avt6gRJI0s4UYvdK7h17PCi+8vaDr75blvmU1sYlw==",
|
||||
"version": "7.0.120",
|
||||
"resolved": "https://registry.npmjs.org/extendable-media-recorder-wav-encoder-broker/-/extendable-media-recorder-wav-encoder-broker-7.0.120.tgz",
|
||||
"integrity": "sha512-wYRnDsHngGGR7LnEfl+tkNa7ck31KSV3PzqGLMCeKRmr02OrYxJUKyCeYf79TEe8O0EFtlBGBR9fiAj6OGDOhg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"broker-factory": "^3.1.7",
|
||||
"extendable-media-recorder-wav-encoder-worker": "^8.0.116",
|
||||
"broker-factory": "^3.1.8",
|
||||
"extendable-media-recorder-wav-encoder-worker": "^8.0.117",
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/extendable-media-recorder-wav-encoder-worker": {
|
||||
"version": "8.0.116",
|
||||
"resolved": "https://registry.npmjs.org/extendable-media-recorder-wav-encoder-worker/-/extendable-media-recorder-wav-encoder-worker-8.0.116.tgz",
|
||||
"integrity": "sha512-bJPR0B7ZHeoqi9YoSie+UXAfEYya3efQ9eLiWuyK4KcOv+SuYQvWCoyzX5kjvb6GqIBCUnev5xulfeHRlyCwvw==",
|
||||
"version": "8.0.117",
|
||||
"resolved": "https://registry.npmjs.org/extendable-media-recorder-wav-encoder-worker/-/extendable-media-recorder-wav-encoder-worker-8.0.117.tgz",
|
||||
"integrity": "sha512-rnlIPkMB5F2sslesLXLdJ/Z0Kes4ROtdr+Kf/a6aF3233oJWK7165krWNZP5gpKZ4Z5Lhjx4fxVB12exZvlb2g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"tslib": "^2.8.1",
|
||||
"worker-factory": "^7.0.43"
|
||||
"worker-factory": "^7.0.44"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
@@ -9130,6 +9171,7 @@
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
|
||||
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -10427,6 +10469,18 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonc-eslint-parser/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/jsondiffpatch": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz",
|
||||
@@ -10985,6 +11039,7 @@
|
||||
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz",
|
||||
"integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mlly": "^1.7.3",
|
||||
"pkg-types": "^1.2.1"
|
||||
@@ -11527,40 +11582,40 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/media-encoder-host": {
|
||||
"version": "9.0.20",
|
||||
"resolved": "https://registry.npmjs.org/media-encoder-host/-/media-encoder-host-9.0.20.tgz",
|
||||
"integrity": "sha512-IyEYxw6az97RNuETOAZV4YZqNAPOiF9GKIp5mVZb4HOyWd6mhkWQ34ydOzhqAWogMyc4W05kjN/VCgTtgyFmsw==",
|
||||
"version": "9.0.21",
|
||||
"resolved": "https://registry.npmjs.org/media-encoder-host/-/media-encoder-host-9.0.21.tgz",
|
||||
"integrity": "sha512-hL41jBYmTKxGB2Z82rcR2hx4JCFBR3fmSOLR+G7EEkZn+uwSXDJTRFiYd7XWKnpjiHXy7ewf0ByEpby+2mv4Qg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"media-encoder-host-broker": "^8.0.19",
|
||||
"media-encoder-host-worker": "^10.0.19",
|
||||
"media-encoder-host-broker": "^8.0.20",
|
||||
"media-encoder-host-worker": "^10.0.20",
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/media-encoder-host-broker": {
|
||||
"version": "8.0.19",
|
||||
"resolved": "https://registry.npmjs.org/media-encoder-host-broker/-/media-encoder-host-broker-8.0.19.tgz",
|
||||
"integrity": "sha512-lTpsNuaZdTCdtTHsOyww7Ae0Mwv+7mFS+O4YkFYWhXwVs0rm6XbRK5jRRn5JmcX3n1eTE1lQS5RgX8qbNaIjSg==",
|
||||
"version": "8.0.20",
|
||||
"resolved": "https://registry.npmjs.org/media-encoder-host-broker/-/media-encoder-host-broker-8.0.20.tgz",
|
||||
"integrity": "sha512-Jab7frQx8d7Js+yBeMx7FeTrxRDjIpSmdARRkHSOtWNKJv9a+Hy2/OAPXoEJl6Y15jk1eiDwJkMMF6zvlnNMEA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"broker-factory": "^3.1.7",
|
||||
"broker-factory": "^3.1.8",
|
||||
"fast-unique-numbers": "^9.0.22",
|
||||
"media-encoder-host-worker": "^10.0.19",
|
||||
"media-encoder-host-worker": "^10.0.20",
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/media-encoder-host-worker": {
|
||||
"version": "10.0.19",
|
||||
"resolved": "https://registry.npmjs.org/media-encoder-host-worker/-/media-encoder-host-worker-10.0.19.tgz",
|
||||
"integrity": "sha512-I8fwc6f41peER3RFSiwDxnIHbqU7p3pc2ghQozcw9CQfL0mWEo4IjQJtyswrrlL/HO2pgVSMQbaNzE4q/0mfDQ==",
|
||||
"version": "10.0.20",
|
||||
"resolved": "https://registry.npmjs.org/media-encoder-host-worker/-/media-encoder-host-worker-10.0.20.tgz",
|
||||
"integrity": "sha512-Ysqjwlcu4VP6FlM+atQJZFFJwvfrw603gnmhWfyBlcE5+pl7ctc0wo2dbPiBpz9pc1pY4N5N6PHHhywkqHxfnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"extendable-media-recorder-wav-encoder-broker": "^7.0.119",
|
||||
"extendable-media-recorder-wav-encoder-broker": "^7.0.120",
|
||||
"tslib": "^2.8.1",
|
||||
"worker-factory": "^7.0.43"
|
||||
"worker-factory": "^7.0.44"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
@@ -12298,6 +12353,7 @@
|
||||
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz",
|
||||
"integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.0",
|
||||
"pathe": "^2.0.1",
|
||||
@@ -12309,7 +12365,8 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
||||
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mri": {
|
||||
"version": "1.2.0",
|
||||
@@ -12867,6 +12924,7 @@
|
||||
"resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz",
|
||||
"integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"quansync": "^0.2.7"
|
||||
}
|
||||
@@ -13167,6 +13225,7 @@
|
||||
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
|
||||
"integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"confbox": "^0.1.8",
|
||||
"mlly": "^1.7.4",
|
||||
@@ -13177,7 +13236,8 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
||||
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.52.0",
|
||||
@@ -13781,7 +13841,8 @@
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/sxzz"
|
||||
}
|
||||
]
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/querystringify": {
|
||||
"version": "2.2.0",
|
||||
@@ -13941,19 +14002,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/recorder-audio-worklet": {
|
||||
"version": "6.0.48",
|
||||
"resolved": "https://registry.npmjs.org/recorder-audio-worklet/-/recorder-audio-worklet-6.0.48.tgz",
|
||||
"integrity": "sha512-PVlq/1hjCrPcUGqARg8rR30A303xDCao0jmlBTaUaKkN3Xme58RI7EQxurv8rw2eDwVrN+nrni0UoJoa5/v+zg==",
|
||||
"version": "6.0.49",
|
||||
"resolved": "https://registry.npmjs.org/recorder-audio-worklet/-/recorder-audio-worklet-6.0.49.tgz",
|
||||
"integrity": "sha512-JLKSKh5rAWxReHqca/f3h9L8pzBF1KnB+N2oIP2jkTD37n2yupkCazKD2f20pUOGkaX6AJG998gtKNCHYJn4nw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"broker-factory": "^3.1.7",
|
||||
"broker-factory": "^3.1.8",
|
||||
"fast-unique-numbers": "^9.0.22",
|
||||
"recorder-audio-worklet-processor": "^5.0.35",
|
||||
"standardized-audio-context": "^25.3.77",
|
||||
"subscribable-things": "^2.1.53",
|
||||
"tslib": "^2.8.1",
|
||||
"worker-factory": "^7.0.43"
|
||||
"worker-factory": "^7.0.44"
|
||||
}
|
||||
},
|
||||
"node_modules/recorder-audio-worklet-processor": {
|
||||
@@ -14559,6 +14620,7 @@
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
@@ -15333,7 +15395,8 @@
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
|
||||
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinypool": {
|
||||
"version": "1.0.1",
|
||||
@@ -16269,6 +16332,7 @@
|
||||
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.5.tgz",
|
||||
"integrity": "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.1",
|
||||
"picomatch": "^4.0.2",
|
||||
@@ -16283,6 +16347,7 @@
|
||||
"resolved": "https://registry.npmjs.org/unplugin-icons/-/unplugin-icons-0.22.0.tgz",
|
||||
"integrity": "sha512-CP+iZq5U7doOifer5bcM0jQ9t3Is7EGybIYt3myVxceI8Zuk8EZEpe1NPtJvh7iqMs1VdbK0L41t9+um9VuuLw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@antfu/install-pkg": "^0.5.0",
|
||||
"@antfu/utils": "^0.7.10",
|
||||
@@ -16329,6 +16394,7 @@
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
@@ -16346,6 +16412,7 @@
|
||||
"resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.28.0.tgz",
|
||||
"integrity": "sha512-jiTGtJ3JsRFBjgvyilfrX7yUoGKScFgbdNw+6p6kEXU+Spf/rhxzgvdfuMcvhCcLmflB/dY3pGQshYBVGOUx7Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@antfu/utils": "^0.7.10",
|
||||
"@rollup/pluginutils": "^5.1.4",
|
||||
@@ -16392,6 +16459,7 @@
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
@@ -16424,6 +16492,7 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -17435,7 +17504,8 @@
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/websocket-driver": {
|
||||
"version": "0.7.4",
|
||||
@@ -17615,9 +17685,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/worker-factory": {
|
||||
"version": "7.0.43",
|
||||
"resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.43.tgz",
|
||||
"integrity": "sha512-SACVoj3gWKtMVyT9N+VD11Pd/Xe58+ZFfp8b7y/PagOvj3i8lU3Uyj+Lj7WYTmSBvNLC0JFaQkx44E6DhH5+WA==",
|
||||
"version": "7.0.44",
|
||||
"resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.44.tgz",
|
||||
"integrity": "sha512-08AuUfWi+KeZI+KC7nU4pU/9tDeAFvE5NSWk+K9nIfuQc6UlOsZtjjeGVYVEn+DEchyXNJ5i10HCn0xRzFXEQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
|
||||
@@ -16,11 +16,13 @@ import { computed, onMounted } from 'vue'
|
||||
|
||||
import GlobalDialog from '@/components/dialog/GlobalDialog.vue'
|
||||
import config from '@/config'
|
||||
import { useConflictDetection } from '@/composables/useConflictDetection'
|
||||
import { useWorkspaceStore } from '@/stores/workspaceStore'
|
||||
|
||||
import { electronAPI, isElectron } from './utils/envUtil'
|
||||
|
||||
const workspaceStore = useWorkspaceStore()
|
||||
const conflictDetection = useConflictDetection()
|
||||
const isLoading = computed<boolean>(() => workspaceStore.spinner)
|
||||
const handleKey = (e: KeyboardEvent) => {
|
||||
workspaceStore.shiftDown = e.shiftKey
|
||||
@@ -47,5 +49,9 @@ onMounted(() => {
|
||||
if (isElectron()) {
|
||||
document.addEventListener('contextmenu', showContextMenu)
|
||||
}
|
||||
|
||||
// Initialize conflict detection in background
|
||||
// This runs async and doesn't block UI setup
|
||||
void conflictDetection.initializeConflictDetection()
|
||||
})
|
||||
</script>
|
||||
|
||||
1096
src/composables/useConflictDetection.ts
Normal file
1096
src/composables/useConflictDetection.ts
Normal file
File diff suppressed because it is too large
Load Diff
239
src/stores/nodeCompatibilityStore.ts
Normal file
239
src/stores/nodeCompatibilityStore.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import type {
|
||||
ConflictType,
|
||||
SystemEnvironment
|
||||
} from '@/types/conflictDetectionTypes'
|
||||
|
||||
interface IncompatibleNodeInfo {
|
||||
nodeId: string
|
||||
nodeName: string
|
||||
disableReason: ConflictType
|
||||
conflictDetails: string
|
||||
detectedAt: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Store for managing node compatibility checking functionality.
|
||||
* Follows error-resilient patterns from useConflictDetection composable.
|
||||
*/
|
||||
export const useNodeCompatibilityStore = defineStore(
|
||||
'nodeCompatibility',
|
||||
() => {
|
||||
// Core state
|
||||
const isChecking = ref(false)
|
||||
const lastCheckTime = ref<string | null>(null)
|
||||
const checkError = ref<string | null>(null)
|
||||
const systemEnvironment = ref<SystemEnvironment | null>(null)
|
||||
|
||||
// Node tracking maps
|
||||
const incompatibleNodes = ref<Map<string, IncompatibleNodeInfo>>(new Map())
|
||||
const failedImportNodes = ref<Set<string>>(new Set())
|
||||
const bannedNodes = ref<Set<string>>(new Set())
|
||||
const securityPendingNodes = ref<Set<string>>(new Set())
|
||||
|
||||
// User interaction state
|
||||
const hasShownNotificationModal = ref(false)
|
||||
const pendingNotificationNodes = ref<IncompatibleNodeInfo[]>([])
|
||||
|
||||
// Computed properties
|
||||
const hasIncompatibleNodes = computed(
|
||||
() => incompatibleNodes.value.size > 0
|
||||
)
|
||||
const totalIncompatibleCount = computed(
|
||||
() =>
|
||||
incompatibleNodes.value.size +
|
||||
failedImportNodes.value.size +
|
||||
bannedNodes.value.size
|
||||
)
|
||||
|
||||
const incompatibleNodesList = computed(() =>
|
||||
Array.from(incompatibleNodes.value.values())
|
||||
)
|
||||
|
||||
const shouldShowNotification = computed(() => {
|
||||
// Show notification if there are incompatible nodes and we haven't shown notification yet
|
||||
return hasIncompatibleNodes.value && !hasShownNotificationModal.value
|
||||
})
|
||||
|
||||
/**
|
||||
* Checks if a node has compatibility issues.
|
||||
*/
|
||||
function hasNodeCompatibilityIssues(nodeId: string): boolean {
|
||||
return (
|
||||
incompatibleNodes.value.has(nodeId) ||
|
||||
failedImportNodes.value.has(nodeId) ||
|
||||
bannedNodes.value.has(nodeId)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the compatibility info for a node.
|
||||
*/
|
||||
function getNodeCompatibilityInfo(
|
||||
nodeId: string
|
||||
): IncompatibleNodeInfo | null {
|
||||
return incompatibleNodes.value.get(nodeId) || null
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a node to the incompatible list.
|
||||
*/
|
||||
function addIncompatibleNode(
|
||||
nodeId: string,
|
||||
nodeName: string,
|
||||
reason: ConflictType,
|
||||
details: string
|
||||
): void {
|
||||
const info: IncompatibleNodeInfo = {
|
||||
nodeId,
|
||||
nodeName,
|
||||
disableReason: reason,
|
||||
conflictDetails: details,
|
||||
detectedAt: new Date().toISOString()
|
||||
}
|
||||
|
||||
incompatibleNodes.value.set(nodeId, info)
|
||||
|
||||
// Add to pending list (for notification purposes)
|
||||
if (!hasShownNotificationModal.value) {
|
||||
pendingNotificationNodes.value.push(info)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a node from the incompatible list.
|
||||
*/
|
||||
function removeIncompatibleNode(nodeId: string): void {
|
||||
incompatibleNodes.value.delete(nodeId)
|
||||
failedImportNodes.value.delete(nodeId)
|
||||
bannedNodes.value.delete(nodeId)
|
||||
securityPendingNodes.value.delete(nodeId)
|
||||
|
||||
// Remove from pending list
|
||||
pendingNotificationNodes.value = pendingNotificationNodes.value.filter(
|
||||
(node) => node.nodeId !== nodeId
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all compatibility check results.
|
||||
*/
|
||||
function clearResults(): void {
|
||||
incompatibleNodes.value.clear()
|
||||
failedImportNodes.value.clear()
|
||||
bannedNodes.value.clear()
|
||||
securityPendingNodes.value.clear()
|
||||
pendingNotificationNodes.value = []
|
||||
checkError.value = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks that the notification modal has been shown.
|
||||
*/
|
||||
function markNotificationModalShown(): void {
|
||||
hasShownNotificationModal.value = true
|
||||
pendingNotificationNodes.value = []
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the notification modal state (for testing or re-initialization).
|
||||
*/
|
||||
function resetNotificationModalState(): void {
|
||||
hasShownNotificationModal.value = false
|
||||
pendingNotificationNodes.value = Array.from(
|
||||
incompatibleNodes.value.values()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the system environment information.
|
||||
*/
|
||||
function setSystemEnvironment(env: SystemEnvironment): void {
|
||||
systemEnvironment.value = env
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the checking state.
|
||||
*/
|
||||
function setCheckingState(checking: boolean): void {
|
||||
isChecking.value = checking
|
||||
if (checking) {
|
||||
checkError.value = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a successful check completion.
|
||||
*/
|
||||
function recordCheckCompletion(): void {
|
||||
lastCheckTime.value = new Date().toISOString()
|
||||
isChecking.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a check error.
|
||||
*/
|
||||
function recordCheckError(error: string): void {
|
||||
checkError.value = error
|
||||
isChecking.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a summary of the current compatibility state.
|
||||
*/
|
||||
function getCompatibilitySummary() {
|
||||
return {
|
||||
totalChecked: lastCheckTime.value ? 'completed' : 'pending',
|
||||
incompatibleCount: incompatibleNodes.value.size,
|
||||
failedImportCount: failedImportNodes.value.size,
|
||||
bannedCount: bannedNodes.value.size,
|
||||
securityPendingCount: securityPendingNodes.value.size,
|
||||
totalIssues: totalIncompatibleCount.value,
|
||||
lastCheckTime: lastCheckTime.value,
|
||||
hasError: !!checkError.value
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
isChecking: computed(() => isChecking.value),
|
||||
lastCheckTime: computed(() => lastCheckTime.value),
|
||||
checkError: computed(() => checkError.value),
|
||||
systemEnvironment: computed(() => systemEnvironment.value),
|
||||
|
||||
// Node tracking
|
||||
incompatibleNodes: computed(() => incompatibleNodes.value),
|
||||
incompatibleNodesList,
|
||||
failedImportNodes: computed(() => failedImportNodes.value),
|
||||
bannedNodes: computed(() => bannedNodes.value),
|
||||
securityPendingNodes: computed(() => securityPendingNodes.value),
|
||||
|
||||
// User interaction
|
||||
hasShownNotificationModal: computed(
|
||||
() => hasShownNotificationModal.value
|
||||
),
|
||||
pendingNotificationNodes: computed(() => pendingNotificationNodes.value),
|
||||
shouldShowNotification,
|
||||
|
||||
// Computed
|
||||
hasIncompatibleNodes,
|
||||
totalIncompatibleCount,
|
||||
|
||||
// Methods
|
||||
hasNodeCompatibilityIssues,
|
||||
getNodeCompatibilityInfo,
|
||||
addIncompatibleNode,
|
||||
removeIncompatibleNode,
|
||||
clearResults,
|
||||
markNotificationModalShown,
|
||||
resetNotificationModalState,
|
||||
setSystemEnvironment,
|
||||
setCheckingState,
|
||||
recordCheckCompletion,
|
||||
recordCheckError,
|
||||
getCompatibilitySummary
|
||||
}
|
||||
}
|
||||
)
|
||||
264
src/types/conflictDetectionTypes.ts
Normal file
264
src/types/conflictDetectionTypes.ts
Normal file
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* Type definitions for the conflict detection system.
|
||||
* These types are used to detect compatibility issues between Node Packs and the system environment.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Conflict types that can be detected in the system
|
||||
* @enum {string}
|
||||
*/
|
||||
export type ConflictType =
|
||||
| 'comfyui_version' // ComfyUI version mismatch
|
||||
| 'frontend_version' // Frontend version mismatch
|
||||
| 'python_version' // Python version mismatch
|
||||
| 'os' // Operating system incompatibility
|
||||
| 'accelerator' // GPU/accelerator incompatibility
|
||||
| 'banned' // Banned package
|
||||
| 'security_pending' // Security verification pending
|
||||
|
||||
/**
|
||||
* Security scan status for packages
|
||||
* @enum {string}
|
||||
*/
|
||||
export type SecurityScanStatus = 'pending' | 'passed' | 'failed' | 'unknown'
|
||||
|
||||
/**
|
||||
* Supported operating systems (as per Registry Admin guide)
|
||||
* @enum {string}
|
||||
*/
|
||||
export type SupportedOS = 'Windows' | 'macOS' | 'Linux' | 'any'
|
||||
|
||||
/**
|
||||
* Supported accelerators for GPU computation (as per Registry Admin guide)
|
||||
* @enum {string}
|
||||
*/
|
||||
export type SupportedAccelerator = 'CUDA' | 'ROCm' | 'Metal' | 'CPU' | 'any'
|
||||
|
||||
/**
|
||||
* Version comparison operators
|
||||
* @enum {string}
|
||||
*/
|
||||
export type VersionOperator = '>=' | '>' | '<=' | '<' | '==' | '!='
|
||||
|
||||
/**
|
||||
* Version requirement specification
|
||||
*/
|
||||
export interface VersionRequirement {
|
||||
/** @description Comparison operator for version checking */
|
||||
operator: VersionOperator
|
||||
/** @description Target version string */
|
||||
version: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Node Pack requirements from Registry API
|
||||
*/
|
||||
export interface NodePackRequirements {
|
||||
/** @description Unique package identifier */
|
||||
package_id: string
|
||||
/** @description Human-readable package name */
|
||||
package_name: string
|
||||
/** @description Currently installed version */
|
||||
installed_version: string
|
||||
/** @description Whether the package is enabled locally */
|
||||
is_enabled: boolean
|
||||
|
||||
/** @description Supported ComfyUI version from Registry */
|
||||
supported_comfyui_version?: string
|
||||
/** @description Supported frontend version from Registry */
|
||||
supported_comfyui_frontend_version?: string
|
||||
/** @description List of supported operating systems from Registry */
|
||||
supported_os?: SupportedOS[]
|
||||
/** @description List of supported accelerators from Registry */
|
||||
supported_accelerators?: SupportedAccelerator[]
|
||||
/** @description Package dependencies from Registry */
|
||||
dependencies?: string[]
|
||||
|
||||
/** @description Node status from Registry (Active/Banned/Deleted) */
|
||||
registry_status?:
|
||||
| 'NodeStatusActive'
|
||||
| 'NodeStatusBanned'
|
||||
| 'NodeStatusDeleted'
|
||||
/** @description Node version status from Registry */
|
||||
version_status?:
|
||||
| 'NodeVersionStatusActive'
|
||||
| 'NodeVersionStatusBanned'
|
||||
| 'NodeVersionStatusDeleted'
|
||||
| 'NodeVersionStatusPending'
|
||||
| 'NodeVersionStatusFlagged'
|
||||
/** @description Whether package is banned (derived from status) */
|
||||
is_banned: boolean
|
||||
/** @description Reason for ban if applicable */
|
||||
ban_reason?: string
|
||||
|
||||
// Metadata
|
||||
/** @description Registry data fetch timestamp */
|
||||
registry_fetch_time: string
|
||||
/** @description Whether Registry data was successfully fetched */
|
||||
has_registry_data: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Current system environment information
|
||||
*/
|
||||
export interface SystemEnvironment {
|
||||
// Version information
|
||||
/** @description Current ComfyUI version */
|
||||
comfyui_version: string
|
||||
/** @description Current frontend version */
|
||||
frontend_version: string
|
||||
/** @description Current Python version */
|
||||
python_version: string
|
||||
|
||||
// Platform information
|
||||
/** @description Operating system type */
|
||||
os: SupportedOS
|
||||
/** @description Detailed platform information (e.g., 'Darwin 24.5.0', 'Windows 10') */
|
||||
platform_details: string
|
||||
/** @description System architecture (e.g., 'x64', 'arm64') */
|
||||
architecture: string
|
||||
|
||||
// GPU/accelerator information
|
||||
/** @description List of available accelerators */
|
||||
available_accelerators: SupportedAccelerator[]
|
||||
/** @description Primary accelerator in use */
|
||||
primary_accelerator: SupportedAccelerator
|
||||
/** @description GPU memory in megabytes, if available */
|
||||
gpu_memory_mb?: number
|
||||
|
||||
// Runtime information
|
||||
/** @description Node.js environment mode */
|
||||
node_env: 'development' | 'production'
|
||||
/** @description Browser user agent string */
|
||||
user_agent: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual conflict detection result for a package
|
||||
*/
|
||||
export interface ConflictDetectionResult {
|
||||
/** @description Package identifier */
|
||||
package_id: string
|
||||
/** @description Package name */
|
||||
package_name: string
|
||||
/** @description Whether any conflicts were detected */
|
||||
has_conflict: boolean
|
||||
/** @description List of detected conflicts */
|
||||
conflicts: ConflictDetail[]
|
||||
/** @description Overall compatibility status */
|
||||
is_compatible: boolean
|
||||
/** @description Whether conflicts can be automatically resolved */
|
||||
can_auto_resolve: boolean
|
||||
/** @description Recommended action to resolve conflicts */
|
||||
recommended_action: RecommendedAction
|
||||
}
|
||||
|
||||
/**
|
||||
* Detailed information about a specific conflict
|
||||
*/
|
||||
export interface ConflictDetail {
|
||||
/** @description Type of conflict detected */
|
||||
type: ConflictType
|
||||
/** @description Severity level of the conflict */
|
||||
severity: 'error' | 'warning' | 'info'
|
||||
/** @description Human-readable description of the conflict */
|
||||
description: string
|
||||
/** @description Current system value */
|
||||
current_value: string
|
||||
/** @description Required value for compatibility */
|
||||
required_value: string
|
||||
/** @description Optional steps to resolve the conflict */
|
||||
resolution_steps?: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Recommended action to resolve conflicts
|
||||
*/
|
||||
export interface RecommendedAction {
|
||||
/** @description Type of action to take */
|
||||
action_type: 'disable' | 'update' | 'ignore' | 'manual_review'
|
||||
/** @description Reason for the recommended action */
|
||||
reason: string
|
||||
/** @description Step-by-step instructions */
|
||||
steps: string[]
|
||||
/** @description Estimated difficulty of implementing the action */
|
||||
estimated_difficulty: 'easy' | 'medium' | 'hard'
|
||||
}
|
||||
|
||||
/**
|
||||
* Overall conflict detection summary
|
||||
*/
|
||||
export interface ConflictDetectionSummary {
|
||||
/** @description Total number of packages checked */
|
||||
total_packages: number
|
||||
/** @description Number of compatible packages */
|
||||
compatible_packages: number
|
||||
/** @description Number of packages with conflicts */
|
||||
conflicted_packages: number
|
||||
/** @description Number of banned packages */
|
||||
banned_packages: number
|
||||
/** @description Number of packages pending security verification */
|
||||
security_pending_packages: number
|
||||
/** @description Node IDs grouped by conflict type */
|
||||
conflicts_by_type_details: Record<ConflictType, string[]>
|
||||
/** @description Timestamp of the last conflict check */
|
||||
last_check_timestamp: string
|
||||
/** @description Duration of the conflict check in milliseconds */
|
||||
check_duration_ms: number
|
||||
}
|
||||
|
||||
/**
|
||||
* API request/response interfaces
|
||||
*/
|
||||
|
||||
/**
|
||||
* Request payload for conflict detection API
|
||||
*/
|
||||
export interface ConflictDetectionRequest {
|
||||
/** @description Current system environment information */
|
||||
system_environment: SystemEnvironment
|
||||
/** @description Optional list of specific package IDs to check */
|
||||
package_ids?: string[]
|
||||
/** @description Whether to include banned packages in the check */
|
||||
include_banned?: boolean
|
||||
/** @description Whether to include security-pending packages in the check */
|
||||
include_security_pending?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Response payload from conflict detection API
|
||||
*/
|
||||
export interface ConflictDetectionResponse {
|
||||
/** @description Whether the API request was successful */
|
||||
success: boolean
|
||||
/** @description Error message if the request failed */
|
||||
error_message?: string
|
||||
|
||||
/** @description Summary of the conflict detection results */
|
||||
summary: ConflictDetectionSummary
|
||||
/** @description Detailed results for each package */
|
||||
results: ConflictDetectionResult[]
|
||||
|
||||
/** @description System environment information detected by the server (for comparison) */
|
||||
detected_system_environment?: Partial<SystemEnvironment>
|
||||
}
|
||||
|
||||
/**
|
||||
* Real-time conflict detection event
|
||||
*/
|
||||
export interface ConflictDetectionEvent {
|
||||
/** @description Type of event */
|
||||
event_type:
|
||||
| 'conflict_detected'
|
||||
| 'conflict_resolved'
|
||||
| 'scan_started'
|
||||
| 'scan_completed'
|
||||
/** @description Event timestamp */
|
||||
timestamp: string
|
||||
/** @description Package ID associated with the event, if applicable */
|
||||
package_id?: string
|
||||
/** @description Type of conflict, if applicable */
|
||||
conflict_type?: ConflictType
|
||||
/** @description Additional event details */
|
||||
details?: string
|
||||
}
|
||||
80
src/utils/versionUtil.ts
Normal file
80
src/utils/versionUtil.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import * as semver from 'semver'
|
||||
|
||||
/**
|
||||
* Cleans a version string by removing common prefixes and normalizing format
|
||||
* @param version Raw version string (e.g., "v1.2.3", "1.2.3-alpha")
|
||||
* @returns Cleaned version string or original if cleaning fails
|
||||
*/
|
||||
export function cleanVersion(version: string): string {
|
||||
return semver.clean(version) || version
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a version satisfies a version range
|
||||
* @param version Current version
|
||||
* @param range Version range (e.g., ">=1.0.0", "^1.2.0", "1.0.0 - 2.0.0")
|
||||
* @returns true if version satisfies the range
|
||||
*/
|
||||
export function satisfiesVersion(version: string, range: string): boolean {
|
||||
try {
|
||||
const cleanedVersion = cleanVersion(version)
|
||||
return semver.satisfies(cleanedVersion, range)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two versions and returns the difference type
|
||||
* @param version1 First version
|
||||
* @param version2 Second version
|
||||
* @returns Difference type or null if comparison fails
|
||||
*/
|
||||
export function getVersionDifference(
|
||||
version1: string,
|
||||
version2: string
|
||||
): semver.ReleaseType | null {
|
||||
try {
|
||||
const clean1 = cleanVersion(version1)
|
||||
const clean2 = cleanVersion(version2)
|
||||
return semver.diff(clean1, clean2)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a version is valid according to semver
|
||||
* @param version Version string to validate
|
||||
* @returns true if version is valid
|
||||
*/
|
||||
export function isValidVersion(version: string): boolean {
|
||||
return semver.valid(version) !== null
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a human-readable description of a version range
|
||||
* @param range Version range string
|
||||
* @returns Description of what the range means
|
||||
*/
|
||||
export function describeVersionRange(range: string): string {
|
||||
if (range.startsWith('>=')) {
|
||||
return `version ${range.substring(2)} or higher`
|
||||
} else if (range.startsWith('>')) {
|
||||
return `version higher than ${range.substring(1)}`
|
||||
} else if (range.startsWith('<=')) {
|
||||
return `version ${range.substring(2)} or lower`
|
||||
} else if (range.startsWith('<')) {
|
||||
return `version lower than ${range.substring(1)}`
|
||||
} else if (range.startsWith('^')) {
|
||||
return `compatible with version ${range.substring(1)}`
|
||||
} else if (range.startsWith('~')) {
|
||||
return `approximately version ${range.substring(1)}`
|
||||
} else if (range.includes(' - ')) {
|
||||
const [min, max] = range.split(' - ')
|
||||
return `version between ${min} and ${max}`
|
||||
} else if (range.includes('||')) {
|
||||
return `one of multiple version ranges: ${range}`
|
||||
}
|
||||
return `version ${range}`
|
||||
}
|
||||
864
tests-ui/tests/composables/useConflictDetection.test.ts
Normal file
864
tests-ui/tests/composables/useConflictDetection.test.ts
Normal file
@@ -0,0 +1,864 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
import { useConflictDetection } from '@/composables/useConflictDetection'
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import type { components as ManagerComponents } from '@/types/generatedManagerTypes'
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('@/scripts/api', () => ({
|
||||
api: {
|
||||
fetchApi: vi.fn()
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('@/services/comfyManagerService', () => ({
|
||||
useComfyManagerService: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('@/services/comfyRegistryService', () => ({
|
||||
useComfyRegistryService: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('@/stores/systemStatsStore', () => ({
|
||||
useSystemStatsStore: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('@/config', () => ({
|
||||
default: {
|
||||
app_version: '1.24.0-1'
|
||||
}
|
||||
}))
|
||||
|
||||
describe('useConflictDetection with Registry Store', () => {
|
||||
const mockComfyManagerService = {
|
||||
listInstalledPacks: vi.fn()
|
||||
}
|
||||
|
||||
const mockRegistryService = {
|
||||
getPackByVersion: vi.fn()
|
||||
}
|
||||
|
||||
const mockSystemStatsStore = {
|
||||
fetchSystemStats: vi.fn(),
|
||||
systemStats: {
|
||||
system: {
|
||||
comfyui_version: '0.3.41',
|
||||
python_version: '3.12.11',
|
||||
os: 'Darwin'
|
||||
},
|
||||
devices: [
|
||||
{
|
||||
name: 'Apple M1 Pro',
|
||||
type: 'mps',
|
||||
vram_total: 17179869184
|
||||
}
|
||||
]
|
||||
} as any
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.clearAllMocks()
|
||||
|
||||
// Reset mock system stats to default state
|
||||
mockSystemStatsStore.systemStats = {
|
||||
system: {
|
||||
comfyui_version: '0.3.41',
|
||||
python_version: '3.12.11',
|
||||
os: 'Darwin'
|
||||
},
|
||||
devices: [
|
||||
{
|
||||
name: 'Apple M1 Pro',
|
||||
type: 'mps',
|
||||
vram_total: 17179869184
|
||||
}
|
||||
]
|
||||
} as any
|
||||
|
||||
// Reset mock functions
|
||||
mockSystemStatsStore.fetchSystemStats.mockResolvedValue(undefined)
|
||||
mockComfyManagerService.listInstalledPacks.mockReset()
|
||||
mockRegistryService.getPackByVersion.mockReset()
|
||||
|
||||
// Mock useComfyManagerService
|
||||
const { useComfyManagerService } = await import(
|
||||
'@/services/comfyManagerService'
|
||||
)
|
||||
vi.mocked(useComfyManagerService).mockReturnValue(
|
||||
mockComfyManagerService as any
|
||||
)
|
||||
|
||||
// Mock useComfyRegistryService
|
||||
const { useComfyRegistryService } = await import(
|
||||
'@/services/comfyRegistryService'
|
||||
)
|
||||
vi.mocked(useComfyRegistryService).mockReturnValue(
|
||||
mockRegistryService as any
|
||||
)
|
||||
|
||||
// Mock useSystemStatsStore
|
||||
const { useSystemStatsStore } = await import('@/stores/systemStatsStore')
|
||||
vi.mocked(useSystemStatsStore).mockReturnValue(mockSystemStatsStore as any)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe('system environment detection', () => {
|
||||
it('should collect system environment information successfully', async () => {
|
||||
const { detectSystemEnvironment } = useConflictDetection()
|
||||
const environment = await detectSystemEnvironment()
|
||||
|
||||
expect(environment.comfyui_version).toBe('0.3.41')
|
||||
expect(environment.frontend_version).toBe('1.24.0-1')
|
||||
expect(environment.python_version).toBe('3.12.11')
|
||||
expect(environment.available_accelerators).toContain('Metal')
|
||||
expect(environment.available_accelerators).toContain('CPU')
|
||||
expect(environment.primary_accelerator).toBe('Metal')
|
||||
})
|
||||
|
||||
it('should return fallback environment information when systemStatsStore fails', async () => {
|
||||
// Mock systemStatsStore failure
|
||||
mockSystemStatsStore.fetchSystemStats.mockRejectedValue(
|
||||
new Error('Store failure')
|
||||
)
|
||||
mockSystemStatsStore.systemStats = null
|
||||
|
||||
const { detectSystemEnvironment } = useConflictDetection()
|
||||
const environment = await detectSystemEnvironment()
|
||||
|
||||
expect(environment.comfyui_version).toBe('unknown')
|
||||
expect(environment.frontend_version).toBe('1.24.0-1')
|
||||
expect(environment.python_version).toBe('unknown')
|
||||
expect(environment.available_accelerators).toEqual(['CPU'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('package requirements detection with Registry Store', () => {
|
||||
it('should fetch and combine local + Registry data successfully', async () => {
|
||||
// Mock installed packages
|
||||
const mockInstalledPacks: ManagerComponents['schemas']['InstalledPacksResponse'] =
|
||||
{
|
||||
'ComfyUI-Manager': {
|
||||
ver: 'cb0fa5829d5378e5dddb8e8515b30a3ff20e1471',
|
||||
cnr_id: '',
|
||||
aux_id: 'viva-jinyi/ComfyUI-Manager',
|
||||
enabled: true
|
||||
},
|
||||
'ComfyUI-TestNode': {
|
||||
ver: '1.0.0',
|
||||
cnr_id: 'test-node',
|
||||
aux_id: null,
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
// Mock Registry data
|
||||
const mockRegistryPacks: components['schemas']['Node'][] = [
|
||||
{
|
||||
id: 'ComfyUI-Manager',
|
||||
name: 'ComfyUI Manager',
|
||||
supported_os: ['Windows', 'Linux', 'macOS'],
|
||||
supported_accelerators: ['CUDA', 'Metal', 'CPU'],
|
||||
supported_comfyui_version: '>=0.3.0',
|
||||
status: 'NodeStatusActive'
|
||||
} as components['schemas']['Node'],
|
||||
{
|
||||
id: 'ComfyUI-TestNode',
|
||||
name: 'Test Node',
|
||||
supported_os: ['Windows', 'Linux'],
|
||||
supported_accelerators: ['CUDA'],
|
||||
supported_comfyui_version: '>=0.2.0',
|
||||
status: 'NodeStatusBanned'
|
||||
} as components['schemas']['Node']
|
||||
]
|
||||
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue(
|
||||
mockInstalledPacks
|
||||
)
|
||||
|
||||
// Mock Registry Service individual calls
|
||||
mockRegistryService.getPackByVersion.mockImplementation(
|
||||
(packageName: string) => {
|
||||
const packageData = mockRegistryPacks.find(
|
||||
(p) => p.id === packageName
|
||||
)
|
||||
if (packageData) {
|
||||
return Promise.resolve({
|
||||
...packageData,
|
||||
supported_comfyui_version: packageData.supported_comfyui_version,
|
||||
supported_os: packageData.supported_os,
|
||||
supported_accelerators: packageData.supported_accelerators,
|
||||
status: packageData.status
|
||||
})
|
||||
}
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
)
|
||||
|
||||
const { performConflictDetection } = useConflictDetection()
|
||||
const result = await performConflictDetection()
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.summary.total_packages).toBe(2)
|
||||
expect(result.results).toHaveLength(2)
|
||||
|
||||
// Verify individual calls were made
|
||||
expect(mockRegistryService.getPackByVersion).toHaveBeenCalledWith(
|
||||
'ComfyUI-Manager',
|
||||
'cb0fa5829d5378e5dddb8e8515b30a3ff20e1471',
|
||||
expect.anything()
|
||||
)
|
||||
expect(mockRegistryService.getPackByVersion).toHaveBeenCalledWith(
|
||||
'ComfyUI-TestNode',
|
||||
'1.0.0',
|
||||
expect.anything()
|
||||
)
|
||||
|
||||
// Check that Registry data was properly integrated
|
||||
const managerNode = result.results.find(
|
||||
(r) => r.package_id === 'ComfyUI-Manager'
|
||||
)
|
||||
expect(managerNode?.is_compatible).toBe(true) // Should be compatible
|
||||
|
||||
// Disabled + banned node should have conflicts
|
||||
const testNode = result.results.find(
|
||||
(r) => r.package_id === 'ComfyUI-TestNode'
|
||||
)
|
||||
expect(testNode?.conflicts).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'banned',
|
||||
severity: 'error'
|
||||
})
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle Registry Store failures gracefully', async () => {
|
||||
// Mock installed packages
|
||||
const mockInstalledPacks: ManagerComponents['schemas']['InstalledPacksResponse'] =
|
||||
{
|
||||
'Unknown-Package': {
|
||||
ver: '1.0.0',
|
||||
cnr_id: 'unknown',
|
||||
aux_id: null,
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue(
|
||||
mockInstalledPacks
|
||||
)
|
||||
|
||||
// Mock Registry Service returning null (no packages found)
|
||||
mockRegistryService.getPackByVersion.mockResolvedValue(null)
|
||||
|
||||
const { performConflictDetection } = useConflictDetection()
|
||||
const result = await performConflictDetection()
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.summary.total_packages).toBe(1)
|
||||
expect(result.results).toHaveLength(1)
|
||||
|
||||
// Should have warning about missing Registry data
|
||||
const unknownPackage = result.results[0]
|
||||
expect(unknownPackage.conflicts).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'security_pending',
|
||||
severity: 'warning',
|
||||
description: expect.stringContaining('Registry data not available')
|
||||
})
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it('should return empty array when local package information cannot be retrieved', async () => {
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue(null)
|
||||
|
||||
const { performConflictDetection } = useConflictDetection()
|
||||
const result = await performConflictDetection()
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.summary.total_packages).toBe(0)
|
||||
expect(result.results).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('conflict detection logic with Registry Store', () => {
|
||||
it('should detect no conflicts for fully compatible packages', async () => {
|
||||
// Mock compatible package
|
||||
const mockInstalledPacks: ManagerComponents['schemas']['InstalledPacksResponse'] =
|
||||
{
|
||||
CompatibleNode: {
|
||||
ver: '1.0.0',
|
||||
cnr_id: 'compatible-node',
|
||||
aux_id: null,
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
const mockCompatibleRegistryPacks: components['schemas']['Node'][] = [
|
||||
{
|
||||
id: 'CompatibleNode',
|
||||
name: 'Compatible Node',
|
||||
supported_os: ['Windows', 'Linux', 'macOS'],
|
||||
supported_accelerators: ['Metal', 'CUDA', 'CPU'],
|
||||
supported_comfyui_version: '>=0.3.0',
|
||||
status: 'NodeStatusActive'
|
||||
} as components['schemas']['Node']
|
||||
]
|
||||
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue(
|
||||
mockInstalledPacks
|
||||
)
|
||||
// Mock Registry Service for compatible package
|
||||
mockRegistryService.getPackByVersion.mockImplementation(
|
||||
(packageName: string) => {
|
||||
const packageData = mockCompatibleRegistryPacks.find(
|
||||
(p) => p.id === packageName
|
||||
)
|
||||
return Promise.resolve(packageData || null)
|
||||
}
|
||||
)
|
||||
|
||||
const { performConflictDetection } = useConflictDetection()
|
||||
const result = await performConflictDetection()
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.summary.conflicted_packages).toBe(0)
|
||||
expect(result.summary.compatible_packages).toBe(1)
|
||||
expect(result.results[0].conflicts).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('should detect OS incompatibility conflicts', async () => {
|
||||
// Mock OS-incompatible package
|
||||
const mockInstalledPacks: ManagerComponents['schemas']['InstalledPacksResponse'] =
|
||||
{
|
||||
WindowsOnlyNode: {
|
||||
ver: '1.0.0',
|
||||
cnr_id: 'windows-only',
|
||||
aux_id: null,
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
const mockWindowsOnlyRegistryPacks: components['schemas']['Node'][] = [
|
||||
{
|
||||
id: 'WindowsOnlyNode',
|
||||
name: 'Windows Only Node',
|
||||
supported_os: ['Windows'],
|
||||
supported_accelerators: ['Metal', 'CUDA', 'CPU'],
|
||||
supported_comfyui_version: '>=0.3.0',
|
||||
status: 'NodeStatusActive'
|
||||
} as components['schemas']['Node']
|
||||
]
|
||||
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue(
|
||||
mockInstalledPacks
|
||||
)
|
||||
mockRegistryService.getPackByVersion.mockImplementation(
|
||||
(packageName: string) => {
|
||||
const packageData = mockWindowsOnlyRegistryPacks.find(
|
||||
(p: any) => p.id === packageName
|
||||
)
|
||||
return Promise.resolve(packageData || null)
|
||||
}
|
||||
)
|
||||
|
||||
const { performConflictDetection } = useConflictDetection()
|
||||
const result = await performConflictDetection()
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.summary.conflicted_packages).toBe(1)
|
||||
|
||||
const windowsNode = result.results[0]
|
||||
expect(windowsNode.conflicts).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'os',
|
||||
severity: 'error',
|
||||
description: expect.stringContaining('Unsupported operating system')
|
||||
})
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it('should detect accelerator incompatibility conflicts', async () => {
|
||||
// Mock CUDA-only package
|
||||
const mockInstalledPacks: ManagerComponents['schemas']['InstalledPacksResponse'] =
|
||||
{
|
||||
CudaOnlyNode: {
|
||||
ver: '1.0.0',
|
||||
cnr_id: 'cuda-only',
|
||||
aux_id: null,
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
const mockCudaOnlyRegistryPacks: components['schemas']['Node'][] = [
|
||||
{
|
||||
id: 'CudaOnlyNode',
|
||||
name: 'CUDA Only Node',
|
||||
supported_os: ['windows', 'linux', 'macos'],
|
||||
supported_accelerators: ['CUDA'],
|
||||
supported_comfyui_version: '>=0.3.0',
|
||||
status: 'NodeStatusActive'
|
||||
} as components['schemas']['Node']
|
||||
]
|
||||
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue(
|
||||
mockInstalledPacks
|
||||
)
|
||||
mockRegistryService.getPackByVersion.mockImplementation(
|
||||
(packageName: string) => {
|
||||
const packageData = mockCudaOnlyRegistryPacks.find(
|
||||
(p: any) => p.id === packageName
|
||||
)
|
||||
return Promise.resolve(packageData || null)
|
||||
}
|
||||
)
|
||||
|
||||
const { performConflictDetection } = useConflictDetection()
|
||||
const result = await performConflictDetection()
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.summary.conflicted_packages).toBe(1)
|
||||
|
||||
const cudaNode = result.results[0]
|
||||
expect(cudaNode.conflicts).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'accelerator',
|
||||
severity: 'error',
|
||||
description: expect.stringContaining(
|
||||
'Required GPU/accelerator not available'
|
||||
)
|
||||
})
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it('should treat Registry-banned packages as conflicts', async () => {
|
||||
// Mock Registry-banned package
|
||||
const mockInstalledPacks: ManagerComponents['schemas']['InstalledPacksResponse'] =
|
||||
{
|
||||
BannedNode: {
|
||||
ver: '1.0.0',
|
||||
cnr_id: 'banned-node',
|
||||
aux_id: null,
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
const mockBannedRegistryPacks: components['schemas']['NodeVersion'][] = [
|
||||
{
|
||||
id: 'BannedNode',
|
||||
supported_os: ['windows', 'linux', 'macos'],
|
||||
supported_accelerators: ['Metal', 'CUDA', 'CPU'],
|
||||
supported_comfyui_version: '>=0.3.0',
|
||||
status: 'NodeVersionStatusBanned'
|
||||
} as components['schemas']['NodeVersion']
|
||||
]
|
||||
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue(
|
||||
mockInstalledPacks
|
||||
)
|
||||
mockRegistryService.getPackByVersion.mockImplementation(
|
||||
(packageName: string) => {
|
||||
const packageData = mockBannedRegistryPacks.find(
|
||||
(p: any) => p.id === packageName
|
||||
)
|
||||
return Promise.resolve(packageData || null)
|
||||
}
|
||||
)
|
||||
|
||||
const { performConflictDetection } = useConflictDetection()
|
||||
const result = await performConflictDetection()
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.summary.banned_packages).toBe(1)
|
||||
|
||||
const bannedNode = result.results[0]
|
||||
expect(bannedNode.conflicts).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'banned',
|
||||
severity: 'error',
|
||||
description: expect.stringContaining('Package is banned')
|
||||
})
|
||||
])
|
||||
)
|
||||
expect(bannedNode.recommended_action.action_type).toBe('disable')
|
||||
})
|
||||
|
||||
it('should treat locally disabled packages as banned', async () => {
|
||||
// Mock locally disabled package
|
||||
const mockInstalledPacks: ManagerComponents['schemas']['InstalledPacksResponse'] =
|
||||
{
|
||||
DisabledNode: {
|
||||
ver: '1.0.0',
|
||||
cnr_id: 'disabled-node',
|
||||
aux_id: null,
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
const mockActiveRegistryPacks: components['schemas']['Node'][] = [
|
||||
{
|
||||
id: 'DisabledNode',
|
||||
name: 'Disabled Node',
|
||||
supported_os: ['windows', 'linux', 'macos'],
|
||||
supported_accelerators: ['Metal', 'CUDA', 'CPU'],
|
||||
supported_comfyui_version: '>=0.3.0',
|
||||
status: 'NodeStatusActive'
|
||||
} as components['schemas']['Node']
|
||||
]
|
||||
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue(
|
||||
mockInstalledPacks
|
||||
)
|
||||
mockRegistryService.getPackByVersion.mockImplementation(
|
||||
(packageName: string) => {
|
||||
const packageData = mockActiveRegistryPacks.find(
|
||||
(p: any) => p.id === packageName
|
||||
)
|
||||
return Promise.resolve(packageData || null)
|
||||
}
|
||||
)
|
||||
|
||||
const { performConflictDetection } = useConflictDetection()
|
||||
const result = await performConflictDetection()
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.summary.banned_packages).toBe(1)
|
||||
|
||||
const disabledNode = result.results[0]
|
||||
expect(disabledNode.conflicts).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'banned',
|
||||
severity: 'error',
|
||||
description: expect.stringContaining('Package is disabled locally')
|
||||
})
|
||||
])
|
||||
)
|
||||
expect(disabledNode.recommended_action.action_type).toBe('disable')
|
||||
})
|
||||
})
|
||||
|
||||
describe('computed properties with Registry Store', () => {
|
||||
it('should return true for hasConflicts when Registry conflicts exist', async () => {
|
||||
// Mock package with OS incompatibility
|
||||
const mockInstalledPacks: ManagerComponents['schemas']['InstalledPacksResponse'] =
|
||||
{
|
||||
ConflictedNode: {
|
||||
ver: '1.0.0',
|
||||
cnr_id: 'conflicted-node',
|
||||
aux_id: null,
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
const mockConflictedRegistryPacks: components['schemas']['Node'][] = [
|
||||
{
|
||||
id: 'ConflictedNode',
|
||||
name: 'Conflicted Node',
|
||||
supported_os: ['Windows'],
|
||||
supported_accelerators: ['Metal', 'CUDA', 'CPU'],
|
||||
supported_comfyui_version: '>=0.3.0',
|
||||
status: 'NodeStatusActive'
|
||||
} as components['schemas']['Node']
|
||||
]
|
||||
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue(
|
||||
mockInstalledPacks
|
||||
)
|
||||
mockRegistryService.getPackByVersion.mockImplementation(
|
||||
(packageName: string) => {
|
||||
const packageData = mockConflictedRegistryPacks.find(
|
||||
(p: any) => p.id === packageName
|
||||
)
|
||||
return Promise.resolve(packageData || null)
|
||||
}
|
||||
)
|
||||
|
||||
const { hasConflicts, performConflictDetection } = useConflictDetection()
|
||||
|
||||
// Initial value should be false
|
||||
expect(hasConflicts.value).toBe(false)
|
||||
|
||||
// Execute conflict detection
|
||||
await performConflictDetection()
|
||||
await nextTick()
|
||||
|
||||
// Should be true when conflicts are detected
|
||||
expect(hasConflicts.value).toBe(true)
|
||||
})
|
||||
|
||||
it('should return only error-level conflicts for criticalConflicts', async () => {
|
||||
// Mock package with error-level conflict
|
||||
const mockInstalledPacks: ManagerComponents['schemas']['InstalledPacksResponse'] =
|
||||
{
|
||||
ErrorNode: {
|
||||
ver: '1.0.0',
|
||||
cnr_id: 'error-node',
|
||||
aux_id: null,
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
const mockErrorRegistryPacks: components['schemas']['Node'][] = [
|
||||
{
|
||||
id: 'ErrorNode',
|
||||
name: 'Error Node',
|
||||
supported_os: ['Windows'],
|
||||
supported_accelerators: ['CUDA'],
|
||||
supported_comfyui_version: '>=0.3.0',
|
||||
status: 'NodeStatusActive'
|
||||
} as components['schemas']['Node']
|
||||
]
|
||||
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue(
|
||||
mockInstalledPacks
|
||||
)
|
||||
mockRegistryService.getPackByVersion.mockImplementation(
|
||||
(packageName: string) => {
|
||||
const packageData = mockErrorRegistryPacks.find(
|
||||
(p: any) => p.id === packageName
|
||||
)
|
||||
return Promise.resolve(packageData || null)
|
||||
}
|
||||
)
|
||||
|
||||
const { criticalConflicts, performConflictDetection } =
|
||||
useConflictDetection()
|
||||
|
||||
await performConflictDetection()
|
||||
await nextTick()
|
||||
|
||||
expect(criticalConflicts.value.length).toBeGreaterThan(0)
|
||||
expect(
|
||||
criticalConflicts.value.every(
|
||||
(conflict) => conflict.severity === 'error'
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('should return only banned packages for bannedPackages', async () => {
|
||||
// Mock one banned and one normal package
|
||||
const mockInstalledPacks: ManagerComponents['schemas']['InstalledPacksResponse'] =
|
||||
{
|
||||
BannedNode: {
|
||||
ver: '1.0.0',
|
||||
cnr_id: 'banned-node',
|
||||
aux_id: null,
|
||||
enabled: false
|
||||
},
|
||||
NormalNode: {
|
||||
ver: '1.0.0',
|
||||
cnr_id: 'normal-node',
|
||||
aux_id: null,
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
const mockRegistryPacks: components['schemas']['Node'][] = [
|
||||
{
|
||||
id: 'BannedNode',
|
||||
name: 'Banned Node',
|
||||
supported_os: ['windows', 'linux', 'macos'],
|
||||
supported_accelerators: ['Metal', 'CUDA', 'CPU'],
|
||||
supported_comfyui_version: '>=0.3.0',
|
||||
status: 'NodeStatusActive'
|
||||
} as components['schemas']['Node'],
|
||||
{
|
||||
id: 'NormalNode',
|
||||
name: 'Normal Node',
|
||||
supported_os: ['windows', 'linux', 'macos'],
|
||||
supported_accelerators: ['Metal', 'CUDA', 'CPU'],
|
||||
supported_comfyui_version: '>=0.3.0',
|
||||
status: 'NodeStatusActive'
|
||||
} as components['schemas']['Node']
|
||||
]
|
||||
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue(
|
||||
mockInstalledPacks
|
||||
)
|
||||
mockRegistryService.getPackByVersion.mockImplementation(
|
||||
(packageName: string) => {
|
||||
const packageData = mockRegistryPacks.find(
|
||||
(p: any) => p.id === packageName
|
||||
)
|
||||
return Promise.resolve(packageData || null)
|
||||
}
|
||||
)
|
||||
|
||||
const { bannedPackages, performConflictDetection } =
|
||||
useConflictDetection()
|
||||
|
||||
await performConflictDetection()
|
||||
await nextTick()
|
||||
|
||||
expect(bannedPackages.value).toHaveLength(1)
|
||||
expect(bannedPackages.value[0].package_id).toBe('BannedNode')
|
||||
})
|
||||
})
|
||||
|
||||
describe('error resilience with Registry Store', () => {
|
||||
it('should continue execution even when system environment detection fails', async () => {
|
||||
// Mock system stats store failure
|
||||
mockSystemStatsStore.fetchSystemStats.mockRejectedValue(
|
||||
new Error('Store error')
|
||||
)
|
||||
mockSystemStatsStore.systemStats = null
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue({})
|
||||
mockRegistryService.getPackByVersion.mockImplementation(
|
||||
(packageName: string) => {
|
||||
const packageData = [].find((p: any) => p.id === packageName)
|
||||
return Promise.resolve(packageData || null)
|
||||
}
|
||||
)
|
||||
|
||||
const { performConflictDetection } = useConflictDetection()
|
||||
const result = await performConflictDetection()
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.detected_system_environment?.comfyui_version).toBe(
|
||||
'unknown'
|
||||
)
|
||||
})
|
||||
|
||||
it('should continue system operation even when local package information fails', async () => {
|
||||
// Mock local package service failure
|
||||
mockComfyManagerService.listInstalledPacks.mockRejectedValue(
|
||||
new Error('Service error')
|
||||
)
|
||||
|
||||
const { performConflictDetection } = useConflictDetection()
|
||||
const result = await performConflictDetection()
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.summary.total_packages).toBe(0)
|
||||
})
|
||||
|
||||
it('should handle Registry Store partial data gracefully', async () => {
|
||||
// Mock successful local data but partial Registry data
|
||||
const mockInstalledPacks: ManagerComponents['schemas']['InstalledPacksResponse'] =
|
||||
{
|
||||
'Package-A': {
|
||||
ver: '1.0.0',
|
||||
cnr_id: 'a',
|
||||
aux_id: null,
|
||||
enabled: true
|
||||
},
|
||||
'Package-B': {
|
||||
ver: '2.0.0',
|
||||
cnr_id: 'b',
|
||||
aux_id: null,
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue(
|
||||
mockInstalledPacks
|
||||
)
|
||||
|
||||
// Only first package found in Registry / Registry에서 첫 번째 패키지만 찾음
|
||||
const mockPartialRegistryPacks: components['schemas']['Node'][] = [
|
||||
{
|
||||
id: 'Package-A',
|
||||
name: 'Package A',
|
||||
supported_os: ['windows', 'linux', 'macos'],
|
||||
supported_accelerators: ['Metal', 'CUDA', 'CPU'],
|
||||
status: 'NodeStatusActive'
|
||||
} as components['schemas']['Node']
|
||||
// Package-B is missing from Registry results
|
||||
]
|
||||
|
||||
mockRegistryService.getPackByVersion.mockImplementation(
|
||||
(packageName: string) => {
|
||||
const packageData = mockPartialRegistryPacks.find(
|
||||
(p: any) => p.id === packageName
|
||||
)
|
||||
return Promise.resolve(packageData || null)
|
||||
}
|
||||
)
|
||||
|
||||
const { performConflictDetection } = useConflictDetection()
|
||||
const result = await performConflictDetection()
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.summary.total_packages).toBe(2)
|
||||
|
||||
// Package A should have Registry data
|
||||
const packageA = result.results.find((r) => r.package_id === 'Package-A')
|
||||
expect(packageA?.conflicts).toHaveLength(0) // No conflicts
|
||||
|
||||
// Package B should have warning about missing Registry data
|
||||
const packageB = result.results.find((r) => r.package_id === 'Package-B')
|
||||
expect(packageB?.conflicts).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'security_pending',
|
||||
severity: 'warning',
|
||||
description: expect.stringContaining('Registry data not available')
|
||||
})
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle complete system failure gracefully', async () => {
|
||||
// Mock all stores/services failing
|
||||
mockSystemStatsStore.fetchSystemStats.mockRejectedValue(
|
||||
new Error('Critical error')
|
||||
)
|
||||
mockSystemStatsStore.systemStats = null
|
||||
mockComfyManagerService.listInstalledPacks.mockRejectedValue(
|
||||
new Error('Critical error')
|
||||
)
|
||||
mockRegistryService.getPackByVersion.mockRejectedValue(
|
||||
new Error('Critical error')
|
||||
)
|
||||
|
||||
const { performConflictDetection } = useConflictDetection()
|
||||
const result = await performConflictDetection()
|
||||
|
||||
expect(result.success).toBe(true) // Error resilience maintains success
|
||||
expect(result.summary.total_packages).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('initialization', () => {
|
||||
it('should execute initializeConflictDetection without errors', async () => {
|
||||
mockComfyManagerService.listInstalledPacks.mockResolvedValue({})
|
||||
|
||||
const { initializeConflictDetection } = useConflictDetection()
|
||||
|
||||
expect(() => {
|
||||
void initializeConflictDetection()
|
||||
}).not.toThrow()
|
||||
})
|
||||
|
||||
it('should set initial state values correctly', () => {
|
||||
const {
|
||||
isDetecting,
|
||||
lastDetectionTime,
|
||||
detectionError,
|
||||
systemEnvironment,
|
||||
detectionResults,
|
||||
detectionSummary
|
||||
} = useConflictDetection()
|
||||
|
||||
expect(isDetecting.value).toBe(false)
|
||||
expect(lastDetectionTime.value).toBeNull()
|
||||
expect(detectionError.value).toBeNull()
|
||||
expect(systemEnvironment.value).toBeNull()
|
||||
expect(detectionResults.value).toEqual([])
|
||||
expect(detectionSummary.value).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
129
tests-ui/tests/stores/nodeCompatibilityStore.test.ts
Normal file
129
tests-ui/tests/stores/nodeCompatibilityStore.test.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import { useNodeCompatibilityStore } from '@/stores/nodeCompatibilityStore'
|
||||
|
||||
describe('useNodeCompatibilityStore', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
})
|
||||
|
||||
it('should initialize with empty state', () => {
|
||||
const store = useNodeCompatibilityStore()
|
||||
|
||||
expect(store.isChecking).toBe(false)
|
||||
expect(store.lastCheckTime).toBeNull()
|
||||
expect(store.checkError).toBeNull()
|
||||
expect(store.hasIncompatibleNodes).toBe(false)
|
||||
expect(store.totalIncompatibleCount).toBe(0)
|
||||
expect(store.shouldShowNotification).toBe(false)
|
||||
})
|
||||
|
||||
it('should add incompatible nodes correctly', () => {
|
||||
const store = useNodeCompatibilityStore()
|
||||
|
||||
store.addIncompatibleNode(
|
||||
'test-node',
|
||||
'Test Node',
|
||||
'banned',
|
||||
'Node is banned for testing'
|
||||
)
|
||||
|
||||
expect(store.hasIncompatibleNodes).toBe(true)
|
||||
expect(store.totalIncompatibleCount).toBe(1)
|
||||
expect(store.hasNodeCompatibilityIssues('test-node')).toBe(true)
|
||||
|
||||
const compatibilityInfo = store.getNodeCompatibilityInfo('test-node')
|
||||
expect(compatibilityInfo).toBeDefined()
|
||||
expect(compatibilityInfo?.disableReason).toBe('banned')
|
||||
})
|
||||
|
||||
it('should remove incompatible nodes correctly', () => {
|
||||
const store = useNodeCompatibilityStore()
|
||||
|
||||
store.addIncompatibleNode(
|
||||
'test-node',
|
||||
'Test Node',
|
||||
'banned',
|
||||
'Node is banned for testing'
|
||||
)
|
||||
|
||||
expect(store.hasIncompatibleNodes).toBe(true)
|
||||
|
||||
store.removeIncompatibleNode('test-node')
|
||||
|
||||
expect(store.hasIncompatibleNodes).toBe(false)
|
||||
expect(store.hasNodeCompatibilityIssues('test-node')).toBe(false)
|
||||
})
|
||||
|
||||
it('should handle notification modal state correctly', () => {
|
||||
const store = useNodeCompatibilityStore()
|
||||
|
||||
// Add an incompatible node
|
||||
store.addIncompatibleNode(
|
||||
'test-node',
|
||||
'Test Node',
|
||||
'banned',
|
||||
'Node is banned for testing'
|
||||
)
|
||||
|
||||
expect(store.shouldShowNotification).toBe(true)
|
||||
expect(store.pendingNotificationNodes).toHaveLength(1)
|
||||
|
||||
store.markNotificationModalShown()
|
||||
|
||||
expect(store.shouldShowNotification).toBe(false)
|
||||
expect(store.pendingNotificationNodes).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('should clear all results correctly', () => {
|
||||
const store = useNodeCompatibilityStore()
|
||||
|
||||
store.addIncompatibleNode(
|
||||
'test-node',
|
||||
'Test Node',
|
||||
'banned',
|
||||
'Node is banned for testing'
|
||||
)
|
||||
store.recordCheckError('Test error')
|
||||
|
||||
expect(store.hasIncompatibleNodes).toBe(true)
|
||||
expect(store.checkError).toBe('Test error')
|
||||
|
||||
store.clearResults()
|
||||
|
||||
expect(store.hasIncompatibleNodes).toBe(false)
|
||||
expect(store.checkError).toBeNull()
|
||||
})
|
||||
|
||||
it('should track checking state correctly', () => {
|
||||
const store = useNodeCompatibilityStore()
|
||||
|
||||
expect(store.isChecking).toBe(false)
|
||||
|
||||
store.setCheckingState(true)
|
||||
expect(store.isChecking).toBe(true)
|
||||
|
||||
store.recordCheckCompletion()
|
||||
expect(store.isChecking).toBe(false)
|
||||
expect(store.lastCheckTime).toBeDefined()
|
||||
})
|
||||
|
||||
it('should provide compatibility summary', () => {
|
||||
const store = useNodeCompatibilityStore()
|
||||
|
||||
store.addIncompatibleNode(
|
||||
'banned-node',
|
||||
'Banned Node',
|
||||
'banned',
|
||||
'Node is banned'
|
||||
)
|
||||
|
||||
const summary = store.getCompatibilitySummary()
|
||||
|
||||
expect(summary.incompatibleCount).toBe(1)
|
||||
expect(summary.bannedCount).toBe(0) // bannedNodes is separate from incompatibleNodes
|
||||
expect(summary.totalIssues).toBe(1)
|
||||
expect(summary.hasError).toBe(false)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user