diff --git a/.git2gus/config.json b/.git2gus/config.json index 803948b7..a05c2865 100644 --- a/.git2gus/config.json +++ b/.git2gus/config.json @@ -11,5 +11,6 @@ "type:security": "BUG P0", "type:feedback": "", "type:duplicate": "" - } + }, + "statusWhenClosed": "CLOSED" } diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..3867a0fe --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npm run lint diff --git a/SHA256.md b/SHA256.md index b59161f0..dbcaaae4 100644 --- a/SHA256.md +++ b/SHA256.md @@ -15,7 +15,7 @@ make sure that their SHA values match the values in the list below. shasum -a 256 3. Confirm that the SHA in your output matches the value in this list of SHAs. - a26fa56962cc53dfd3deeb0a32f184218e71b0110eb96b1a56966d363538fee6 ./extensions/sfdx-code-analyzer-vscode-1.9.0.vsix + 92d8b8bf23aec05328349ceb9b471fd004fe3d530f584b066d56cca6bf41c009 ./extensions/sfdx-code-analyzer-vscode-1.10.0.vsix 4. Change the filename extension for the file that you downloaded from .zip to .vsix. diff --git a/package-lock.json b/package-lock.json index 089514c6..0e60d943 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,48 +1,50 @@ { "name": "sfdx-code-analyzer-vscode", - "version": "1.10.0", + "version": "1.11.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sfdx-code-analyzer-vscode", - "version": "1.10.0", + "version": "1.11.0", "license": "BSD-3-Clause", "dependencies": { "@salesforce/vscode-service-provider": "^1.5.0", "@types/jest": "^30.0.0", - "@types/semver": "^7.7.0", + "@types/semver": "^7.7.1", "@types/tmp": "^0.2.6", "diff": "^5.2.0", "glob": "^11.0.3", "semver": "^7.7.2", - "tmp": "^0.2.4" + "tmp": "^0.2.5" }, "devDependencies": { - "@eslint/js": "^9.32.0", + "@eslint/js": "^9.35.0", "@types/chai": "^4.3.20", "@types/diff": "^5.2.3", "@types/mocha": "^10.0.10", - "@types/node": "^22.16.4", + "@types/node": "^22.18.5", "@types/sinon": "^10.0.20", "@types/vscode": "^1.90.0", "@vscode/test-cli": "^0.0.11", "@vscode/test-electron": "^2.5.2", "@vscode/vsce": "^3.6.0", "chai": "^4.5.0", - "esbuild": "^0.25.8", - "eslint": "^9.32.0", - "jest": "^30.0.5", - "jest-mock-vscode": "^4.6.0", + "esbuild": "^0.25.9", + "eslint": "^9.35.0", + "husky": "^9.1.7", + "jest": "^30.1.3", + "jest-mock-vscode": "^4.7.0", "mocha": "^10.8.2", + "npm-run-all": "^4.1.5", "ovsx": "^0.10.5", "proxyquire": "^2.1.3", "rimraf": "*", "sinon": "^15.2.0", - "ts-jest": "^29.4.1", + "ts-jest": "^29.4.2", "ts-node": "^10.9.2", "typescript": "^5.9.2", - "typescript-eslint": "^8.39.0" + "typescript-eslint": "^8.44.0" }, "engines": { "node": ">=20.9.0", @@ -839,9 +841,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", - "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ "ppc64" ], @@ -856,9 +858,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", - "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ "arm" ], @@ -873,9 +875,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", - "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], @@ -890,9 +892,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", - "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], @@ -907,9 +909,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", - "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], @@ -924,9 +926,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", - "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], @@ -941,9 +943,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", - "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], @@ -958,9 +960,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", - "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], @@ -975,9 +977,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", - "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], @@ -992,9 +994,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", - "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], @@ -1009,9 +1011,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", - "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], @@ -1026,9 +1028,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", - "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], @@ -1043,9 +1045,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", - "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], @@ -1060,9 +1062,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", - "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], @@ -1077,9 +1079,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", - "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], @@ -1094,9 +1096,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", - "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], @@ -1111,9 +1113,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", - "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], @@ -1128,9 +1130,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", - "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "cpu": [ "arm64" ], @@ -1145,9 +1147,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", - "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], @@ -1162,9 +1164,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", - "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "cpu": [ "arm64" ], @@ -1179,9 +1181,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", - "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], @@ -1196,9 +1198,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", - "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", "cpu": [ "arm64" ], @@ -1213,9 +1215,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", - "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], @@ -1230,9 +1232,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", - "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], @@ -1247,9 +1249,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", - "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], @@ -1264,9 +1266,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", - "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], @@ -1281,9 +1283,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { @@ -1362,9 +1364,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1372,9 +1374,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1477,9 +1479,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", - "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", + "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", "dev": true, "license": "MIT", "engines": { @@ -1500,13 +1502,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -1734,16 +1736,16 @@ } }, "node_modules/@jest/console": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.0.5.tgz", - "integrity": "sha512-xY6b0XiL0Nav3ReresUarwl2oIz1gTnxGbGpho9/rbUWsLH0f1OD/VT84xs8c7VmH7MChnLb0pag6PhZhAdDiA==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.1.2.tgz", + "integrity": "sha512-BGMAxj8VRmoD0MoA/jo9alMXSRoqW8KPeqOfEo1ncxnRLatTBCpRoOwlwlEMdudp68Q6WSGwYrrLtTGOh8fLzw==", "dev": true, "license": "MIT", "dependencies": { "@jest/types": "30.0.5", "@types/node": "*", "chalk": "^4.1.2", - "jest-message-util": "30.0.5", + "jest-message-util": "30.1.0", "jest-util": "30.0.5", "slash": "^3.0.0" }, @@ -1752,17 +1754,17 @@ } }, "node_modules/@jest/core": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.0.5.tgz", - "integrity": "sha512-fKD0OulvRsXF1hmaFgHhVJzczWzA1RXMMo9LTPuFXo9q/alDbME3JIyWYqovWsUBWSoBcsHaGPSLF9rz4l9Qeg==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.1.3.tgz", + "integrity": "sha512-LIQz7NEDDO1+eyOA2ZmkiAyYvZuo6s1UxD/e2IHldR6D7UYogVq3arTmli07MkENLq6/3JEQjp0mA8rrHHJ8KQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.0.5", + "@jest/console": "30.1.2", "@jest/pattern": "30.0.1", - "@jest/reporters": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/transform": "30.0.5", + "@jest/reporters": "30.1.3", + "@jest/test-result": "30.1.3", + "@jest/transform": "30.1.2", "@jest/types": "30.0.5", "@types/node": "*", "ansi-escapes": "^4.3.2", @@ -1771,18 +1773,18 @@ "exit-x": "^0.2.2", "graceful-fs": "^4.2.11", "jest-changed-files": "30.0.5", - "jest-config": "30.0.5", - "jest-haste-map": "30.0.5", - "jest-message-util": "30.0.5", + "jest-config": "30.1.3", + "jest-haste-map": "30.1.0", + "jest-message-util": "30.1.0", "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.5", - "jest-resolve-dependencies": "30.0.5", - "jest-runner": "30.0.5", - "jest-runtime": "30.0.5", - "jest-snapshot": "30.0.5", + "jest-resolve": "30.1.3", + "jest-resolve-dependencies": "30.1.3", + "jest-runner": "30.1.3", + "jest-runtime": "30.1.3", + "jest-snapshot": "30.1.2", "jest-util": "30.0.5", - "jest-validate": "30.0.5", - "jest-watcher": "30.0.5", + "jest-validate": "30.1.0", + "jest-watcher": "30.1.3", "micromatch": "^4.0.8", "pretty-format": "30.0.5", "slash": "^3.0.0" @@ -1809,13 +1811,13 @@ } }, "node_modules/@jest/environment": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.5.tgz", - "integrity": "sha512-aRX7WoaWx1oaOkDQvCWImVQ8XNtdv5sEWgk4gxR6NXb7WBUnL5sRak4WRzIQRZ1VTWPvV4VI4mgGjNL9TeKMYA==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.1.2.tgz", + "integrity": "sha512-N8t1Ytw4/mr9uN28OnVf0SYE2dGhaIxOVYcwsf9IInBKjvofAjbFRvedvBBlyTYk2knbJTiEjEJ2PyyDIBnd9w==", "dev": true, "license": "MIT", "dependencies": { - "@jest/fake-timers": "30.0.5", + "@jest/fake-timers": "30.1.2", "@jest/types": "30.0.5", "@types/node": "*", "jest-mock": "30.0.5" @@ -1825,42 +1827,42 @@ } }, "node_modules/@jest/expect": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.0.5.tgz", - "integrity": "sha512-6udac8KKrtTtC+AXZ2iUN/R7dp7Ydry+Fo6FPFnDG54wjVMnb6vW/XNlf7Xj8UDjAE3aAVAsR4KFyKk3TCXmTA==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.1.2.tgz", + "integrity": "sha512-tyaIExOwQRCxPCGNC05lIjWJztDwk2gPDNSDGg1zitXJJ8dC3++G/CRjE5mb2wQsf89+lsgAgqxxNpDLiCViTA==", "dev": true, "license": "MIT", "dependencies": { - "expect": "30.0.5", - "jest-snapshot": "30.0.5" + "expect": "30.1.2", + "jest-snapshot": "30.1.2" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.5.tgz", - "integrity": "sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.1.2.tgz", + "integrity": "sha512-HXy1qT/bfdjCv7iC336ExbqqYtZvljrV8odNdso7dWK9bSeHtLlvwWWC3YSybSPL03Gg5rug6WLCZAZFH72m0A==", "license": "MIT", "dependencies": { - "@jest/get-type": "30.0.1" + "@jest/get-type": "30.1.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/fake-timers": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.5.tgz", - "integrity": "sha512-ZO5DHfNV+kgEAeP3gK3XlpJLL4U3Sz6ebl/n68Uwt64qFFs5bv4bfEEjyRGK5uM0C90ewooNgFuKMdkbEoMEXw==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.1.2.tgz", + "integrity": "sha512-Beljfv9AYkr9K+ETX9tvV61rJTY706BhBUtiaepQHeEGfe0DbpvUA5Z3fomwc5Xkhns6NWrcFDZn+72fLieUnA==", "dev": true, "license": "MIT", "dependencies": { "@jest/types": "30.0.5", "@sinonjs/fake-timers": "^13.0.0", "@types/node": "*", - "jest-message-util": "30.0.5", + "jest-message-util": "30.1.0", "jest-mock": "30.0.5", "jest-util": "30.0.5" }, @@ -1869,23 +1871,23 @@ } }, "node_modules/@jest/get-type": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.1.tgz", - "integrity": "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==", + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", "license": "MIT", "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/globals": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.0.5.tgz", - "integrity": "sha512-7oEJT19WW4oe6HR7oLRvHxwlJk2gev0U9px3ufs8sX9PoD1Eza68KF0/tlN7X0dq/WVsBScXQGgCldA1V9Y/jA==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.1.2.tgz", + "integrity": "sha512-teNTPZ8yZe3ahbYnvnVRDeOjr+3pu2uiAtNtrEsiMjVPPj+cXd5E/fr8BL7v/T7F31vYdEHrI5cC/2OoO/vM9A==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.0.5", - "@jest/expect": "30.0.5", + "@jest/environment": "30.1.2", + "@jest/expect": "30.1.2", "@jest/types": "30.0.5", "jest-mock": "30.0.5" }, @@ -1907,16 +1909,16 @@ } }, "node_modules/@jest/reporters": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.0.5.tgz", - "integrity": "sha512-mafft7VBX4jzED1FwGC1o/9QUM2xebzavImZMeqnsklgcyxBto8mV4HzNSzUrryJ+8R9MFOM3HgYuDradWR+4g==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.1.3.tgz", + "integrity": "sha512-VWEQmJWfXMOrzdFEOyGjUEOuVXllgZsoPtEHZzfdNz18RmzJ5nlR6kp8hDdY8dDS1yGOXAY7DHT+AOHIPSBV0w==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/transform": "30.0.5", + "@jest/console": "30.1.2", + "@jest/test-result": "30.1.3", + "@jest/transform": "30.1.2", "@jest/types": "30.0.5", "@jridgewell/trace-mapping": "^0.3.25", "@types/node": "*", @@ -1930,9 +1932,9 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^5.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "30.0.5", + "jest-message-util": "30.1.0", "jest-util": "30.0.5", - "jest-worker": "30.0.5", + "jest-worker": "30.1.0", "slash": "^3.0.0", "string-length": "^4.0.2", "v8-to-istanbul": "^9.0.1" @@ -2023,9 +2025,9 @@ } }, "node_modules/@jest/snapshot-utils": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.0.5.tgz", - "integrity": "sha512-XcCQ5qWHLvi29UUrowgDFvV4t7ETxX91CbDczMnoqXPOIcZOxyNdSjm6kV5XMc8+HkxfRegU/MUmnTbJRzGrUQ==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.1.2.tgz", + "integrity": "sha512-vHoMTpimcPSR7OxS2S0V1Cpg8eKDRxucHjoWl5u4RQcnxqQrV3avETiFpl8etn4dqxEGarBeHbIBety/f8mLXw==", "dev": true, "license": "MIT", "dependencies": { @@ -2054,13 +2056,13 @@ } }, "node_modules/@jest/test-result": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.0.5.tgz", - "integrity": "sha512-wPyztnK0gbDMQAJZ43tdMro+qblDHH1Ru/ylzUo21TBKqt88ZqnKKK2m30LKmLLoKtR2lxdpCC/P3g1vfKcawQ==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.1.3.tgz", + "integrity": "sha512-P9IV8T24D43cNRANPPokn7tZh0FAFnYS2HIfi5vK18CjRkTDR9Y3e1BoEcAJnl4ghZZF4Ecda4M/k41QkvurEQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.0.5", + "@jest/console": "30.1.2", "@jest/types": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "collect-v8-coverage": "^1.0.2" @@ -2070,15 +2072,15 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.0.5.tgz", - "integrity": "sha512-Aea/G1egWoIIozmDD7PBXUOxkekXl7ueGzrsGGi1SbeKgQqCYCIf+wfbflEbf2LiPxL8j2JZGLyrzZagjvW4YQ==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.1.3.tgz", + "integrity": "sha512-82J+hzC0qeQIiiZDThh+YUadvshdBswi5nuyXlEmXzrhw5ZQSRHeQ5LpVMD/xc8B3wPePvs6VMzHnntxL+4E3w==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.0.5", + "@jest/test-result": "30.1.3", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.5", + "jest-haste-map": "30.1.0", "slash": "^3.0.0" }, "engines": { @@ -2086,9 +2088,9 @@ } }, "node_modules/@jest/transform": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.5.tgz", - "integrity": "sha512-Vk8amLQCmuZyy6GbBht1Jfo9RSdBtg7Lks+B0PecnjI8J+PCLQPGh7uI8Q/2wwpW2gLdiAfiHNsmekKlywULqg==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.1.2.tgz", + "integrity": "sha512-UYYFGifSgfjujf1Cbd3iU/IQoSd6uwsj8XHj5DSDf5ERDcWMdJOPTkHWXj4U+Z/uMagyOQZ6Vne8C4nRIrCxqA==", "dev": true, "license": "MIT", "dependencies": { @@ -2100,7 +2102,7 @@ "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.5", + "jest-haste-map": "30.1.0", "jest-regex-util": "30.0.1", "jest-util": "30.0.5", "micromatch": "^4.0.8", @@ -3000,9 +3002,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.17.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz", - "integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==", + "version": "22.18.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.5.tgz", + "integrity": "sha512-g9BpPfJvxYBXUWI9bV37j6d6LTMNQ88hPwdWWUeYZnMhlo66FIg9gCc1/DZb15QylJSKwOZjwrckvOTWpOiChg==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -3023,9 +3025,9 @@ "license": "MIT" }, "node_modules/@types/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", "license": "MIT" }, "node_modules/@types/sinon": { @@ -3058,9 +3060,9 @@ "license": "MIT" }, "node_modules/@types/vscode": { - "version": "1.102.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.102.0.tgz", - "integrity": "sha512-V9sFXmcXz03FtYTSUsYsu5K0Q9wH9w9V25slddcxrh5JgORD14LpnOA7ov0L9ALi+6HrTjskLJ/tY5zeRF3TFA==", + "version": "1.104.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.104.0.tgz", + "integrity": "sha512-0KwoU2rZ2ecsTGFxo4K1+f+AErRsYW0fsp6A0zufzGuhyczc2IoKqYqcwXidKXmy2u8YB2GsYsOtiI9Izx3Tig==", "dev": true, "license": "MIT" }, @@ -3080,17 +3082,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz", - "integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.0.tgz", + "integrity": "sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.39.0", - "@typescript-eslint/type-utils": "8.39.0", - "@typescript-eslint/utils": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0", + "@typescript-eslint/scope-manager": "8.44.0", + "@typescript-eslint/type-utils": "8.44.0", + "@typescript-eslint/utils": "8.44.0", + "@typescript-eslint/visitor-keys": "8.44.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3104,7 +3106,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.39.0", + "@typescript-eslint/parser": "^8.44.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -3120,16 +3122,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz", - "integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.0.tgz", + "integrity": "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/typescript-estree": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0", + "@typescript-eslint/scope-manager": "8.44.0", + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/typescript-estree": "8.44.0", + "@typescript-eslint/visitor-keys": "8.44.0", "debug": "^4.3.4" }, "engines": { @@ -3145,14 +3147,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", - "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.0.tgz", + "integrity": "sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.39.0", - "@typescript-eslint/types": "^8.39.0", + "@typescript-eslint/tsconfig-utils": "^8.44.0", + "@typescript-eslint/types": "^8.44.0", "debug": "^4.3.4" }, "engines": { @@ -3167,14 +3169,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", - "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.0.tgz", + "integrity": "sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0" + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/visitor-keys": "8.44.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3185,9 +3187,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", - "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.0.tgz", + "integrity": "sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==", "dev": true, "license": "MIT", "engines": { @@ -3202,15 +3204,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz", - "integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.0.tgz", + "integrity": "sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/typescript-estree": "8.39.0", - "@typescript-eslint/utils": "8.39.0", + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/typescript-estree": "8.44.0", + "@typescript-eslint/utils": "8.44.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3227,9 +3229,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", - "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.0.tgz", + "integrity": "sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==", "dev": true, "license": "MIT", "engines": { @@ -3241,16 +3243,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", - "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.0.tgz", + "integrity": "sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.39.0", - "@typescript-eslint/tsconfig-utils": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/visitor-keys": "8.39.0", + "@typescript-eslint/project-service": "8.44.0", + "@typescript-eslint/tsconfig-utils": "8.44.0", + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/visitor-keys": "8.44.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3270,16 +3272,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz", - "integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.0.tgz", + "integrity": "sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.39.0", - "@typescript-eslint/types": "8.39.0", - "@typescript-eslint/typescript-estree": "8.39.0" + "@typescript-eslint/scope-manager": "8.44.0", + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/typescript-estree": "8.44.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3294,13 +3296,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", - "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.0.tgz", + "integrity": "sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/types": "8.44.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -4186,6 +4188,45 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -4206,6 +4247,16 @@ "node": ">=8" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4213,6 +4264,22 @@ "dev": true, "license": "MIT" }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/azure-devops-node-api": { "version": "12.5.0", "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", @@ -4225,13 +4292,13 @@ } }, "node_modules/babel-jest": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.5.tgz", - "integrity": "sha512-mRijnKimhGDMsizTvBTWotwNpzrkHr+VvZUQBof2AufXKB8NXrL1W69TG20EvOz7aevx6FTJIaBuBkYxS8zolg==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.1.2.tgz", + "integrity": "sha512-IQCus1rt9kaSh7PQxLYRY5NmkNrNlU2TpabzwV7T2jljnpdHOcmnYYv8QmE04Li4S3a2Lj8/yXyET5pBarPr6g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/transform": "30.0.5", + "@jest/transform": "30.1.2", "@types/babel__core": "^7.20.5", "babel-plugin-istanbul": "^7.0.0", "babel-preset-jest": "30.0.1", @@ -4600,6 +4667,25 @@ "node": ">=14.14.0" } }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -5067,6 +5153,60 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -5116,9 +5256,9 @@ } }, "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", + "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5497,6 +5637,75 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -5546,10 +5755,28 @@ "node": ">= 0.4" } }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/esbuild": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", - "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5560,32 +5787,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.8", - "@esbuild/android-arm": "0.25.8", - "@esbuild/android-arm64": "0.25.8", - "@esbuild/android-x64": "0.25.8", - "@esbuild/darwin-arm64": "0.25.8", - "@esbuild/darwin-x64": "0.25.8", - "@esbuild/freebsd-arm64": "0.25.8", - "@esbuild/freebsd-x64": "0.25.8", - "@esbuild/linux-arm": "0.25.8", - "@esbuild/linux-arm64": "0.25.8", - "@esbuild/linux-ia32": "0.25.8", - "@esbuild/linux-loong64": "0.25.8", - "@esbuild/linux-mips64el": "0.25.8", - "@esbuild/linux-ppc64": "0.25.8", - "@esbuild/linux-riscv64": "0.25.8", - "@esbuild/linux-s390x": "0.25.8", - "@esbuild/linux-x64": "0.25.8", - "@esbuild/netbsd-arm64": "0.25.8", - "@esbuild/netbsd-x64": "0.25.8", - "@esbuild/openbsd-arm64": "0.25.8", - "@esbuild/openbsd-x64": "0.25.8", - "@esbuild/openharmony-arm64": "0.25.8", - "@esbuild/sunos-x64": "0.25.8", - "@esbuild/win32-arm64": "0.25.8", - "@esbuild/win32-ia32": "0.25.8", - "@esbuild/win32-x64": "0.25.8" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/escalade": { @@ -5612,20 +5839,20 @@ } }, "node_modules/eslint": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", - "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", + "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.32.0", - "@eslint/plugin-kit": "^0.3.4", + "@eslint/js": "9.35.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -5894,15 +6121,15 @@ } }, "node_modules/expect": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.5.tgz", - "integrity": "sha512-P0te2pt+hHI5qLJkIR+iMvS+lYUZml8rKKsohVHAGY+uClp9XVbdyYNJOIjSRpHVp8s8YqxJCiHUkSYZGr8rtQ==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.1.2.tgz", + "integrity": "sha512-xvHszRavo28ejws8FpemjhwswGj4w/BetHIL8cU49u4sGyXDw2+p3YbeDbj6xzlxi6kWTjIRSTJ+9sNXPnF0Zg==", "license": "MIT", "dependencies": { - "@jest/expect-utils": "30.0.5", - "@jest/get-type": "30.0.1", - "jest-matcher-utils": "30.0.5", - "jest-message-util": "30.0.5", + "@jest/expect-utils": "30.1.2", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.1.2", + "jest-message-util": "30.1.0", "jest-mock": "30.0.5", "jest-util": "30.0.5" }, @@ -6103,6 +6330,22 @@ } } }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -6191,6 +6434,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -6296,6 +6570,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -6477,6 +6769,19 @@ "uglify-js": "^3.1.4" } }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -6499,6 +6804,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -6642,6 +6963,22 @@ "node": ">=10.17.0" } }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -6781,40 +7118,139 @@ "license": "ISC", "optional": true }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "license": "MIT", "dependencies": { - "binary-extensions": "^2.0.0" + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "license": "MIT", "dependencies": { - "ci-info": "^2.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, - "bin": { - "is-ci": "bin.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-ci/node_modules/ci-info": { + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-ci/node_modules/ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", @@ -6837,6 +7273,41 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-docker": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", @@ -6863,6 +7334,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -6882,6 +7369,25 @@ "node": ">=6" } }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -6940,6 +7446,32 @@ "node": ">=12" } }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -6949,6 +7481,23 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", @@ -6969,6 +7518,54 @@ "node": ">=8" } }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -6982,6 +7579,57 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -6995,6 +7643,52 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-wsl": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", @@ -7142,16 +7836,16 @@ } }, "node_modules/jest": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.0.5.tgz", - "integrity": "sha512-y2mfcJywuTUkvLm2Lp1/pFX8kTgMO5yyQGq/Sk/n2mN7XWYp4JsCZ/QXW34M8YScgk8bPZlREH04f6blPnoHnQ==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.1.3.tgz", + "integrity": "sha512-Ry+p2+NLk6u8Agh5yVqELfUJvRfV51hhVBRIB5yZPY7mU0DGBmOuFG5GebZbMbm86cdQNK0fhJuDX8/1YorISQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.0.5", + "@jest/core": "30.1.3", "@jest/types": "30.0.5", "import-local": "^3.2.0", - "jest-cli": "30.0.5" + "jest-cli": "30.1.3" }, "bin": { "jest": "bin/jest.js" @@ -7184,26 +7878,26 @@ } }, "node_modules/jest-circus": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.0.5.tgz", - "integrity": "sha512-h/sjXEs4GS+NFFfqBDYT7y5Msfxh04EwWLhQi0F8kuWpe+J/7tICSlswU8qvBqumR3kFgHbfu7vU6qruWWBPug==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.1.3.tgz", + "integrity": "sha512-Yf3dnhRON2GJT4RYzM89t/EXIWNxKTpWTL9BfF3+geFetWP4XSvJjiU1vrWplOiUkmq8cHLiwuhz+XuUp9DscA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.0.5", - "@jest/expect": "30.0.5", - "@jest/test-result": "30.0.5", + "@jest/environment": "30.1.2", + "@jest/expect": "30.1.2", + "@jest/test-result": "30.1.3", "@jest/types": "30.0.5", "@types/node": "*", "chalk": "^4.1.2", "co": "^4.6.0", "dedent": "^1.6.0", "is-generator-fn": "^2.1.0", - "jest-each": "30.0.5", - "jest-matcher-utils": "30.0.5", - "jest-message-util": "30.0.5", - "jest-runtime": "30.0.5", - "jest-snapshot": "30.0.5", + "jest-each": "30.1.0", + "jest-matcher-utils": "30.1.2", + "jest-message-util": "30.1.0", + "jest-runtime": "30.1.3", + "jest-snapshot": "30.1.2", "jest-util": "30.0.5", "p-limit": "^3.1.0", "pretty-format": "30.0.5", @@ -7216,21 +7910,21 @@ } }, "node_modules/jest-cli": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.0.5.tgz", - "integrity": "sha512-Sa45PGMkBZzF94HMrlX4kUyPOwUpdZasaliKN3mifvDmkhLYqLLg8HQTzn6gq7vJGahFYMQjXgyJWfYImKZzOw==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.1.3.tgz", + "integrity": "sha512-G8E2Ol3OKch1DEeIBl41NP7OiC6LBhfg25Btv+idcusmoUSpqUkbrneMqbW9lVpI/rCKb/uETidb7DNteheuAQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.0.5", - "@jest/test-result": "30.0.5", + "@jest/core": "30.1.3", + "@jest/test-result": "30.1.3", "@jest/types": "30.0.5", "chalk": "^4.1.2", "exit-x": "^0.2.2", "import-local": "^3.2.0", - "jest-config": "30.0.5", + "jest-config": "30.1.3", "jest-util": "30.0.5", - "jest-validate": "30.0.5", + "jest-validate": "30.1.0", "yargs": "^17.7.2" }, "bin": { @@ -7249,31 +7943,31 @@ } }, "node_modules/jest-config": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.0.5.tgz", - "integrity": "sha512-aIVh+JNOOpzUgzUnPn5FLtyVnqc3TQHVMupYtyeURSb//iLColiMIR8TxCIDKyx9ZgjKnXGucuW68hCxgbrwmA==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.1.3.tgz", + "integrity": "sha512-M/f7gqdQEPgZNA181Myz+GXCe8jXcJsGjCMXUzRj22FIXsZOyHNte84e0exntOvdPaeh9tA0w+B8qlP2fAezfw==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.27.4", - "@jest/get-type": "30.0.1", + "@jest/get-type": "30.1.0", "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.0.5", + "@jest/test-sequencer": "30.1.3", "@jest/types": "30.0.5", - "babel-jest": "30.0.5", + "babel-jest": "30.1.2", "chalk": "^4.1.2", "ci-info": "^4.2.0", "deepmerge": "^4.3.1", "glob": "^10.3.10", "graceful-fs": "^4.2.11", - "jest-circus": "30.0.5", + "jest-circus": "30.1.3", "jest-docblock": "30.0.1", - "jest-environment-node": "30.0.5", + "jest-environment-node": "30.1.2", "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.5", - "jest-runner": "30.0.5", + "jest-resolve": "30.1.3", + "jest-runner": "30.1.3", "jest-util": "30.0.5", - "jest-validate": "30.0.5", + "jest-validate": "30.1.0", "micromatch": "^4.0.8", "parse-json": "^5.2.0", "pretty-format": "30.0.5", @@ -7362,13 +8056,13 @@ } }, "node_modules/jest-diff": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.5.tgz", - "integrity": "sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.1.2.tgz", + "integrity": "sha512-4+prq+9J61mOVXCa4Qp8ZjavdxzrWQXrI80GNxP8f4tkI2syPuPrJgdRPZRrfUTRvIoUwcmNLbqEJy9W800+NQ==", "license": "MIT", "dependencies": { "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.0.1", + "@jest/get-type": "30.1.0", "chalk": "^4.1.2", "pretty-format": "30.0.5" }, @@ -7390,13 +8084,13 @@ } }, "node_modules/jest-each": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.0.5.tgz", - "integrity": "sha512-dKjRsx1uZ96TVyejD3/aAWcNKy6ajMaN531CwWIsrazIqIoXI9TnnpPlkrEYku/8rkS3dh2rbH+kMOyiEIv0xQ==", + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.1.0.tgz", + "integrity": "sha512-A+9FKzxPluqogNahpCv04UJvcZ9B3HamqpDNWNKDjtxVRYB8xbZLFuCr8JAJFpNp83CA0anGQFlpQna9Me+/tQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.0.1", + "@jest/get-type": "30.1.0", "@jest/types": "30.0.5", "chalk": "^4.1.2", "jest-util": "30.0.5", @@ -7407,28 +8101,28 @@ } }, "node_modules/jest-environment-node": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.0.5.tgz", - "integrity": "sha512-ppYizXdLMSvciGsRsMEnv/5EFpvOdXBaXRBzFUDPWrsfmog4kYrOGWXarLllz6AXan6ZAA/kYokgDWuos1IKDA==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.1.2.tgz", + "integrity": "sha512-w8qBiXtqGWJ9xpJIA98M0EIoq079GOQRQUyse5qg1plShUCQ0Ek1VTTcczqKrn3f24TFAgFtT+4q3aOXvjbsuA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.0.5", - "@jest/fake-timers": "30.0.5", + "@jest/environment": "30.1.2", + "@jest/fake-timers": "30.1.2", "@jest/types": "30.0.5", "@types/node": "*", "jest-mock": "30.0.5", "jest-util": "30.0.5", - "jest-validate": "30.0.5" + "jest-validate": "30.1.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-haste-map": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.5.tgz", - "integrity": "sha512-dkmlWNlsTSR0nH3nRfW5BKbqHefLZv0/6LCccG0xFCTWcJu8TuEwG+5Cm75iBfjVoockmO6J35o5gxtFSn5xeg==", + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.1.0.tgz", + "integrity": "sha512-JLeM84kNjpRkggcGpQLsV7B8W4LNUWz7oDNVnY1Vjj22b5/fAb3kk3htiD+4Na8bmJmjJR7rBtS2Rmq/NEcADg==", "dev": true, "license": "MIT", "dependencies": { @@ -7439,7 +8133,7 @@ "graceful-fs": "^4.2.11", "jest-regex-util": "30.0.1", "jest-util": "30.0.5", - "jest-worker": "30.0.5", + "jest-worker": "30.1.0", "micromatch": "^4.0.8", "walker": "^1.0.8" }, @@ -7451,13 +8145,13 @@ } }, "node_modules/jest-leak-detector": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.0.5.tgz", - "integrity": "sha512-3Uxr5uP8jmHMcsOtYMRB/zf1gXN3yUIc+iPorhNETG54gErFIiUhLvyY/OggYpSMOEYqsmRxmuU4ZOoX5jpRFg==", + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.1.0.tgz", + "integrity": "sha512-AoFvJzwxK+4KohH60vRuHaqXfWmeBATFZpzpmzNmYTtmRMiyGPVhkXpBqxUQunw+dQB48bDf4NpUs6ivVbRv1g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.0.1", + "@jest/get-type": "30.1.0", "pretty-format": "30.0.5" }, "engines": { @@ -7465,14 +8159,14 @@ } }, "node_modules/jest-matcher-utils": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.5.tgz", - "integrity": "sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.1.2.tgz", + "integrity": "sha512-7ai16hy4rSbDjvPTuUhuV8nyPBd6EX34HkBsBcBX2lENCuAQ0qKCPb/+lt8OSWUa9WWmGYLy41PrEzkwRwoGZQ==", "license": "MIT", "dependencies": { - "@jest/get-type": "30.0.1", + "@jest/get-type": "30.1.0", "chalk": "^4.1.2", - "jest-diff": "30.0.5", + "jest-diff": "30.1.2", "pretty-format": "30.0.5" }, "engines": { @@ -7480,9 +8174,9 @@ } }, "node_modules/jest-message-util": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.5.tgz", - "integrity": "sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA==", + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.1.0.tgz", + "integrity": "sha512-HizKDGG98cYkWmaLUHChq4iN+oCENohQLb7Z5guBPumYs+/etonmNFlg1Ps6yN9LTPyZn+M+b/9BbnHx3WTMDg==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", @@ -7514,9 +8208,9 @@ } }, "node_modules/jest-mock-vscode": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/jest-mock-vscode/-/jest-mock-vscode-4.6.0.tgz", - "integrity": "sha512-FTEGP5TT5y/NfoiCJQQOcauLFnA7wIcbedjlZbLZ+Udlm+QwoKcF1E+Qv5TiYTLRmV8jsPQf6RpzvLQk1iYDaw==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/jest-mock-vscode/-/jest-mock-vscode-4.7.0.tgz", + "integrity": "sha512-kjrzV0sdhCZGgCAj8JiQo/Belid9OrStAk0qS8aIqjB6lfWvHlUyqXHNZKBbMp10kg/TxFGfIXWVwnBgIxQWsg==", "dev": true, "license": "MIT", "dependencies": { @@ -7557,18 +8251,18 @@ } }, "node_modules/jest-resolve": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.0.5.tgz", - "integrity": "sha512-d+DjBQ1tIhdz91B79mywH5yYu76bZuE96sSbxj8MkjWVx5WNdt1deEFRONVL4UkKLSrAbMkdhb24XN691yDRHg==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.1.3.tgz", + "integrity": "sha512-DI4PtTqzw9GwELFS41sdMK32Ajp3XZQ8iygeDMWkxlRhm7uUTOFSZFVZABFuxr0jvspn8MAYy54NxZCsuCTSOw==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.2", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.5", + "jest-haste-map": "30.1.0", "jest-pnp-resolver": "^1.2.3", "jest-util": "30.0.5", - "jest-validate": "30.0.5", + "jest-validate": "30.1.0", "slash": "^3.0.0", "unrs-resolver": "^1.7.11" }, @@ -7577,30 +8271,30 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.0.5.tgz", - "integrity": "sha512-/xMvBR4MpwkrHW4ikZIWRttBBRZgWK4d6xt3xW1iRDSKt4tXzYkMkyPfBnSCgv96cpkrctfXs6gexeqMYqdEpw==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.1.3.tgz", + "integrity": "sha512-DNfq3WGmuRyHRHfEet+Zm3QOmVFtIarUOQHHryKPc0YL9ROfgWZxl4+aZq/VAzok2SS3gZdniP+dO4zgo59hBg==", "dev": true, "license": "MIT", "dependencies": { "jest-regex-util": "30.0.1", - "jest-snapshot": "30.0.5" + "jest-snapshot": "30.1.2" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-runner": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.0.5.tgz", - "integrity": "sha512-JcCOucZmgp+YuGgLAXHNy7ualBx4wYSgJVWrYMRBnb79j9PD0Jxh0EHvR5Cx/r0Ce+ZBC4hCdz2AzFFLl9hCiw==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.1.3.tgz", + "integrity": "sha512-dd1ORcxQraW44Uz029TtXj85W11yvLpDuIzNOlofrC8GN+SgDlgY4BvyxJiVeuabA1t6idjNbX59jLd2oplOGQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.0.5", - "@jest/environment": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/transform": "30.0.5", + "@jest/console": "30.1.2", + "@jest/environment": "30.1.2", + "@jest/test-result": "30.1.3", + "@jest/transform": "30.1.2", "@jest/types": "30.0.5", "@types/node": "*", "chalk": "^4.1.2", @@ -7608,15 +8302,15 @@ "exit-x": "^0.2.2", "graceful-fs": "^4.2.11", "jest-docblock": "30.0.1", - "jest-environment-node": "30.0.5", - "jest-haste-map": "30.0.5", - "jest-leak-detector": "30.0.5", - "jest-message-util": "30.0.5", - "jest-resolve": "30.0.5", - "jest-runtime": "30.0.5", + "jest-environment-node": "30.1.2", + "jest-haste-map": "30.1.0", + "jest-leak-detector": "30.1.0", + "jest-message-util": "30.1.0", + "jest-resolve": "30.1.3", + "jest-runtime": "30.1.3", "jest-util": "30.0.5", - "jest-watcher": "30.0.5", - "jest-worker": "30.0.5", + "jest-watcher": "30.1.3", + "jest-worker": "30.1.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -7625,18 +8319,18 @@ } }, "node_modules/jest-runtime": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.0.5.tgz", - "integrity": "sha512-7oySNDkqpe4xpX5PPiJTe5vEa+Ak/NnNz2bGYZrA1ftG3RL3EFlHaUkA1Cjx+R8IhK0Vg43RML5mJedGTPNz3A==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.1.3.tgz", + "integrity": "sha512-WS8xgjuNSphdIGnleQcJ3AKE4tBKOVP+tKhCD0u+Tb2sBmsU8DxfbBpZX7//+XOz81zVs4eFpJQwBNji2Y07DA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.0.5", - "@jest/fake-timers": "30.0.5", - "@jest/globals": "30.0.5", + "@jest/environment": "30.1.2", + "@jest/fake-timers": "30.1.2", + "@jest/globals": "30.1.2", "@jest/source-map": "30.0.1", - "@jest/test-result": "30.0.5", - "@jest/transform": "30.0.5", + "@jest/test-result": "30.1.3", + "@jest/transform": "30.1.2", "@jest/types": "30.0.5", "@types/node": "*", "chalk": "^4.1.2", @@ -7644,12 +8338,12 @@ "collect-v8-coverage": "^1.0.2", "glob": "^10.3.10", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.5", - "jest-message-util": "30.0.5", + "jest-haste-map": "30.1.0", + "jest-message-util": "30.1.0", "jest-mock": "30.0.5", "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.5", - "jest-snapshot": "30.0.5", + "jest-resolve": "30.1.3", + "jest-snapshot": "30.1.2", "jest-util": "30.0.5", "slash": "^3.0.0", "strip-bom": "^4.0.0" @@ -7720,9 +8414,9 @@ } }, "node_modules/jest-snapshot": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.0.5.tgz", - "integrity": "sha512-T00dWU/Ek3LqTp4+DcW6PraVxjk28WY5Ua/s+3zUKSERZSNyxTqhDXCWKG5p2HAJ+crVQ3WJ2P9YVHpj1tkW+g==", + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.1.2.tgz", + "integrity": "sha512-4q4+6+1c8B6Cy5pGgFvjDy/Pa6VYRiGu0yQafKkJ9u6wQx4G5PqI2QR6nxTl43yy7IWsINwz6oT4o6tD12a8Dg==", "dev": true, "license": "MIT", "dependencies": { @@ -7731,18 +8425,18 @@ "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.0.5", - "@jest/get-type": "30.0.1", - "@jest/snapshot-utils": "30.0.5", - "@jest/transform": "30.0.5", + "@jest/expect-utils": "30.1.2", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.1.2", + "@jest/transform": "30.1.2", "@jest/types": "30.0.5", "babel-preset-current-node-syntax": "^1.1.0", "chalk": "^4.1.2", - "expect": "30.0.5", + "expect": "30.1.2", "graceful-fs": "^4.2.11", - "jest-diff": "30.0.5", - "jest-matcher-utils": "30.0.5", - "jest-message-util": "30.0.5", + "jest-diff": "30.1.2", + "jest-matcher-utils": "30.1.2", + "jest-message-util": "30.1.0", "jest-util": "30.0.5", "pretty-format": "30.0.5", "semver": "^7.7.2", @@ -7782,13 +8476,13 @@ } }, "node_modules/jest-validate": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.0.5.tgz", - "integrity": "sha512-ouTm6VFHaS2boyl+k4u+Qip4TSH7Uld5tyD8psQ8abGgt2uYYB8VwVfAHWHjHc0NWmGGbwO5h0sCPOGHHevefw==", + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.1.0.tgz", + "integrity": "sha512-7P3ZlCFW/vhfQ8pE7zW6Oi4EzvuB4sgR72Q1INfW9m0FGo0GADYlPwIkf4CyPq7wq85g+kPMtPOHNAdWHeBOaA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.0.1", + "@jest/get-type": "30.1.0", "@jest/types": "30.0.5", "camelcase": "^6.3.0", "chalk": "^4.1.2", @@ -7813,13 +8507,13 @@ } }, "node_modules/jest-watcher": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.0.5.tgz", - "integrity": "sha512-z9slj/0vOwBDBjN3L4z4ZYaA+pG56d6p3kTUhFRYGvXbXMWhXmb/FIxREZCD06DYUwDKKnj2T80+Pb71CQ0KEg==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.1.3.tgz", + "integrity": "sha512-6jQUZCP1BTL2gvG9E4YF06Ytq4yMb4If6YoQGRR6PpjtqOXSP3sKe2kqwB6SQ+H9DezOfZaSLnmka1NtGm3fCQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.0.5", + "@jest/test-result": "30.1.3", "@jest/types": "30.0.5", "@types/node": "*", "ansi-escapes": "^4.3.2", @@ -7833,9 +8527,9 @@ } }, "node_modules/jest-worker": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.5.tgz", - "integrity": "sha512-ojRXsWzEP16NdUuBw/4H/zkZdHOa7MMYCk4E430l+8fELeLg/mqmMlRhjL7UNZvQrDmnovWZV4DxX03fZF48fQ==", + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.1.0.tgz", + "integrity": "sha512-uvWcSjlwAAgIu133Tt77A05H7RIk3Ho8tZL50bQM2AkvLdluw9NG48lRCl3Dt+MOH719n/0nnb5YxUwcuJiKRA==", "dev": true, "license": "MIT", "dependencies": { @@ -7905,6 +8599,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -8099,6 +8800,46 @@ "uc.micro": "^2.0.0" } }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -8307,6 +9048,15 @@ "dev": true, "license": "MIT" }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -8681,9 +9431,9 @@ "optional": true }, "node_modules/napi-postinstall": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.2.tgz", - "integrity": "sha512-tWVJxJHmBWLy69PvO96TZMZDrzmw5KeiZBz3RHmiM2XZ9grBJ2WgMAFVVg25nqp3ZjTFUs2Ftw1JhscL3Teliw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", + "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==", "dev": true, "license": "MIT", "bin": { @@ -8710,6 +9460,13 @@ "dev": true, "license": "MIT" }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true, + "license": "MIT" + }, "node_modules/nise": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", @@ -8829,6 +9586,255 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/npm-run-all/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/npm-run-all/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/npm-run-all/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm-run-all/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -8878,6 +9884,27 @@ "node": ">= 0.4" } }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -9079,6 +10106,24 @@ "node": ">= 6" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -9368,6 +10413,29 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", @@ -9457,6 +10525,16 @@ "node": ">=4" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -9816,6 +10894,50 @@ "node": ">=8.10.0" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -9991,6 +11113,33 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -10012,6 +11161,48 @@ ], "license": "MIT" }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -10070,6 +11261,55 @@ "randombytes": "^2.1.0" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -10098,6 +11338,19 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -10413,6 +11666,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -10538,6 +11805,84 @@ "node": ">=8" } }, + "node_modules/string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -10906,9 +12251,9 @@ } }, "node_modules/tmp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", - "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "license": "MIT", "engines": { "node": ">=14.14" @@ -10947,9 +12292,9 @@ } }, "node_modules/ts-jest": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.1.tgz", - "integrity": "sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==", + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.2.tgz", + "integrity": "sha512-pBNOkn4HtuLpNrXTMVRC9b642CBaDnKqWXny4OzuoULT9S7Kf8MMlaRe2veKax12rjf5WcpMBhVPbQurlWGNxA==", "dev": true, "license": "MIT", "dependencies": { @@ -11133,6 +12478,84 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typed-rest-client": { "version": "1.8.11", "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", @@ -11160,16 +12583,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.0.tgz", - "integrity": "sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.0.tgz", + "integrity": "sha512-ib7mCkYuIzYonCq9XWF5XNw+fkj2zg629PSa9KNIQ47RXFF763S5BIX4wqz1+FLPogTZoiw8KmCiRPRa8bL3qw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.39.0", - "@typescript-eslint/parser": "8.39.0", - "@typescript-eslint/typescript-estree": "8.39.0", - "@typescript-eslint/utils": "8.39.0" + "@typescript-eslint/eslint-plugin": "8.44.0", + "@typescript-eslint/parser": "8.44.0", + "@typescript-eslint/typescript-estree": "8.44.0", + "@typescript-eslint/utils": "8.44.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11204,6 +12627,25 @@ "node": ">=0.8.0" } }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/underscore": { "version": "1.13.7", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", @@ -11451,6 +12893,102 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index 3e1f1ffa..5e655c9c 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "color": "#ECECEC", "theme": "light" }, - "version": "1.10.0", + "version": "1.11.0", "publisher": "salesforce", "license": "BSD-3-Clause", "engines": { @@ -35,38 +35,40 @@ "dependencies": { "@salesforce/vscode-service-provider": "^1.5.0", "@types/jest": "^30.0.0", - "@types/semver": "^7.7.0", + "@types/semver": "^7.7.1", "@types/tmp": "^0.2.6", "diff": "^5.2.0", "glob": "^11.0.3", "semver": "^7.7.2", - "tmp": "^0.2.4" + "tmp": "^0.2.5" }, "devDependencies": { - "@eslint/js": "^9.32.0", - "@types/diff": "^5.2.3", + "@eslint/js": "^9.35.0", "@types/chai": "^4.3.20", + "@types/diff": "^5.2.3", "@types/mocha": "^10.0.10", - "@types/node": "^22.16.4", + "@types/node": "^22.18.5", "@types/sinon": "^10.0.20", "@types/vscode": "^1.90.0", "@vscode/test-cli": "^0.0.11", "@vscode/test-electron": "^2.5.2", "@vscode/vsce": "^3.6.0", "chai": "^4.5.0", - "esbuild": "^0.25.8", - "eslint": "^9.32.0", - "jest": "^30.0.5", - "jest-mock-vscode": "^4.6.0", + "esbuild": "^0.25.9", + "eslint": "^9.35.0", + "husky": "^9.1.7", + "jest": "^30.1.3", + "jest-mock-vscode": "^4.7.0", "mocha": "^10.8.2", + "npm-run-all": "^4.1.5", "ovsx": "^0.10.5", "proxyquire": "^2.1.3", "rimraf": "*", "sinon": "^15.2.0", - "ts-jest": "^29.4.1", + "ts-jest": "^29.4.2", "ts-node": "^10.9.2", "typescript": "^5.9.2", - "typescript-eslint": "^8.39.0" + "typescript-eslint": "^8.44.0" }, "extensionDependencies": [ "salesforce.salesforcedx-vscode-core" @@ -89,7 +91,8 @@ "clean": "npm run precompile && rimraf coverage", "showcoverage": "npm run showcoverage-unit && npm run showcoverage-legacy", "showcoverage-unit": "open ./coverage/unit/lcov-report/index.html", - "showcoverage-legacy": "open ./coverage/legacy/lcov-report/index.html" + "showcoverage-legacy": "open ./coverage/legacy/lcov-report/index.html", + "prepare": "husky" }, "activationEvents": [ "workspaceContains:sfdx-project.json", @@ -138,11 +141,6 @@ "type": "boolean", "default": false, "description": "Scan files on open automatically." - }, - "codeAnalyzer.apexGuru.enabled": { - "type": "boolean", - "default": false, - "description": "(Pilot) Discover critical problems and performance issues in your Apex code with ApexGuru, which analyzes your Apex files for you. This feature is in a closed pilot; contact your account representative to learn more." } } }, @@ -186,7 +184,7 @@ }, { "command": "sfca.runApexGuruAnalysisOnCurrentFile", - "when": "sfca.extensionActivated && sfca.apexGuruEnabled && resourceExtname =~ /\\.cls|\\.trigger|\\.apex/" + "when": "sfca.extensionActivated && sfca.shouldShowApexGuruButtons && resourceExtname =~ /\\.cls|\\.trigger|\\.apex/" } ], "editor/context": [ @@ -200,7 +198,7 @@ }, { "command": "sfca.runApexGuruAnalysisOnCurrentFile", - "when": "sfca.extensionActivated && sfca.apexGuruEnabled && resourceExtname =~ /\\.cls|\\.trigger|\\.apex/" + "when": "sfca.extensionActivated && sfca.shouldShowApexGuruButtons && resourceExtname =~ /\\.cls|\\.trigger|\\.apex/" } ], "explorer/context": [ @@ -214,7 +212,7 @@ }, { "command": "sfca.runApexGuruAnalysisOnSelectedFile", - "when": "sfca.extensionActivated && sfca.apexGuruEnabled && explorerResourceIsFolder == false && resourceExtname =~ /\\.cls|\\.trigger|\\.apex/" + "when": "sfca.extensionActivated && sfca.shouldShowApexGuruButtons && explorerResourceIsFolder == false && resourceExtname =~ /\\.cls|\\.trigger|\\.apex/" } ] }, diff --git a/src/extension.ts b/src/extension.ts index b29ad2a5..35aec3a4 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -33,7 +33,7 @@ import {PMDSupressionsCodeActionProvider} from './lib/pmd/pmd-suppressions-code- import {ApplyViolationFixesActionProvider} from './lib/apply-violation-fixes-action-provider'; import {ApplyViolationFixesAction} from './lib/apply-violation-fixes-action'; import {ViolationSuggestionsHoverProvider} from './lib/violation-suggestions-hover-provider'; -import {ApexGuruService, LiveApexGuruService} from './lib/apexguru/apex-guru-service'; +import {ApexGuruAccess, ApexGuruService, LiveApexGuruService} from './lib/apexguru/apex-guru-service'; import {ApexGuruRunAction} from './lib/apexguru/apex-guru-run-action'; import {OrgConnectionService} from './lib/external-services/org-connection-service'; @@ -246,15 +246,12 @@ export async function activate(context: vscode.ExtensionContext): Promise Promise = async () => settingsManager.getApexGuruEnabled() && - // Currently we don't watch for changes here when a user has apex guru enabled already. That is, - // if the user logs into an org post activation of this extension, it won't show the command until they - // refresh or toggle the "ApexGuru enabled" setting off and back on. At some point we might want to see - // if it is possible to monitor changes to the users org so we can re-trigger this check. - await apexGuruService.isApexGuruAvailable(); - await establishVariableInContext(Constants.CONTEXT_VAR_APEX_GURU_ENABLED, isApexGuruEnabled); + apexGuruService.onAccessChange((access: ApexGuruAccess) => { + logger.debug(`Access to ApexGuru has been set '${access}'.`); + void vscode.commands.executeCommand('setContext', Constants.CONTEXT_VAR_SHOULD_SHOW_APEX_GURU_BUTTONS, + access === ApexGuruAccess.ENABLED || access === ApexGuruAccess.ELIGIBLE); + }); + void apexGuruService.updateAvailability(); // This asyncronously triggers the first AccessChanged Event to establish the context variable // COMMAND_RUN_APEX_GURU_ON_FILE: Invokable by 'explorer/context' menu only when: "sfca.apexGuruEnabled && explorerResourceIsFolder == false && resourceExtname =~ /\\.cls|\\.trigger|\\.apex/" registerCommand(Constants.COMMAND_RUN_APEX_GURU_ON_FILE, async (selection: vscode.Uri, multiSelect?: vscode.Uri[]) => { @@ -319,19 +316,6 @@ export function _isValidFileForAnalysis(documentUri: vscode.Uri): boolean { return allowedFileTypes.includes(path.extname(documentUri.fsPath)); } -// TODO: This is only used by apex guru right now and is tied to the pilot setting. Soon we will be removing the pilot -// setting and instead we should be adding a watch to the onOrgChange event of the OrgConnectionService instead. -// Inside our package.json you'll see things like: -// "when": "sfca.apexGuruEnabled" -// which helps determine when certain commands and menus are available. -// To make these "context variables" set and stay updated when settings change, use this helper function: -async function establishVariableInContext(varUsedInPackageJson: string, getValueFcn: () => Promise): Promise { - await vscode.commands.executeCommand('setContext', varUsedInPackageJson, await getValueFcn()); - vscode.workspace.onDidChangeConfiguration(async () => { - await vscode.commands.executeCommand('setContext', varUsedInPackageJson, await getValueFcn()); - }); -} - async function getActiveDocument(): Promise { // Note that the active editor window could be the output window instead of the actual file editor, so we // force focus it first to ensure we are getting the correct editor diff --git a/src/lib/apexguru/apex-guru-run-action.ts b/src/lib/apexguru/apex-guru-run-action.ts index 9ca5a276..7ead56f5 100644 --- a/src/lib/apexguru/apex-guru-run-action.ts +++ b/src/lib/apexguru/apex-guru-run-action.ts @@ -6,7 +6,7 @@ import { TelemetryService } from "../external-services/telemetry-service"; import { Display } from "../display"; import { messages } from "../messages"; import { getErrorMessage, getErrorMessageWithStack } from "../utils"; -import { APEX_GURU_ENGINE_NAME, ApexGuruService } from "./apex-guru-service"; +import { APEX_GURU_ENGINE_NAME, ApexGuruAccess, ApexGuruAvailability, ApexGuruService } from "./apex-guru-service"; export class ApexGuruRunAction { private readonly taskWithProgressRunner: TaskWithProgressRunner; @@ -33,6 +33,16 @@ export class ApexGuruRunAction { const startTime: number = Date.now(); try { + const availability: ApexGuruAvailability = this.apexGuruService.getAvailability(); + if (availability.access !== ApexGuruAccess.ENABLED) { + this.display.displayError(availability.message); + this.telemetryService.sendCommandEvent(Constants.TELEM_APEX_GURU_FILE_ANALYSIS_NOT_ENABLED, { + executedCommand: commandName, + access: availability.access + }); + return; + } + progressReporter.reportProgress({ message: messages.apexGuru.runningAnalysis }); diff --git a/src/lib/apexguru/apex-guru-service.ts b/src/lib/apexguru/apex-guru-service.ts index fbc79cd1..a00cbe73 100644 --- a/src/lib/apexguru/apex-guru-service.ts +++ b/src/lib/apexguru/apex-guru-service.ts @@ -5,12 +5,13 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import {CodeLocation, Fix, Suggestion, Violation} from '../diagnostics'; +import {CodeLocation, Fix, normalizeViolation, Suggestion, Violation} from '../diagnostics'; import {Logger} from "../logger"; import {getErrorMessage, indent} from '../utils'; -import {HttpMethods, HttpRequest, OrgConnectionService} from '../external-services/org-connection-service'; +import {HttpMethods, HttpRequest, OrgConnectionService, OrgUserInfo} from '../external-services/org-connection-service'; import {FileHandler} from '../fs-utils'; import { messages } from '../messages'; +import { EventEmitter } from 'node:stream'; export const APEX_GURU_ENGINE_NAME: string = 'apexguru'; const APEX_GURU_MAX_TIMEOUT_SECONDS = 60; @@ -24,16 +25,43 @@ const RESPONSE_STATUS = { } export interface ApexGuruService { - isApexGuruAvailable(): Promise; + getAvailability(): ApexGuruAvailability; + updateAvailability(): Promise; + onAccessChange(callback: (access: ApexGuruAccess) => void): void; scan(absFileToScan: string): Promise; } +export type ApexGuruAvailability = { + access: ApexGuruAccess, + message: string +} + +export enum ApexGuruAccess { + // In this case, ApexGuru scans are allowed + ENABLED = "enabled", + + // In this case, the org is eligible to be enabled, but an admin hasn't set the permissions yet, so we should still + // show the scan button but then show a message with the instructions sent from the validate endpoint. + ELIGIBLE = "eligible-but-not-enabled", + + // In this case, the org is not eligible for ApexGuru at all, so we should not show the scan button at all. + INELIGIBLE = "ineligible", + + // In this case, the user has not authed into an org, so we should not show the scan button at all. + NOT_AUTHED = "not-authed" +} + +const ACCESS_CHANGED_EVENT = "apexGuruAccessChanged"; + export class LiveApexGuruService implements ApexGuruService { private readonly orgConnectionService: OrgConnectionService; private readonly fileHandler: FileHandler; private readonly logger: Logger; private readonly maxTimeoutSeconds: number; private readonly retryIntervalMillis: number; + private readonly eventEmitter: EventEmitter = new EventEmitter(); + private availability?: ApexGuruAvailability; + constructor( orgConnectionService: OrgConnectionService, fileHandler: FileHandler, @@ -45,14 +73,61 @@ export class LiveApexGuruService implements ApexGuruService { this.logger = logger; this.maxTimeoutSeconds = maxTimeoutSeconds; this.retryIntervalMillis = retryIntervalMillis; + + // Every time an org is changed (authed or unauthed) then we recalculate the availability asyncronously + orgConnectionService.onOrgChange((_orgUserInfo: OrgUserInfo) => { + void this.updateAvailability(); + }); + } + + getAvailability(): ApexGuruAvailability { + if (this.availability === undefined) { + // This should never happen in production because updateAvailability must be called prior to enabling + // the user to even have access to any of the ApexGuru scan buttons. If it does, we should investigate. + throw new Error('The getAvailability method should not be called until updateAvailability is first called'); + } + return this.availability; + } + + onAccessChange(callback: (access: ApexGuruAccess) => void): void { + this.eventEmitter.addListener(ACCESS_CHANGED_EVENT, callback); } - async isApexGuruAvailable(): Promise { + async updateAvailability(): Promise { if (!this.orgConnectionService.isAuthed()) { - return false; + this.setAvailability({ + access: ApexGuruAccess.NOT_AUTHED, + message: messages.apexGuru.noOrgAuthed + }); + return; } + const response: ApexGuruResponse = await this.request('GET', await this.getValidateEndpoint()); - return response.status === RESPONSE_STATUS.SUCCESS; + + if (response.status === RESPONSE_STATUS.SUCCESS) { + this.setAvailability({ + access: ApexGuruAccess.ENABLED, + + // This message isn't used anywhere except for debugging purposes and it allows us to make message field + // a string instead of a string | undefined. + message: "ApexGuru access is enabled." + }); + } else { + this.setAvailability({ + access: response.status === RESPONSE_STATUS.FAILED ? ApexGuruAccess.ELIGIBLE : ApexGuruAccess.INELIGIBLE, + + // There should always be a message on failed and error responses, but adding this here just in case + message: response.message ?? `ApexGuru access is not enabled. Response: ${JSON.stringify(response)}` + }); + } + } + + private setAvailability(availability: ApexGuruAvailability) { + const oldAccess: ApexGuruAccess | undefined = this.availability?.access; + this.availability = availability; + if (availability.access !== oldAccess) { + this.eventEmitter.emit(ACCESS_CHANGED_EVENT, availability.access); + } } async scan(absFileToScan: string): Promise { @@ -63,7 +138,9 @@ export class LiveApexGuruService implements ApexGuruService { const payloadStr: string = decodeFromBase64(queryResponse.report); this.logger.debug(`ApexGuru Analysis completed for Request Id: ${requestId}\n\nDecoded Response Payload:\n${payloadStr}`); const apexGuruViolations: ApexGuruViolation[] = parsePayload(payloadStr); - return apexGuruViolations.map(v => toViolation(v, absFileToScan)); + + const lineLengths: number[] = fileContent.split(/\r?\n/).map(l => l.length); + return apexGuruViolations.map(v => toViolation(v, absFileToScan, lineLengths)); } private async initiateRequest(fileContent: string): Promise { @@ -149,7 +226,7 @@ export function parsePayload(payloadStr: string): ApexGuruViolation[] { } } -function toViolation(apexGuruViolation: ApexGuruViolation, file: string): Violation { +function toViolation(apexGuruViolation: ApexGuruViolation, file: string, lineLengths: number[]): Violation { const codeAnalyzerViolation: Violation = { rule: apexGuruViolation.rule, engine: APEX_GURU_ENGINE_NAME, @@ -168,7 +245,7 @@ function toViolation(apexGuruViolation: ApexGuruViolation, file: string): Violat return f; }) }; - return codeAnalyzerViolation; + return normalizeViolation(codeAnalyzerViolation, lineLengths); } function addFile(apexGuruLocation: CodeLocation, filePath: string): CodeLocation { diff --git a/src/lib/cli-commands.ts b/src/lib/cli-commands.ts index 8d2709e7..642e9e9a 100644 --- a/src/lib/cli-commands.ts +++ b/src/lib/cli-commands.ts @@ -106,8 +106,10 @@ export class CliCommandExecutorImpl implements CliCommandExecutor { let childProcess: cp.ChildProcessWithoutNullStreams; try { - childProcess = IS_WINDOWS ? cp.spawn(command, wrapArgsWithSpacesWithQuotes(args), {shell: true}) : - cp.spawn(command, args); + childProcess = + IS_WINDOWS + ? cp.spawn(command, wrapArgsWithSpacesWithQuotes(args), {shell: true, env: {...process.env, NO_COLOR: '1'}}) + : cp.spawn(command, args, {env: {...process.env, NO_COLOR: '1'}}); } catch (err) { this.logger.logAtLevel(vscode.LogLevel.Error, `Failed to execute the following command:\n` + indent(`${command} ${wrapArgsWithSpacesWithQuotes(args).join(' ')}`) + `\n\n` + diff --git a/src/lib/code-analyzer-run-action.ts b/src/lib/code-analyzer-run-action.ts index 2a09561c..e699c08a 100644 --- a/src/lib/code-analyzer-run-action.ts +++ b/src/lib/code-analyzer-run-action.ts @@ -1,6 +1,6 @@ import * as vscode from "vscode"; import {Logger} from "./logger"; -import {CodeAnalyzerDiagnostic, DiagnosticManager, Violation} from "./diagnostics"; +import {CodeAnalyzerDiagnostic, DiagnosticManager, normalizeViolation, Violation} from "./diagnostics"; import {messages} from "./messages"; import {TelemetryService} from "./external-services/telemetry-service"; import * as Constants from './constants'; @@ -10,6 +10,7 @@ import {getErrorMessage, getErrorMessageWithStack} from "./utils"; import {ProgressReporter, TaskWithProgressRunner} from "./progress"; import {WindowManager} from "./vscode-api"; import {Workspace} from "./workspace"; +import { APEX_GURU_ENGINE_NAME } from "./apexguru/apex-guru-service"; export const UNINSTANTIABLE_ENGINE_RULE = 'UninstantiableEngineError'; @@ -78,9 +79,29 @@ export class CodeAnalyzerRunAction { return true; }); - const diagnostics: CodeAnalyzerDiagnostic[] = violationsWithFileLocation.map(v => CodeAnalyzerDiagnostic.fromViolation(v)); + // Ideally we should be passing in the line lengths array for the primary locations for each violation + // in our call to normalizeViolation, so that it can set the endColumn when it is missing instead of + // setting it to MAX_SAFE_INTEGER, but it might be rather expensive to read and process all files + // in a scan to get the line numbers. So I'm not sure if it is worth it to do this right now unless + // a user complains. Without doing this, any diagnostics that are from locations without an endColumn + // will be given MAX_SAFE_INTEGER which makes them unable to be deleted by the user via edits. Even if + // the user highlights and deletes the diagnostic, they'll might see a 0 character blue line stale + // diagnostic still be visible in some cases (because the new diag range still has a range that extends + // past the length of the line in the editor window). + const diagnostics: CodeAnalyzerDiagnostic[] = violationsWithFileLocation + .map(v => normalizeViolation(v)) // <-- Maybe in the future we'll pass in the lineLengths for the primary file + .map(v => CodeAnalyzerDiagnostic.fromViolation(v)); const targetedFiles: string[] = await workspace.getTargetedFiles(); - this.diagnosticManager.clearDiagnosticsForFiles(targetedFiles.map(f => vscode.Uri.file(f))); + + // Before adding in the new code analyzer diagnostics, we clear all the old code analyzer diagnostics + // except for ApexGuru based diagnostics which are handled separately. + for (const file of targetedFiles) { + const diagsToClear: CodeAnalyzerDiagnostic[] = + this.diagnosticManager.getDiagnosticsForFile(vscode.Uri.file(file)) + .filter(d => d.violation.engine !== APEX_GURU_ENGINE_NAME); + this.diagnosticManager.clearDiagnostics(diagsToClear); + } + this.diagnosticManager.addDiagnostics(diagnostics); void this.displayResults(targetedFiles.length, violationsWithFileLocation); diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 016e795e..775ebc88 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -35,6 +35,7 @@ export const TELEM_SUCCESSFUL_STATIC_ANALYSIS = 'sfdx__codeanalyzer_static_run_c export const TELEM_FAILED_STATIC_ANALYSIS = 'sfdx__codeanalyzer_static_run_failed'; export const TELEM_SUCCESSFUL_APEX_GURU_FILE_ANALYSIS = 'sfdx__apexguru_file_run_complete'; export const TELEM_FAILED_APEX_GURU_FILE_ANALYSIS = 'sfdx__apexguru_file_run_failed'; +export const TELEM_APEX_GURU_FILE_ANALYSIS_NOT_ENABLED = 'sfdx__apexguru_file_run_not_enabled'; export const TELEM_COPY_SUGGESTION_CLICKED = 'sfdx__codeanalyzer_copy_suggestion_clicked'; // telemetry keys used by eGPT (A4D) @@ -57,7 +58,7 @@ export const ABSOLUTE_MINIMUM_REQUIRED_CODE_ANALYZER_CLI_PLUGIN_VERSION = '5.0.0 // Context variables (dynamically set but consumed by the "when" conditions in the package.json "contributes" sections) export const CONTEXT_VAR_EXTENSION_ACTIVATED = 'sfca.extensionActivated'; -export const CONTEXT_VAR_APEX_GURU_ENABLED = 'sfca.apexGuruEnabled'; +export const CONTEXT_VAR_SHOULD_SHOW_APEX_GURU_BUTTONS = 'sfca.shouldShowApexGuruButtons'; // Documentation URLs export const DOCS_SETUP_LINK = 'https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/analyze-vscode.html#install-and-configure-code-analyzer-vs-code-extension'; \ No newline at end of file diff --git a/src/lib/diagnostics.ts b/src/lib/diagnostics.ts index 30750b56..2f616a98 100644 --- a/src/lib/diagnostics.ts +++ b/src/lib/diagnostics.ts @@ -126,7 +126,9 @@ export class CodeAnalyzerDiagnostic extends vscode.Diagnostic { if (i !== violation.primaryLocationIndex && relatedLocation.file) { const relatedRange = toRange(relatedLocation); const vscodeLocation: vscode.Location = new vscode.Location(vscode.Uri.file(relatedLocation.file), relatedRange); - relatedLocations.push(new vscode.DiagnosticRelatedInformation(vscodeLocation, relatedLocation.comment ?? '')); + relatedLocations.push(new vscode.DiagnosticRelatedInformation(vscodeLocation, relatedLocation.comment ?? + messages.diagnostics.defaultAlternativeLocationMessage + )); } } diagnostic.relatedInformation = relatedLocations; @@ -206,7 +208,7 @@ export class DiagnosticManagerImpl implements DiagnosticManager { for (const change of event.contentChanges) { - // Calculating this once and passing it in instead of redoing it for each of the diagnostics + // Calculating this once and pass it in instead of redoing it for each of the diagnostics const replacementLines: string[] = change.text.split('\n'); const updatedDiagnostics: CodeAnalyzerDiagnostic[] = diags @@ -226,17 +228,45 @@ export class DiagnosticManagerImpl implements DiagnosticManager { } } +export function normalizeViolation(violation: Violation, lineLengthsForPrimaryFile?: number[]): Violation { + const primaryFile: string = violation.locations[violation.primaryLocationIndex].file; + + for (let i: number = 0; i < violation.locations.length; i++) { + if (violation.locations[i].file === primaryFile) { + normalizeLocation(violation.locations[i], lineLengthsForPrimaryFile); + } + } + + for (let i: number = 0; i < violation.fixes?.length; i++) { + if (violation.fixes[i].location.file === primaryFile) { + normalizeLocation(violation.fixes[i].location, lineLengthsForPrimaryFile); + } + } + + for (let i: number = 0; i < violation.suggestions?.length; i++) { + if (violation.suggestions[i].location.file === primaryFile) { + normalizeLocation(violation.suggestions[i].location, lineLengthsForPrimaryFile); + } + } + + return violation; +} + +function normalizeLocation(location: CodeLocation, lineLengths?: number[]): CodeLocation { + location.startLine = location.startLine ?? 1; + location.startColumn = location.startColumn ?? 1; + location.endLine = location.endLine ?? location.startLine; + location.endColumn = location.endColumn ?? (lineLengths ? (lineLengths[location.endLine-1] + 1) : Number.MAX_SAFE_INTEGER); + return location; +} export function toRange(codeLocation: CodeLocation): vscode.Range { - // If there's no explicit startLine, just use the first line. - const startLine: number = codeLocation.startLine != null ? adjustToZeroBased(codeLocation.startLine) : 0; - // If there's no explicit startColumn, just use the first column. - const startColumn: number = codeLocation.startColumn != null ? adjustToZeroBased(codeLocation.startColumn) : 0; - // If there's no explicit end line, just use the start line. - const endLine: number = codeLocation.endLine != null ? adjustToZeroBased(codeLocation.endLine) : startLine; - // If there's no explicit end column, just highlight everything through the end of the line (by just using a really large number). - const endColumn = codeLocation.endColumn != null ? adjustToZeroBased(codeLocation.endColumn) : Number.MAX_SAFE_INTEGER; - return new vscode.Range(startLine, startColumn, endLine, endColumn); + normalizeLocation(codeLocation); + return new vscode.Range( + adjustToZeroBased(codeLocation.startLine), + adjustToZeroBased(codeLocation.startColumn), + adjustToZeroBased(codeLocation.endLine), + adjustToZeroBased(codeLocation.endColumn)); } function groupByUri(diags: CodeAnalyzerDiagnostic[]): Map { @@ -261,24 +291,118 @@ function adjustToZeroBased(value: number): number { } +function adjustDiagnosticToChange(diag: CodeAnalyzerDiagnostic, change: vscode.TextDocumentContentChangeEvent, + replacementLines: string[]): CodeAnalyzerDiagnostic | null { + + const violationAdjustment: Adjustment = adjustViolationToChange(diag.violation, change, replacementLines); + if (violationAdjustment.newValue === null) { + return null; // Do not add back a diagnostic if its violation has been marked for removal + } + const newDiag: CodeAnalyzerDiagnostic = CodeAnalyzerDiagnostic.fromViolation(diag.violation); + + if (violationAdjustment.overlapsWithChange || diag.isStale()) { + diag.markStale(); // Not really needed, but added for safety just in case somehow the old diagnostic doesn't properly get thrown away. + newDiag.markStale(); + } + + return newDiag; +} + +function adjustViolationToChange(oldViolation: Violation, change: vscode.TextDocumentContentChangeEvent, + replacementLines: string[]): Adjustment { + const primaryLocation: CodeLocation = oldViolation.locations[oldViolation.primaryLocationIndex]; + let hasPrimaryLocationOverlap: boolean = false; + const newViolation: Violation = oldViolation; + + // Update violation locations + const newViolationLocations: CodeLocation[] = []; + for (let i: number = 0; i < oldViolation.locations.length; i++) { + const origLocation = oldViolation.locations[i]; + // Other locations not associated with this file (for the primary location) shouldn't be updated + if (origLocation.file !== primaryLocation.file) { + newViolationLocations.push(origLocation); + continue; + } + + const locationAdjustment: Adjustment = adjustLocationToChange(origLocation, change, replacementLines); + if (i === oldViolation.primaryLocationIndex) { + hasPrimaryLocationOverlap = locationAdjustment.overlapsWithChange; + } + + if (locationAdjustment.newValue !== null) { + newViolationLocations.push(locationAdjustment.newValue); + } else if (i === oldViolation.primaryLocationIndex) { + return { newValue: null, overlapsWithChange: locationAdjustment.overlapsWithChange }; // Indicates that the violation should be removed + } else if (i < oldViolation.primaryLocationIndex) { + newViolation.primaryLocationIndex--; // Edge case: need to adjust primary index if deleting a location prior to the primary + } + } + newViolation.locations = newViolationLocations; + + + // update fix locations + newViolation.fixes = oldViolation.fixes?.map(fix => { + if (fix.location.file !== primaryLocation.file) { + return fix; + } + const locationAdjustment: Adjustment = adjustLocationToChange(fix.location, change, replacementLines); + return locationAdjustment.newValue === null || locationAdjustment.overlapsWithChange ? + null : {...fix, location: locationAdjustment.newValue}; + }).filter(fix => fix !== null); + + + // update suggestion locations + newViolation.suggestions = oldViolation.suggestions?.map(suggestion => { + if (suggestion.location.file !== primaryLocation.file) { + return suggestion; + } + const locationAdjustment: Adjustment = adjustLocationToChange(suggestion.location, change, replacementLines); + return locationAdjustment.newValue === null || locationAdjustment.overlapsWithChange ? + null : {...suggestion, location: locationAdjustment.newValue}; + }).filter(suggestion => suggestion !== null); + + return { newValue: newViolation, overlapsWithChange: hasPrimaryLocationOverlap }; +} + +function adjustLocationToChange(origLocation: CodeLocation, change: vscode.TextDocumentContentChangeEvent, + replacementLines: string[]): Adjustment { + const origRange: vscode.Range = toRange(origLocation); + const rangeAdjustment: Adjustment = adjustRangeToChange(origRange, change, replacementLines); + if (rangeAdjustment.newValue === null) { + return { newValue: null, overlapsWithChange: rangeAdjustment.overlapsWithChange }; + } + return { + newValue: { + file: origLocation.file, + comment: origLocation.comment, + startLine: rangeAdjustment.newValue.start.line + 1, + startColumn: rangeAdjustment.newValue.start.character + 1, + endLine: rangeAdjustment.newValue.end.line + 1, + endColumn: rangeAdjustment.newValue.end.character >= Number.MAX_SAFE_INTEGER ? + undefined : rangeAdjustment.newValue.end.character + 1 + }, + overlapsWithChange: rangeAdjustment.overlapsWithChange + } +} + /** - * Algorithm to adjust a diagnostic's range (or discard it by return null) based on the text document change event. + * Algorithm to adjust a range (or discard it by return null) based on the text document change event. * This algorithm needs to be very fast since it literally runs on every stroke of a key within the editor window. */ -function adjustDiagnosticToChange(diag: CodeAnalyzerDiagnostic, change: vscode.TextDocumentContentChangeEvent, - replacementLines: string[]): CodeAnalyzerDiagnostic | null { +function adjustRangeToChange(origRange: vscode.Range, change: vscode.TextDocumentContentChangeEvent, + replacementLines: string[]): Adjustment { // Key: . single line (i.e. no '\n' characters) // _ multiple lines (i.e. at least one '\n' character) // * line that could be single or multiple (may or may not contain at least one '\n' character) // { start of change range // } end of change range - // [ start of diagnostic range - // ] end of diagnostic range + // [ start of original range + // ] end of original range // Cases: [*]*{*} - // If the change is after the diagnostic, then no updates needed - if (change.range.start.isAfterOrEqual(diag.range.end)) { - return diag; + // If the change is after the original range, then no updates needed + if (change.range.start.isAfterOrEqual(origRange.end)) { + return { newValue: origRange, overlapsWithChange: false }; } // Calculate the change in the number of lines @@ -286,64 +410,60 @@ function adjustDiagnosticToChange(diag: CodeAnalyzerDiagnostic, change: vscode.T const numLinesDiff: number = replacementLines.length - numLinesInChangeRange; // Initialize the results - let newStartLine: number = diag.range.start.line; - let newStartChar: number = diag.range.start.character; - let newEndLine: number = diag.range.end.line; - let newEndChar: number = diag.range.end.character; + let newStartLine: number = origRange.start.line; + let newStartChar: number = origRange.start.character; + let newEndLine: number = origRange.end.line; + let newEndChar: number = origRange.end.character; // Cases: {*}*[*] - // If the change is before the diagnostic, then we just need to increase the diagnostic lines - if (change.range.end.isBeforeOrEqual(diag.range.start)) { - newStartLine = diag.range.start.line + numLinesDiff; - newEndLine = diag.range.end.line + numLinesDiff; + // If the change is before the original range, then we just need to increase the range lines + if (change.range.end.isBeforeOrEqual(origRange.start)) { + newStartLine = origRange.start.line + numLinesDiff; + newEndLine = origRange.end.line + numLinesDiff; // Cases: {*}.[*] - // If the preceding change is on the same line as the diagnostic, then adjust the characters as well - if (change.range.end.line === diag.range.start.line) { + // If the preceding change is on the same line as the original range, then adjust the characters as well + if (change.range.end.line === origRange.start.line) { const leftPos: number = replacementLines.length > 1 ? 0 : change.range.start.character; - const origLen: number = diag.range.start.character - change.range.end.character; + const origLen: number = origRange.start.character - change.range.end.character; const lastLineLen: number = replacementLines[replacementLines.length-1].length; newStartChar = leftPos + origLen + lastLineLen; // Cases: {*}.[.] - if (diag.range.isSingleLine) { - newEndChar = newStartChar + (diag.range.end.character - diag.range.start.character); + if (origRange.isSingleLine) { + newEndChar = newStartChar + (origRange.end.character - origRange.start.character); } } - diag.range = new vscode.Range(newStartLine, newStartChar, newEndLine, newEndChar); - return diag; + return { newValue: new vscode.Range(newStartLine, newStartChar, newEndLine, newEndChar), overlapsWithChange: false }; } + // At this point, there must be some sort of overlap of the original range with the change, so we set overlapsWithChange to true + // Case: {*[*]*} - // If the entire diagnostic is contained within the change, then we can just remove the diagnostic - if(change.range.start.isBeforeOrEqual(diag.range.start) && change.range.end.isAfterOrEqual(diag.range.end)) { - return null; // Using null to mark for removal + // If the entire original range is contained within the change, then we can just mark the range to be removed + if(change.range.start.isBeforeOrEqual(origRange.start) && change.range.end.isAfterOrEqual(origRange.end)) { + return { newValue: null, overlapsWithChange: true }; // Using null to mark for removal } - // Cases: [*{*]*} or {*[*}*] - // At this point, there must be some sort of overlap of the diagnostic range with the change, so mark it stale: - diag.markStale(); - // Cases: [*{*]*} - // If the change continues past the diagnostic, then we shorten the diagnostic from the right and return - if (change.range.end.isAfterOrEqual(diag.range.end)) { + // If the change continues past the original range, then we shorten the range from the right and return + if (change.range.end.isAfterOrEqual(origRange.end)) { newEndLine = change.range.start.line; newEndChar = change.range.start.character; - diag.range = new vscode.Range(newStartLine, newStartChar, newEndLine, newEndChar); - return diag; + return { newValue: new vscode.Range(newStartLine, newStartChar, newEndLine, newEndChar), overlapsWithChange: true }; } // Cases: {*[*}*] or [*{*}*] - // If the change range's end is within the diagnostic, then we can safely grow or shrink the end line - newEndLine = diag.range.end.line + numLinesDiff; + // If the change range's end is within the original range, then we can safely grow or shrink the end line + newEndLine = origRange.end.line + numLinesDiff; // Cases: {*[*}.] or [*{*}.] - // ... and if the diagnostic ends on the same line that the change ends then we need to adjust the end char as well - if (change.range.end.line === diag.range.end.line) { + // ... and if the original range ends on the same line that the change ends then we need to adjust the end char as well + if (change.range.end.line === origRange.end.line) { const leftPos: number = replacementLines.length > 1 ? 0 : change.range.start.character; const origLen: number = change.range.isSingleLine ? - diag.range.end.character - change.range.start.character : - diag.range.end.character; + origRange.end.character - change.range.start.character : + origRange.end.character; const removalLen: number = change.range.isSingleLine ? change.range.end.character - change.range.start.character : change.range.end.character; @@ -352,8 +472,8 @@ function adjustDiagnosticToChange(diag: CodeAnalyzerDiagnostic, change: vscode.T } // Cases: {*[*}*] - // And if the change starts before the diagnostic starts, we must shorten the diagnostic from the left - if(change.range.start.isBeforeOrEqual(diag.range.start)) { + // And if the change starts before the original range starts, we must shorten the range from the left + if(change.range.start.isBeforeOrEqual(origRange.start)) { newStartLine = change.range.start.line + replacementLines.length - 1; if (replacementLines.length === 1) { newStartChar = change.range.start.character + replacementLines[0].length; @@ -362,6 +482,10 @@ function adjustDiagnosticToChange(diag: CodeAnalyzerDiagnostic, change: vscode.T } } - diag.range = new vscode.Range(newStartLine, newStartChar, newEndLine, newEndChar); - return diag; + return { newValue: new vscode.Range(newStartLine, newStartChar, newEndLine, newEndChar), overlapsWithChange: true }; } + +class Adjustment { + newValue: T | null; // null is a way of marking that there is no new value and thus the old should be removed + overlapsWithChange: boolean; +} \ No newline at end of file diff --git a/src/lib/external-services/org-connection-service.ts b/src/lib/external-services/org-connection-service.ts index bab5aa38..ec816866 100644 --- a/src/lib/external-services/org-connection-service.ts +++ b/src/lib/external-services/org-connection-service.ts @@ -3,7 +3,7 @@ import * as vscode from "vscode"; export interface OrgConnectionService { isAuthed(): boolean; getApiVersion(): Promise; - onOrgChange(callback: () => void): void; + onOrgChange(callback: (orgUserInfo: OrgUserInfo) => void): void; request(requestOptions: HttpRequest): Promise; } @@ -21,7 +21,7 @@ export class NoOpOrgConnectionService implements OrgConnectionService { throw new Error(`Cannot get the api verison because no org is authed.`); } - onOrgChange(_callback: () => void): void { + onOrgChange(_callback: (orgUserInfo: OrgUserInfo) => void): void { // No-op } @@ -38,7 +38,7 @@ export class LiveOrgConnectionService implements OrgConnectionService { } isAuthed(): boolean { - return this.workpaceContext.orgId?.length > 0; + return this.workpaceContext.orgId?.length > 0 && (this.workpaceContext.alias?.length > 0 || this.workpaceContext.username?.length > 0); } async getApiVersion(): Promise { @@ -50,7 +50,7 @@ export class LiveOrgConnectionService implements OrgConnectionService { } - onOrgChange(callback: (event: OrgUserInfo) => void): void { + onOrgChange(callback: (orgUserInfo: OrgUserInfo) => void): void { this.workpaceContext.onOrgChange(callback); } diff --git a/src/lib/messages.ts b/src/lib/messages.ts index 9e3957ab..a132c014 100644 --- a/src/lib/messages.ts +++ b/src/lib/messages.ts @@ -22,6 +22,7 @@ export const messages = { editorCodeLensMustBeEnabled: "This action requires the 'Editor: Code Lens' setting to be enabled." }, apexGuru: { + noOrgAuthed: "No org is authed.", // This message should never show up, but in the unlikely event of a millisecond race condition it theoretically could runningAnalysis: "Code Analyzer is running ApexGuru analysis.", finishedScan: (violationCount: number) => `Scan complete. ${violationCount} violations found.`, warnings: { @@ -61,7 +62,8 @@ export const messages = { messageGenerator: (severity: number, message: string) => `Sev${severity}: ${message}`, source: { suffix: 'via Code Analyzer' - } + }, + defaultAlternativeLocationMessage: 'This location is also associated with the violation.' }, targeting: { error: { diff --git a/src/lib/settings.ts b/src/lib/settings.ts index fc887832..1096a607 100644 --- a/src/lib/settings.ts +++ b/src/lib/settings.ts @@ -10,7 +10,6 @@ export interface SettingsManager { // General Settings getAnalyzeOnOpen(): boolean; getAnalyzeOnSave(): boolean; - getApexGuruEnabled(): boolean; // Configuration Settings getCodeAnalyzerConfigFile(): string; @@ -32,10 +31,6 @@ export class SettingsManagerImpl implements SettingsManager { return vscode.workspace.getConfiguration('codeAnalyzer.analyzeOnSave').get('enabled'); } - public getApexGuruEnabled(): boolean { - return vscode.workspace.getConfiguration('codeAnalyzer.apexGuru').get('enabled'); - } - // ================================================================================================================= // ==== Configuration Settings // ================================================================================================================= diff --git a/src/test/legacy/settings.test.ts b/src/test/legacy/settings.test.ts index 332052fb..15129ef7 100644 --- a/src/test/legacy/settings.test.ts +++ b/src/test/legacy/settings.test.ts @@ -50,19 +50,4 @@ suite('SettingsManager Test Suite', () => { expect(result).to.equal(mockAnalyzeOnOpenEnabled); expect(getConfigurationStub.calledOnceWith('codeAnalyzer.analyzeOnOpen')).to.equal(true); }); - - test('getApexGuruEnabled should return the apexGuru enabled setting', () => { - // ===== SETUP ===== - const mockAnalyzeOnSaveEnabled = true; - getConfigurationStub.withArgs('codeAnalyzer.apexGuru').returns({ - get: Sinon.stub().returns(mockAnalyzeOnSaveEnabled) - }); - - // ===== TEST ===== - const result = new SettingsManagerImpl().getApexGuruEnabled(); - - // ===== ASSERTIONS ===== - expect(result).to.equal(mockAnalyzeOnSaveEnabled); - expect(getConfigurationStub.calledOnceWith('codeAnalyzer.apexGuru')).to.equal(true); - }); }); diff --git a/src/test/unit/lib/apexguru/apex-guru-run-action.test.ts b/src/test/unit/lib/apexguru/apex-guru-run-action.test.ts index 5b9bcbbf..23a0ec3d 100644 --- a/src/test/unit/lib/apexguru/apex-guru-run-action.test.ts +++ b/src/test/unit/lib/apexguru/apex-guru-run-action.test.ts @@ -5,6 +5,7 @@ import { CodeAnalyzerDiagnostic, DiagnosticManager, DiagnosticManagerImpl, Viola import { FakeDiagnosticCollection } from "../../vscode-stubs"; import { ApexGuruRunAction } from "../../../../lib/apexguru/apex-guru-run-action"; import { createSampleCodeAnalyzerDiagnostic } from "../../test-utils"; +import { ApexGuruAccess } from "../../../../lib/apexguru/apex-guru-service"; describe("Tests for ApexGuruRunAction", () => { const sampleUri: vscode.Uri = vscode.Uri.file('/some/file.cls'); @@ -35,7 +36,7 @@ describe("Tests for ApexGuruRunAction", () => { it("When ApexGuru scan throws error, then display error in error window and send exception telemetry event", async () => { apexGuruRunAction = new ApexGuruRunAction( - taskWithProgressRunner, new stubs.ThrowingApexGuruService(), diagnosticManager, telemetryService, display); + taskWithProgressRunner, new stubs.ThrowingScanApexGuruService(), diagnosticManager, telemetryService, display); await apexGuruRunAction.run('SomeCommandName', sampleUri); @@ -59,6 +60,33 @@ describe("Tests for ApexGuruRunAction", () => { expect(diagnosticManager.getDiagnosticsForFile(sampleUri)).toEqual([samplePmdDiag, sampleApexGuruDiag]); }); + it("When user's org is eligible but not enabled, then ApexGuru scan button results in an error window with instructions", async () => { + apexGuruService.getAvailabilityReturnValue = { + access: ApexGuruAccess.ELIGIBLE, + message: "Some instructions from ApexGuru" + }; + + await apexGuruRunAction.run('SomeCommandName', sampleUri); + + // No progress bar should be shown + expect(taskWithProgressRunner.progressReporter.reportProgressCallHistory).toHaveLength(0); + + // A error dialog should be given with the instructions that were sent from the ApexGuru service + expect(display.displayErrorCallHistory).toHaveLength(1); + expect(display.displayErrorCallHistory[0].msg).toEqual('Some instructions from ApexGuru'); + + // Then telemetry should have been sent + expect(telemetryService.sendCommandEventCallHistory).toHaveLength(1); + expect(telemetryService.sendCommandEventCallHistory[0].commandName).toEqual('sfdx__apexguru_file_run_not_enabled'); + expect(telemetryService.sendCommandEventCallHistory[0].properties).toEqual({ + access: 'eligible-but-not-enabled', + executedCommand: 'SomeCommandName' + }); + + // Also validate that we didn't modify the existing diagnostics at all + expect(diagnosticManager.getDiagnosticsForFile(sampleUri)).toEqual([samplePmdDiag, sampleApexGuruDiag]); + }); + it("When ApexGuru scan results in zero violations, then display this information, send telemetry event, and update diagnostics", async () => { apexGuruService.scanReturnValue = []; diff --git a/src/test/unit/lib/apexguru/apex-guru-service.test.ts b/src/test/unit/lib/apexguru/apex-guru-service.test.ts index 28b0bc13..66680b49 100644 --- a/src/test/unit/lib/apexguru/apex-guru-service.test.ts +++ b/src/test/unit/lib/apexguru/apex-guru-service.test.ts @@ -1,10 +1,45 @@ -import { ApexGuruService, LiveApexGuruService } from "../../../../lib/apexguru/apex-guru-service"; -import { HttpRequest, OrgConnectionService } from "../../../../lib/external-services/org-connection-service"; +import { ApexGuruAccess, ApexGuruService, LiveApexGuruService } from "../../../../lib/apexguru/apex-guru-service"; +import { HttpRequest, OrgConnectionService, OrgUserInfo } from "../../../../lib/external-services/org-connection-service"; import * as stubs from "../../stubs"; import { Violation } from "../../../../lib/diagnostics"; +import { expectEventuallyIsTrue } from "../../test-utils"; describe("Tests for LiveApexGuruService", () => { const sampleFile: string = '/some/file.cls'; + const sampleContent: string = + `public class ConsolidatedClass {\n` + + ` public static void processAccountsAndContacts(List accounts) {\n` + + ` // Antipattern [Avoid using Schema.getGlobalDescribe() in Apex]: (has fix)\n` + + ` Schema.DescribeSObjectResult opportunityDescribe = Schema.getGlobalDescribe().get('Opportunity').getDescribe();\n` + + ` System.debug('Opportunity Describe: ' + opportunityDescribe);\n` + + `\n` + + ` for (Account acc : accounts) {\n` + + ` // Antipattern [SOQL in loop]:\n` + + ` List contacts = [SELECT Id, Email FROM Contact WHERE AccountId = :acc.Id];\n` + + ` for (Contact con : contacts) {\n` + + ` con.Email = 'newemail@example.com';\n` + + ` // Antipattern [DML in loop]:\n` + + ` update con;\n` + + ` }\n` + + ` }\n` + + `\n` + + ` // Antipattern [SOQL with negative expression]:\n` + + ` List contactsNotInUS = [SELECT Id, FirstName, LastName FROM Contact WHERE MailingCountry != 'US'];\n` + + ` System.debug('Contacts not in US: ' + contactsNotInUS);\n` + + `\n` + + ` // Antipattern [SOQL without WHERE clause or LIMIT]:\n` + + ` List allAccounts = [SELECT Id, Name FROM Account];\n` + + ` System.debug('All Accounts: ' + allAccounts);\n` + + `\n` + + ` // Antipattern [Using a list of SObjects for an IN-bind to ID in a SOQL]: (has suggestion)\n` + + ` List contactsFromAccounts = [SELECT Id, FirstName, LastName FROM Contact WHERE AccountId IN :accounts];\n` + + ` System.debug('Contacts from Accounts: ' + contactsFromAccounts);\n` + + `\n` + + ` // Antipattern [SOQL with wildcard filters]:\n` + + ` List accountsWithWildcard = [SELECT Id, Name FROM Account WHERE Name LIKE '%Corp%'];\n` + + ` System.debug('Accounts with wildcard: ' + accountsWithWildcard);\n` + + ` }\n` + + `}`; const sampleApexGuruPayload = [ { @@ -68,6 +103,9 @@ describe("Tests for LiveApexGuruService", () => { { file: sampleFile, startLine: 4, + startColumn: 1, + endLine: 4, + endColumn: 120, comment: "api_class.processAccountsAndContacts", } ], @@ -82,6 +120,8 @@ describe("Tests for LiveApexGuruService", () => { file: sampleFile, startLine: 4, startColumn: 8, + endLine: 4, + endColumn: 120, comment: "api_class.processAccountsAndContacts", }, fixedCode: "Schema.DescribeSObjectResult opportunityDescribe = Opportunity.sObjectType.getDescribe();", @@ -97,6 +137,9 @@ describe("Tests for LiveApexGuruService", () => { { file: sampleFile, startLine: 7, + startColumn: 1, + endLine: 7, + endColumn: 39, comment: "api_class.processAccountsAndContacts" } ], @@ -110,6 +153,9 @@ describe("Tests for LiveApexGuruService", () => { location: { file: sampleFile, startLine: 7, + startColumn: 1, + endLine: 7, + endColumn: 39, comment: "api_class.processAccountsAndContacts", }, message: "Sample suggestion message", @@ -126,31 +172,74 @@ describe("Tests for LiveApexGuruService", () => { beforeEach(() => { orgConnectionService = new StubOrgConnectionServiceForApexGuru(); fileHandler = new stubs.StubFileHandler(); - fileHandler.readFileReturnValue = 'dummyContent'; + fileHandler.readFileReturnValue = sampleContent; logger = new stubs.SpyLogger(); const maxTimeOutSecs: number = 3; // Defaulting to 3 seconds for worse case scenario, but the below tests shouldn't depend on it const retryIntervalMillis: number = 5; // Reducing to keep polling based tests fast apexGuruService = new LiveApexGuruService(orgConnectionService, fileHandler, logger, maxTimeOutSecs, retryIntervalMillis); }); - describe("Tests for isApexGuruAvailable", () => { - it("When no org is authed, then return false", async () => { + describe("Tests for updateAvailability and getAvailability", () => { + it('When getAvailability is called before updateAvailability (which should never happen in production), then error', () => { + expect(() => apexGuruService.getAvailability()).toThrow( + 'The getAvailability method should not be called until updateAvailability is first called'); + }); + + it('When no org is authed, then return NOT_AUTHED availability', async () => { orgConnectionService.isAuthedReturnValue = false; - expect(await apexGuruService.isApexGuruAvailable()).toEqual(false); + await apexGuruService.updateAvailability(); + expect(apexGuruService.getAvailability()).toEqual({ + access: ApexGuruAccess.NOT_AUTHED, + message: "No org is authed." + }); + }); + + it('When the ApexGuru validate endpoint returns an error status, then return INELIGIBLE availability', async () => { + orgConnectionService.requestReturnValueForAuthValidation = { + status: "error", + message: "some error message" + }; + await apexGuruService.updateAvailability(); + expect(apexGuruService.getAvailability()).toEqual({ + access: ApexGuruAccess.INELIGIBLE, + message: "some error message" + }); }); - it('When the ApexGuru validate endpoint does not return success, then return false', async () => { + it('When the ApexGuru validate endpoint returns an failed status without a message, then we fill in with our own message', async () => { + // In production this should never happen. Just testing this case to make sure things don't blow up and we + // do something reasonable. orgConnectionService.requestReturnValueForAuthValidation = { status: "failed" }; - expect(await apexGuruService.isApexGuruAvailable()).toEqual(false); + await apexGuruService.updateAvailability(); + expect(apexGuruService.getAvailability()).toEqual({ + access: ApexGuruAccess.ELIGIBLE, + message: "ApexGuru access is not enabled. Response: {\"status\":\"failed\"}" + }); + }); + + it('When the ApexGuru validate endpoint returns a failed status, then return ELIGIBLE availability', async () => { + orgConnectionService.requestReturnValueForAuthValidation = { + status: "failed", + message: "some instruction on how to enable ApexGuru" + }; + await apexGuruService.updateAvailability(); + expect(apexGuruService.getAvailability()).toEqual({ + access: ApexGuruAccess.ELIGIBLE, + message: "some instruction on how to enable ApexGuru" + }); }); - it('When the ApexGuru validate endpoint returns success, then return true', async () => { + it('When the ApexGuru validate endpoint returns success, then return ENABLED availability', async () => { orgConnectionService.requestReturnValueForAuthValidation = { status: "SUccesS" // Also testing that we check with case insensitivity to be more robust }; - expect(await apexGuruService.isApexGuruAvailable()).toEqual(true); + await apexGuruService.updateAvailability(); + expect(apexGuruService.getAvailability()).toEqual({ + access: ApexGuruAccess.ENABLED, + message: "ApexGuru access is enabled." + }); // Sanity check that the right endpoint was used expect(orgConnectionService.requestCallHistory).toHaveLength(1); @@ -159,6 +248,41 @@ describe("Tests for LiveApexGuruService", () => { url: "/services/data/v64.0/apexguru/validate" }); }); + + it("When an org auth change occurs, then the updateAvailability method should automatically be called", async () => { + expect(orgConnectionService.onOrgChangeCallHistory).toHaveLength(1); + const triggerOrgChangeCallback = orgConnectionService.onOrgChangeCallHistory[0].callback; + + // The source code uses async behavior, so we use expectEventuallyIsTrue to help test that something evenutally happes + orgConnectionService.isAuthedReturnValue = false; + triggerOrgChangeCallback({alias: 'org1'}); + await expectEventuallyIsTrue(() => apexGuruService.getAvailability().access === ApexGuruAccess.NOT_AUTHED); + + orgConnectionService.isAuthedReturnValue = true; + triggerOrgChangeCallback({alias: 'org2'}); + await expectEventuallyIsTrue(() => apexGuruService.getAvailability().access === ApexGuruAccess.ENABLED); + }); + + it("When the updateAvailability method is called and changes the access level, then the onAccessChange method is called", async () => { + let latestAccess: ApexGuruAccess | undefined = undefined; + apexGuruService.onAccessChange((access: ApexGuruAccess) => { + latestAccess = access; + }); + await apexGuruService.updateAvailability(); + + expect(latestAccess).toEqual(ApexGuruAccess.ENABLED); + }); + + it("When the updateAvailability method is called but does not change the access level, then the onAccessChange method is not called", async () => { + let wasCalled: boolean = false; + await apexGuruService.updateAvailability(); // First set it + apexGuruService.onAccessChange((_access: ApexGuruAccess) => { + wasCalled = true; + }); + + await apexGuruService.updateAvailability(); // Call it again ... + expect(wasCalled).toEqual(false); // ... since nothing changed this should not have been called + }); }); describe("Tests for scan", () => { @@ -309,8 +433,8 @@ export class StubOrgConnectionServiceForApexGuru implements OrgConnectionService return this.isAuthedReturnValue; } - onOrgChangeCallHistory: {callback: () => void}[] = []; - onOrgChange(callback: () => void): void { + onOrgChangeCallHistory: {callback: (orgUserInfo: OrgUserInfo) => void}[] = []; + onOrgChange(callback: (orgUserInfo: OrgUserInfo) => void): void { this.onOrgChangeCallHistory.push({callback}); } diff --git a/src/test/unit/lib/code-analyzer-run-action.test.ts b/src/test/unit/lib/code-analyzer-run-action.test.ts index 32d89b82..dcd19c94 100644 --- a/src/test/unit/lib/code-analyzer-run-action.test.ts +++ b/src/test/unit/lib/code-analyzer-run-action.test.ts @@ -8,12 +8,13 @@ import { SpyWindowManager, StubCodeAnalyzer, StubFileHandler, StubVscodeWorkspace } from "../stubs"; -import {CodeLocation, DiagnosticManager, DiagnosticManagerImpl, Violation} from "../../../lib/diagnostics"; +import {CodeAnalyzerDiagnostic, CodeLocation, DiagnosticManager, DiagnosticManagerImpl, Violation} from "../../../lib/diagnostics"; import {FakeDiagnosticCollection} from "../vscode-stubs"; import {CodeAnalyzerRunAction, UNINSTANTIABLE_ENGINE_RULE} from "../../../lib/code-analyzer-run-action"; import {messages} from "../../../lib/messages"; import * as Constants from '../../../lib/constants'; import {Workspace} from "../../../lib/workspace"; +import { APEX_GURU_ENGINE_NAME } from "../../../lib/apexguru/apex-guru-service"; describe('Tests for CodeAnalyzerRunAction', () => { let sampleWorkspace: Workspace; @@ -88,6 +89,40 @@ describe('Tests for CodeAnalyzerRunAction', () => { ]); }); + it('When scan happens, it should clear old Code Analyzer diagnostics before setting new ones but keep existing ApexGuru diagnostics', async () => { + diagnosticManager.addDiagnostics([ + CodeAnalyzerDiagnostic.fromViolation({ + rule: 'dummyRule1', + engine: APEX_GURU_ENGINE_NAME, + message: 'messageFromApexGuru', + severity: 3, + locations: [{file: 'someFile.cls'}], + primaryLocationIndex: 0, + tags: [], + resources: [] + }), + CodeAnalyzerDiagnostic.fromViolation({ + rule: 'dummyRule1', + engine: 'pmd', + message: 'messageFromPmd', + severity: 3, + locations: [{file: 'someFile.cls'}], + primaryLocationIndex: 0, + tags: [], + resources: [] + }) + ]); + codeAnalyzer.scanReturnValue = [ + createSampleViolation('New', 2, [{file: 'someFile.cls'}]), // Is sufficient to make this into a diagnostic + ]; + const workspace: Workspace = await Workspace.fromTargetPaths(['someFile.cls'], new StubVscodeWorkspace(), new StubFileHandler()); + await codeAnalyzerRunAction.run('dummyCommandName', workspace); + + const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(vscode.Uri.file('someFile.cls')) as CodeAnalyzerDiagnostic[]; + expect(resultingDiags).toHaveLength(2); + expect(resultingDiags.map(d => d.message)).toEqual(['Sev3: messageFromApexGuru', 'Sev2: messageNew']); + }); + it('When an engine cannot be initialized and user ignores the message, then do not show that exact message again', async () => { const engine = 'flow'; codeAnalyzer.scanReturnValue = [ diff --git a/src/test/unit/lib/diagnostics-handleTextDocumentChangeEvent.test.ts b/src/test/unit/lib/diagnostics-handleTextDocumentChangeEvent.test.ts index 1881c241..621a4f40 100644 --- a/src/test/unit/lib/diagnostics-handleTextDocumentChangeEvent.test.ts +++ b/src/test/unit/lib/diagnostics-handleTextDocumentChangeEvent.test.ts @@ -1,7 +1,7 @@ import * as vscode from "vscode"; // The vscode module is mocked out. See: scripts/setup.jest.ts import {createTextDocument} from "jest-mock-vscode"; import {FakeDiagnosticCollection} from "../vscode-stubs"; -import {CodeAnalyzerDiagnostic, DiagnosticManager, DiagnosticManagerImpl} from "../../../lib/diagnostics"; +import {CodeAnalyzerDiagnostic, CodeLocation, DiagnosticManager, DiagnosticManagerImpl, Violation} from "../../../lib/diagnostics"; import {createSampleCodeAnalyzerDiagnostic} from "../test-utils"; /* @@ -11,6 +11,7 @@ import {createSampleCodeAnalyzerDiagnostic} from "../test-utils"; describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEvent method`, () => { const sampleUri: vscode.Uri = vscode.Uri.file('/someFile.cls'); + const sampleUri2: vscode.Uri = vscode.Uri.file('/someOtherFile.cls'); const sampleLines: string[] = [ 'This is line 0.', 'And this is line 1.', @@ -86,7 +87,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag1, diag2]); // Same diagnostic instances + expect(resultingDiags.map(d => d.message)).toEqual([diag1.message, diag2.message]); // Same diagnostic messages expect(resultingDiags.map(d => d.range)).toEqual([range1, range2]); // Should still have same ranges expect(resultingDiags.map(d => d.isStale())).toEqual([false, false]); // ... that should not be stale }); @@ -103,7 +104,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag1, diag2]); // Same diagnostic instances + expect(resultingDiags.map(d => d.message)).toEqual([diag1.message, diag2.message]); // Same diagnostic messages expect(resultingDiags.map(d => d.range)).toEqual([ // Ranges should have changed new vscode.Range(2, 1, 2, 3), new vscode.Range(2, 5, 3, 2)]); expect(resultingDiags.map(d => d.isStale())).toEqual([false, false]); // ... that should not be stale @@ -121,7 +122,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag1, diag2]); // Same diagnostic instances + expect(resultingDiags.map(d => d.message)).toEqual([diag1.message, diag2.message]); // Same diagnostic messages expect(resultingDiags.map(d => d.range)).toEqual([ // Only second range should have changed range1, new vscode.Range(2, 5, 3, 2)]); expect(resultingDiags.map(d => d.isStale())).toEqual([false, false]); // ... that should not be stale @@ -152,7 +153,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(1, 0, 1, 4)); // ... with same range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -167,7 +168,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(2, 0, 2, 4)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -182,7 +183,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(1, 0, 1, 4)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -197,7 +198,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(2, 0, 2, 4)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -212,7 +213,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(1, 0, 3, 4)); // ... with same range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -227,7 +228,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(2, 0, 4, 4)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -242,7 +243,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(1, 0, 2, 4)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -257,7 +258,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(2, 0, 3, 4)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -272,7 +273,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(2, 4, 2, 9)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -287,7 +288,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(3, 12, 3, 17)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -302,7 +303,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 12, 0, 15)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -317,7 +318,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(3, 5, 3, 8)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -332,7 +333,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(2, 8, 4, 2)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -347,7 +348,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(5, 4, 7, 2)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -362,7 +363,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 5, 1, 5)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -377,7 +378,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(1, 8, 2, 5)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -392,7 +393,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(1, 13, 1, 18)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -407,7 +408,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(3, 4, 3, 9)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -422,7 +423,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 4, 0, 9)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -437,7 +438,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(1, 5, 1, 10)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -452,7 +453,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 28, 1, 6)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -467,7 +468,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(2, 5, 2, 9)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -482,7 +483,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(1, 7, 3, 6)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -497,7 +498,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(5, 0, 7, 6)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -512,7 +513,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(4, 2, 4, 40)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -527,7 +528,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(4, 2, 5, 29)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -542,7 +543,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 0, 4, 35)); // ... with the same range (only because replacement text was same size as change range) ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -557,7 +558,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 1, 6, 29)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -572,7 +573,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 1, 0, 25)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -587,7 +588,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 1, 1, 33)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -602,7 +603,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(2, 2, 2, 6)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -617,7 +618,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(2, 2, 2, 6)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -632,7 +633,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(1, 6, 2, 0)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -647,7 +648,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(1, 6, 2, 0)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -662,7 +663,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(1, 6, 2, 9)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -677,7 +678,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(1, 6, 2, 9)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -692,7 +693,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 2, 0, 4)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -707,7 +708,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 2, 0, 4)); // ... with fixed range ... expect(resultingDiags[0].isStale()).toEqual(true); // ... that should be stale }); @@ -722,7 +723,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 2, 0, 6)); // ... with same range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -737,7 +738,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 2, 0, 6)); // ... with same range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -752,7 +753,7 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; - expect(resultingDiags).toEqual([diag]); // Same diagnostic instance ... + expect(resultingDiags.map(d => d.message)).toEqual([diag.message]); // Same diagnostic message ... expect(resultingDiags[0].range).toEqual(new vscode.Range(0, 2, 0, 6)); // ... with same range ... expect(resultingDiags[0].isStale()).toEqual(false); // ... that should not be stale }); @@ -783,4 +784,373 @@ describe(`Tests for the the DiagnosticManager class's handleTextDocumentChangeEv expect(resultingDiags).toEqual([]); // Diagnostic instance should be removed }); }); + + describe('Tests to ensure violation locations inside of diagnostic get updated when applicable', () => { + it('When diagnostic contains only a primary location, and change is after, then location remains unchanged', () => { + const diag: CodeAnalyzerDiagnostic = createSampleCodeAnalyzerDiagnostic(sampleUri, new vscode.Range(0, 2, 1, 6)); + diagnosticManager.addDiagnostics([diag]); + + const docChangeEvent: vscode.TextDocumentChangeEvent = createTextDocumentChangeEventWith( + new vscode.Range(5, 0, 4, 0), 'hello\nworld'); + diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); + + const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; + expect(resultingDiags).toHaveLength(1); + expect(resultingDiags[0].violation.locations).toHaveLength(1); + expect(resultingDiags[0].violation.locations[0]).toEqual({ + file: sampleUri.fsPath, + startLine: 1, + startColumn: 3, + endLine: 2, + endColumn: 7 + }); + }); + + it('When diagnostic contains only a primary location, and change overlaps, then location updates accordingly', () => { + const diag: CodeAnalyzerDiagnostic = createSampleCodeAnalyzerDiagnostic(sampleUri, new vscode.Range(0, 2, 1, 6)); + diagnosticManager.addDiagnostics([diag]); + + const docChangeEvent: vscode.TextDocumentChangeEvent = createTextDocumentChangeEventWith( + new vscode.Range(0, 2, 0, 3), 'hello'); + diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); + + const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; + expect(resultingDiags).toHaveLength(1); + expect(resultingDiags[0].violation.locations).toHaveLength(1); + expect(resultingDiags[0].violation.locations[0]).toEqual({ + file: sampleUri.fsPath, + startLine: 1, + startColumn: 8, // Was at 3, but should increase by 5 to equal 8 + endLine: 2, + endColumn: 7 + }); + }); + + it('When diagnostic contains only a primary location, and change is before, then location updates accordingly', () => { + const diag: CodeAnalyzerDiagnostic = createSampleCodeAnalyzerDiagnostic(sampleUri, new vscode.Range(0, 2, 1, 6)); + diagnosticManager.addDiagnostics([diag]); + + const docChangeEvent: vscode.TextDocumentChangeEvent = createTextDocumentChangeEventWith( + new vscode.Range(0, 0, 0, 1), 'hello\nworld'); // adds a new line and inserts new characters to replace first char + diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); + + const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; + expect(resultingDiags).toHaveLength(1); + expect(resultingDiags[0].violation.locations).toHaveLength(1); + expect(resultingDiags[0].violation.locations[0]).toEqual({ + file: sampleUri.fsPath, + startLine: 2, + startColumn: 7, // Should increase by 4 + endLine: 3, // Should increase by 1 + endColumn: 7 + }); + }); + + it('When diagnostic contains another location but from another file, then that other location is untouched', () => { + const primaryLocation = { + file: sampleUri.fsPath, + startLine: 7, + startColumn: 1, + endLine: 7 + }; + const otherLocation: CodeLocation = { + file: sampleUri2.fsPath, + startLine: 1, + startColumn: 4, + endLine: 1, + endColumn: 5 + }; + const violation: Violation = { + rule: 'dummyRule', + engine: 'pmd', + message: 'Some dummy violation message', + severity: 3, + locations: [primaryLocation, otherLocation], + primaryLocationIndex: 0, + tags: [], + resources: [], + fixes: [], + suggestions: [] + } + const diag: CodeAnalyzerDiagnostic = CodeAnalyzerDiagnostic.fromViolation(violation); + diagnosticManager.addDiagnostics([diag]); + + const docChangeEvent: vscode.TextDocumentChangeEvent = createTextDocumentChangeEventWith( + new vscode.Range(0, 0, 0, 2), 'hello'); + diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); + + const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; + expect(resultingDiags).toHaveLength(1); + expect(resultingDiags[0].violation.locations).toHaveLength(2); + expect(resultingDiags[0].violation.locations[0]).toEqual(primaryLocation); + expect(resultingDiags[0].violation.locations[1]).toEqual(otherLocation); + expect(resultingDiags[0].relatedInformation).toHaveLength(1); + expect(resultingDiags[0].relatedInformation[0].location.uri.fsPath).toEqual(sampleUri2.fsPath); + expect(resultingDiags[0].relatedInformation[0].location.range).toEqual(new vscode.Range(0, 3, 0, 4)); + }); + + it('When diagnostic contains another location from the same file, and change is after, then that location is untouched', () => { + const primaryLocation = { + file: sampleUri.fsPath, + startLine: 7, + startColumn: 1, + endLine: 7 + }; + const otherLocation: CodeLocation = { + file: sampleUri.fsPath, + startLine: 1, + startColumn: 1, + endLine: 1, + endColumn: 5 + }; + const violation: Violation = { + rule: 'dummyRule', + engine: 'pmd', + message: 'Some dummy violation message', + severity: 3, + locations: [primaryLocation, otherLocation], + primaryLocationIndex: 0, + tags: [], + resources: [], + fixes: [], + suggestions: [] + } + const diag: CodeAnalyzerDiagnostic = CodeAnalyzerDiagnostic.fromViolation(violation); + diagnosticManager.addDiagnostics([diag]); + + const docChangeEvent: vscode.TextDocumentChangeEvent = createTextDocumentChangeEventWith( + new vscode.Range(0, 7, 0, 9), 'hello'); + diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); + + const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; + expect(resultingDiags).toHaveLength(1); + expect(resultingDiags[0].violation.locations).toHaveLength(2); + expect(resultingDiags[0].violation.locations[0]).toEqual(primaryLocation); + expect(resultingDiags[0].violation.locations[1]).toEqual(otherLocation); + expect(resultingDiags[0].relatedInformation).toEqual(diag.relatedInformation); + }); + + it('When diagnostic contains another location from the same file, and change is before, then that location is updated', () => { + const primaryLocation = { + file: sampleUri.fsPath, + startLine: 7, + startColumn: 1, + endLine: 7 + }; + const otherLocation: CodeLocation = { + file: sampleUri.fsPath, + startLine: 1, + startColumn: 4, + endLine: 1, + endColumn: 5 + }; + const violation: Violation = { + rule: 'dummyRule', + engine: 'pmd', + message: 'Some dummy violation message', + severity: 3, + locations: [primaryLocation, otherLocation], + primaryLocationIndex: 0, + tags: [], + resources: [], + fixes: [], + suggestions: [] + } + const diag: CodeAnalyzerDiagnostic = CodeAnalyzerDiagnostic.fromViolation(violation); + diagnosticManager.addDiagnostics([diag]); + + const docChangeEvent: vscode.TextDocumentChangeEvent = createTextDocumentChangeEventWith( + new vscode.Range(0, 0, 0, 2), 'hello'); + diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); + + const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; + expect(resultingDiags).toHaveLength(1); + expect(resultingDiags[0].violation.locations).toHaveLength(2); + expect(resultingDiags[0].violation.locations[0]).toEqual(primaryLocation); + expect(resultingDiags[0].violation.locations[1]).toEqual({ + file: sampleUri.fsPath, + startLine: 1, + startColumn: 7, + endLine: 1, + endColumn: 8 + }); + expect(resultingDiags[0].relatedInformation).toHaveLength(1); + expect(resultingDiags[0].relatedInformation[0].location.uri.fsPath).toEqual(sampleUri.fsPath); + expect(resultingDiags[0].relatedInformation[0].location.range).toEqual(new vscode.Range(0, 6, 0, 7)); + }); + + it('When diagnostic contains another location from the same file, and change exactly overlaps location, then that location is removed', () => { + const otherLocation: CodeLocation = { + file: sampleUri.fsPath, + startLine: 1, + startColumn: 4, + endLine: 1, + endColumn: 5 + }; + const primaryLocation = { + file: sampleUri.fsPath, + startLine: 7, + startColumn: 1, + endLine: 7 + }; + const violation: Violation = { + rule: 'dummyRule', + engine: 'pmd', + message: 'Some dummy violation message', + severity: 3, + // Putting primary location after other location so that when it is removed we confirm primary location index is updated + locations: [otherLocation, primaryLocation], + primaryLocationIndex: 1, + tags: [], + resources: [], + fixes: [], + suggestions: [] + } + const diag: CodeAnalyzerDiagnostic = CodeAnalyzerDiagnostic.fromViolation(violation); + diagnosticManager.addDiagnostics([diag]); + + const docChangeEvent: vscode.TextDocumentChangeEvent = createTextDocumentChangeEventWith( + new vscode.Range(0, 3, 0, 4), 'hello'); + diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); + + const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; + expect(resultingDiags).toHaveLength(1); + expect(resultingDiags[0].violation.primaryLocationIndex).toEqual(0); + expect(resultingDiags[0].violation.locations).toHaveLength(1); + expect(resultingDiags[0].violation.locations[0]).toEqual(primaryLocation); + expect(resultingDiags[0].relatedInformation).toBeUndefined(); + }); + }); + + describe('Tests to ensure violation fixes and suggestions have their location updated upon change', () => { + it('When fixes and suggestions exist, then their locations update only if impacted', () => { + const primaryLocation = { + file: sampleUri.fsPath, + startLine: 7, + startColumn: 1, + endLine: 7 + }; + const violation1: Violation = { + rule: 'dummyRule', + engine: 'pmd', + message: 'Some dummy violation message', + severity: 3, + locations: [primaryLocation], + primaryLocationIndex: 0, + tags: [], + resources: [], + fixes: [ + { + fixedCode: 'fix1', + location: { // Should not be changed because it is before the change + file: sampleUri.fsPath, + startLine: 1, + startColumn: 1, + endLine: 1, + endColumn: 2 + } + }, + { + fixedCode: 'fix2', + location: { // Should not be changed because it is a fix in a different file + file: sampleUri2.fsPath, + startLine: 1 + } + } + ], + suggestions: [{ + message: 'suggestion1', + location: { // Should be removed since the line has been altered + file: sampleUri.fsPath, + startLine: 1 + } + }] + }; + const violation2: Violation = { + rule: 'dummyRule2', + engine: 'eslint', + message: 'Some dummy violation message 2', + severity: 4, + locations: [primaryLocation], + primaryLocationIndex: 0, + tags: [], + resources: [], + fixes: [ + { + fixedCode: 'fix3', + location: { // Should be shifted to the right + file: sampleUri.fsPath, + startLine: 1, + startColumn: 8 + } + } + ], + suggestions: [ + { + message: 'suggestion2', + location: { // Should not be changed because it is a fix in a different file + file: sampleUri2.fsPath, + startLine: 1 + }, + }, + { + message: 'suggestion3', + location: { // Should be removed because it was altered + file: sampleUri.fsPath, + startLine: 1, + startColumn: 1, + endColumn: 5 + }, + }, + ] + }; + const diag1: CodeAnalyzerDiagnostic = CodeAnalyzerDiagnostic.fromViolation(violation1); + const diag2: CodeAnalyzerDiagnostic = CodeAnalyzerDiagnostic.fromViolation(violation2); + diagnosticManager.addDiagnostics([diag1, diag2]); + + const docChangeEvent: vscode.TextDocumentChangeEvent = createTextDocumentChangeEventWith( + new vscode.Range(0, 3, 0, 6), 'hello'); + diagnosticManager.handleTextDocumentChangeEvent(docChangeEvent); + + const resultingDiags: CodeAnalyzerDiagnostic[] = diagnosticCollection.get(sampleUri) as CodeAnalyzerDiagnostic[]; + expect(resultingDiags).toHaveLength(2); + expect(resultingDiags[0].violation.fixes).toHaveLength(2); + expect(resultingDiags[0].violation.fixes[0]).toEqual({ + fixedCode: 'fix1', + location: { // Should not be changed because it is before the change + file: sampleUri.fsPath, + startLine: 1, + startColumn: 1, + endLine: 1, + endColumn: 2 + } + }); + expect(resultingDiags[0].violation.fixes[1]).toEqual({ + fixedCode: 'fix2', + location: { // Should not be changed because it is a fix in a different file + file: sampleUri2.fsPath, + startLine: 1 + } + }); + expect(resultingDiags[0].violation.suggestions).toHaveLength(0); + expect(resultingDiags[1].violation.fixes).toHaveLength(1); + expect(resultingDiags[1].violation.fixes[0]).toEqual({ + fixedCode: 'fix3', + location: { + file: sampleUri.fsPath, + startLine: 1, + startColumn: 10, // Correctly shifted to the right by 2 + endLine: 1 + } + }); + expect(resultingDiags[1].violation.suggestions).toHaveLength(1); + expect(resultingDiags[1].violation.suggestions[0]).toEqual({ + message: 'suggestion2', + location: { // Should not be changed because it is a fix in a different file + file: sampleUri2.fsPath, + startLine: 1 + } + }) + }); + }); }); diff --git a/src/test/unit/lib/diagnostics.test.ts b/src/test/unit/lib/diagnostics.test.ts index 39cb01b6..e1acdfe1 100644 --- a/src/test/unit/lib/diagnostics.test.ts +++ b/src/test/unit/lib/diagnostics.test.ts @@ -74,6 +74,7 @@ describe('Tests for the CodeAnalyzerDiagnostic class', () => { }, { file: '/path/to/some/someFileButNoLineInfo.cls', + comment: 'someDummyComment' }, { file: '/path/to/some/someFileWithSomeLineInfo.cls', @@ -102,17 +103,17 @@ describe('Tests for the CodeAnalyzerDiagnostic class', () => { new vscode.Location( vscode.Uri.file('/path/to/some/someFile.cls'), new vscode.Range(0, 1, 2, 3)), - '')); + messages.diagnostics.defaultAlternativeLocationMessage)); expect(diag.relatedInformation[1]).toEqual(new vscode.DiagnosticRelatedInformation( new vscode.Location( vscode.Uri.file('/path/to/some/someFileButNoLineInfo.cls'), - new vscode.Range(0, 0, 0, Number.MAX_SAFE_INTEGER)), - '')); + new vscode.Range(0, 0, 0, Number.MAX_SAFE_INTEGER-1)), + 'someDummyComment')); expect(diag.relatedInformation[2]).toEqual(new vscode.DiagnosticRelatedInformation( new vscode.Location( vscode.Uri.file('/path/to/some/someFileWithSomeLineInfo.cls'), - new vscode.Range(0, 0, 17, Number.MAX_SAFE_INTEGER)), - '')); + new vscode.Range(0, 0, 17, Number.MAX_SAFE_INTEGER-1)), + messages.diagnostics.defaultAlternativeLocationMessage)); }); it.each([ diff --git a/src/test/unit/lib/settings.test.ts b/src/test/unit/lib/settings.test.ts index b944dbaa..e1048f6f 100644 --- a/src/test/unit/lib/settings.test.ts +++ b/src/test/unit/lib/settings.test.ts @@ -37,13 +37,6 @@ describe('Tests for the SettingsManagerImpl class ', () => { expect(settingsManager.getAnalyzeOnSave()).toBe(false); expect(getMock).toHaveBeenCalledWith('enabled'); }); - - it('should get apexGuruEnabled', () => { - getMock.mockReturnValue(true); - expect(settingsManager.getApexGuruEnabled()).toBe(true); - expect(getMock).toHaveBeenCalledWith('enabled'); - }); - }); describe('Configuration Settings', () => { diff --git a/src/test/unit/stubs.ts b/src/test/unit/stubs.ts index 8ec9ddf9..b81450ec 100644 --- a/src/test/unit/stubs.ts +++ b/src/test/unit/stubs.ts @@ -15,7 +15,7 @@ import * as semver from "semver"; import {FileHandler} from "../../lib/fs-utils"; import {VscodeWorkspace, WindowManager} from "../../lib/vscode-api"; import {Workspace} from "../../lib/workspace"; -import { ApexGuruService } from "../../lib/apexguru/apex-guru-service"; +import { ApexGuruAccess, ApexGuruAvailability, ApexGuruService } from "../../lib/apexguru/apex-guru-service"; export class SpyTelemetryService implements TelemetryService { @@ -249,12 +249,6 @@ export class StubSettingsManager implements SettingsManager { return this.getAnalyzeOnSaveReturnValue; } - getApexGuruEnabledReturnValue: boolean = false; - - getApexGuruEnabled(): boolean { - return this.getApexGuruEnabledReturnValue; - } - // ================================================================================================================= // ==== Configuration Settings // ================================================================================================================= @@ -364,9 +358,20 @@ export class SpyWindowManager implements WindowManager { } export class StubApexGuruService implements ApexGuruService { - isApexGuruAvailableReturnValue: boolean = true; - isApexGuruAvailable(): Promise { - return Promise.resolve(this.isApexGuruAvailableReturnValue); + getAvailabilityReturnValue: ApexGuruAvailability = { + access: ApexGuruAccess.ENABLED, + message: "ApexGuru access is enabled." + }; + getAvailability(): ApexGuruAvailability { + return this.getAvailabilityReturnValue; + } + + updateAvailability(): Promise { + throw new Error("This Stubbed method has not been implemented since it isn't used in testing yet."); + } + + onAccessChange(_callback: (access: ApexGuruAccess) => void): void { + throw new Error("This Stubbed method has not been implemented since it isn't used in testing yet."); } scanReturnValue: Violation[] = []; @@ -375,12 +380,8 @@ export class StubApexGuruService implements ApexGuruService { } } -export class ThrowingApexGuruService implements ApexGuruService { - isApexGuruAvailable(): Promise { - throw new Error("Sample error message from isApexGuruAvailable method"); - } +export class ThrowingScanApexGuruService extends StubApexGuruService { scan(_absFileToScan: string): Promise { throw new Error("Sample error message from scan method"); } - } \ No newline at end of file diff --git a/src/test/unit/test-utils.ts b/src/test/unit/test-utils.ts index 3a365b96..b1ed6be6 100644 --- a/src/test/unit/test-utils.ts +++ b/src/test/unit/test-utils.ts @@ -1,5 +1,6 @@ import * as vscode from "vscode"; import {CodeAnalyzerDiagnostic, CodeLocation, Fix, Suggestion} from "../../lib/diagnostics"; +import { getErrorMessageWithStack } from "../../lib/utils"; export function createSampleCodeAnalyzerDiagnostic(uri: vscode.Uri, range: vscode.Range, ruleName: string = 'someRule', engineName: string = 'pmd'): CodeAnalyzerDiagnostic { return CodeAnalyzerDiagnostic.fromViolation(createSampleViolation({ @@ -28,4 +29,31 @@ export function createSampleViolation(location: CodeLocation, ruleName: string = fixes: fixes, suggestions: suggestions }; +} + +// To help test asyncronous code (when we purposely do not await a promise), we can use this function to help wait for +// async operation to take place successfully. If it doesn't within the specified timeout, then an exception is thrown. +export async function expectEventuallyIsTrue(conditionFn: () => boolean, timeout: number = 5000, interval: number = 50): Promise { + const start = Date.now(); + + return new Promise((resolve, reject) => { + let lastErrMsg = ''; + + const check = () => { + try { + if (conditionFn()) { + resolve(); + } else if (Date.now() - start >= timeout) { + reject(new Error(`The condition was not satisfied within the allocated ${timeout} milliseconds. ${lastErrMsg}`)); + } else { + setTimeout(check, interval); + } + } catch (err) { + lastErrMsg = getErrorMessageWithStack(err); + setTimeout(check, interval); + } + }; + + check(); + }); } \ No newline at end of file