diff --git a/package-lock.json b/package-lock.json index 47ac9020f..6f9391309 100644 --- a/package-lock.json +++ b/package-lock.json @@ -760,9 +760,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-cpp": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-7.0.2.tgz", - "integrity": "sha512-dfbeERiVNeqmo/npivdR6rDiBCqZi3QtjH2Z0HFcXwpdj6i97dX1xaKyK2GUsO/p4u1TOv63Dmj5Vm48haDpuA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-7.1.0.tgz", + "integrity": "sha512-rcjycobioQUd9Jm/Y1n8U+sq6ytpZ0iV8AYIo+vyEFfutC5x7g6hL6Fh3bCdh6IporGHRqfEutGK/4sfopD2ZA==", "dev": true, "license": "MIT" }, @@ -837,9 +837,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-en-common-misspellings": { - "version": "2.1.13", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.1.13.tgz", - "integrity": "sha512-00rpydUxKNWY2xxrSx+h46aNWLvbkJdd57SsnEFt24fbs1fROhXZ6XSQu+gQz/zNuiCvFi4Ro3ej9DLbEdWQmQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.2.0.tgz", + "integrity": "sha512-5PmCHv+AhY0LVNo3bE1FdRKMmw1esKj83GPwLymnVPYNtlI2Jf6y27EjC0azg4zny5keWmku0GQiMf55Fi+qeA==", "dev": true, "license": "CC BY-SA 4.0" }, @@ -900,9 +900,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-golang": { - "version": "6.0.26", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.26.tgz", - "integrity": "sha512-YKA7Xm5KeOd14v5SQ4ll6afe9VSy3a2DWM7L9uBq4u3lXToRBQ1W5PRa+/Q9udd+DTURyVVnQ+7b9cnOlNxaRg==", + "version": "6.0.27", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.27.tgz", + "integrity": "sha512-rRDb2JL8EefaezHGTEclKXCs4eMOS0q/datk94Lm7KQOhlGlzS9FDVZiR1/guh0o+iJCmejQuf5NR2FQeQSWsg==", "dev": true, "license": "MIT" }, @@ -921,9 +921,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-html": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.15.tgz", - "integrity": "sha512-GJYnYKoD9fmo2OI0aySEGZOjThnx3upSUvV7mmqUu8oG+mGgzqm82P/f7OqsuvTaInZZwZbo+PwJQd/yHcyFIw==", + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.16.tgz", + "integrity": "sha512-9B6/Cpb5YVcYgsEAlKudun1yd4ONllYS/ZibKLYea2apPR+l2vQdARnPrP9kTqa7NUNfRKl7m9Fb6k9rjimnow==", "dev": true, "license": "MIT" }, @@ -991,22 +991,22 @@ "license": "MIT" }, "node_modules/@cspell/dict-markdown": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.17.tgz", - "integrity": "sha512-H8bAxih6U8NOnSPL7R8My+tqjaB4tmnJTjERuz4zYqmf+cH+5xshX3UVgKlwWFcyjsYfv/zEDuRdMctQv1q6HQ==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.18.tgz", + "integrity": "sha512-KLRSwVrwKDz4n7bl2XQjzvaBZbGgx5QKkP5Ok1b24xaO3TpXsLhAbAn1mItvFlI2tF6Rkt83iYjQcYppywuf5Q==", "dev": true, "license": "MIT", "peerDependencies": { "@cspell/dict-css": "^4.1.2", - "@cspell/dict-html": "^4.0.15", + "@cspell/dict-html": "^4.0.16", "@cspell/dict-html-symbol-entities": "^4.0.5", "@cspell/dict-typescript": "^3.2.3" } }, "node_modules/@cspell/dict-monkeyc": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.12.tgz", - "integrity": "sha512-MN7Vs11TdP5mbdNFQP5x2Ac8zOBm97ARg6zM5Sb53YQt/eMvXOMvrep7+/+8NJXs0jkp70bBzjqU4APcqBFNAw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.1.0.tgz", + "integrity": "sha512-mc/hgvSy/emOIYtc8kuVEaLUlnaERunfAubfJ5plUXq6s0A69bcukJzUzSt9C0UTCwS28641ILRPv80m3L9QbA==", "dev": true, "license": "MIT" }, @@ -1018,9 +1018,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-npm": { - "version": "5.2.43", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.43.tgz", - "integrity": "sha512-H2gYwtu59dNO9662Uq0usfuhyNd7lZJE1C61a/UXcpRyWWSrTo2Bz+vwGYp1bXZ1LmjXadqvwJ8ArFlGdiadNQ==", + "version": "5.2.46", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.46.tgz", + "integrity": "sha512-Z5GG59c17UEk6BPZ3lVD/++GvPBI+O05OTGm4sRA/0I/qszbstuYRw+j2//5f8SWzOFXKaGYI8kygwKX9EQZ9Q==", "dev": true, "license": "MIT" }, @@ -1046,9 +1046,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-python": { - "version": "4.2.29", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.29.tgz", - "integrity": "sha512-OnEt1a35iuQzc2Ize1qU/43ZyF10urRKAm+mlTz++vnAgDLBHpKfWakpSK50nyL5/1WvyQ8BaMjb52MBLEpTeA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.4.0.tgz", + "integrity": "sha512-49Eju/a97V6ExAeMJDM2Tk4jsYr0pm+kQzqQrKLAZdrRd6VrLIgseI481EQSmgoVkPPdsuwK1xj9ZlpDQIF4qg==", "dev": true, "license": "MIT", "dependencies": { @@ -1063,9 +1063,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-ruby": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.1.1.tgz", - "integrity": "sha512-LHrp84oEV6q1ZxPPyj4z+FdKyq1XAKYPtmGptrd+uwHbrF/Ns5+fy6gtSi7pS+uc0zk3JdO9w/tPK+8N1/7WUA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.1.2.tgz", + "integrity": "sha512-f6Slf11N91ittf71AWlfVVG9GZPezVRBfcMPCTNTWhUsY/VEspkBRZYDhPXjV4df3jqHy5YJs8nlsFcFVF/bMA==", "dev": true, "license": "MIT" }, @@ -1091,9 +1091,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-software-terms": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.2.4.tgz", - "integrity": "sha512-z6y/TGH3QNf5wB4pVvN/P3GfFEW/Whf6QAekNsIn06VKl95dnamfpkPWqV8rEtCixQFaKalb5+y9hRQXH3XQ1g==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.4.1.tgz", + "integrity": "sha512-tY88gnOWj2ax6h+i7BjU7dIH1f3fvY4WypS0a+TpjBgXC318AdOMmy0zK1SzxGNOjhWJfCeCZNVoLFM/DuOG4g==", "dev": true, "license": "MIT" }, @@ -1248,9 +1248,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz", - "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.8.tgz", + "integrity": "sha512-CpMLjAvwQg3BL5S0IeqsZNMH7EQrEWi0kLKOC13ZBF0ZwERiLWlibNPJr8G1kdU3Ms/r2KiNrF81pUh2HwAHdg==", "dev": true, "funding": [ { @@ -1369,38 +1369,35 @@ "license": "MIT" }, "node_modules/@emnapi/core": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", - "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", + "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.3", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", - "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", - "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -2036,9 +2033,9 @@ "license": "MIT" }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", - "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", + "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", "dev": true, "license": "MIT", "optional": true, @@ -2053,8 +2050,8 @@ "url": "https://github.com/sponsors/Brooooooklyn" }, "peerDependencies": { - "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", - "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" } }, "node_modules/@nodelib/fs.scandir": { @@ -2443,9 +2440,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz", - "integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==", + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.143.0.tgz", + "integrity": "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==", "dev": true, "license": "MIT", "funding": { @@ -2558,6 +2555,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2572,6 +2572,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2586,6 +2589,9 @@ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2600,6 +2606,9 @@ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2614,6 +2623,9 @@ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2628,6 +2640,9 @@ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2642,6 +2657,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2656,6 +2674,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2695,40 +2716,6 @@ "node": ">=14.0.0" } }, - "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", - "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", - "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { "version": "11.24.2", "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.24.2.tgz", @@ -2883,10 +2870,27 @@ "react-dom": ">=16.9.0" } }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.5.tgz", + "integrity": "sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.2.tgz", - "integrity": "sha512-l7x215OGvo1s52JWmR8U/DAVzEDWBCIbTm28aeJV/WDTSHgcKXaZTuBT0hJMs5NggilfJTW3clZVvd24yfKJxA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.5.tgz", + "integrity": "sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig==", "cpu": [ "arm64" ], @@ -2901,9 +2905,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.2.tgz", - "integrity": "sha512-9u9Xv6c1AJZT0FfwH5vrMG5Jjcwhc1MlyrPu0XfTqkzsmqfks2M6W/o5XwAJgVVN/jHpqqngC1WevHKKTIUtIA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.5.tgz", + "integrity": "sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww==", "cpu": [ "arm64" ], @@ -2918,9 +2922,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.2.tgz", - "integrity": "sha512-9W1mbGZAfW3oqd85bhBkmpyHCCzL1TeG/zFFP3vg7b0rlly8cxOcre5nXwz+LHazCwac2MNWgPdPCHndABjpWQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.5.tgz", + "integrity": "sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A==", "cpu": [ "x64" ], @@ -2935,9 +2939,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.2.tgz", - "integrity": "sha512-0p1lhiCSCyaerFwtrdZQUx7NqGk6LQnaRKWX7tFQqwQgvX0rjM15cIkm3pax1UpEakK14C4mOxx/jSqCBdBRqQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.5.tgz", + "integrity": "sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw==", "cpu": [ "x64" ], @@ -2952,9 +2956,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.2.tgz", - "integrity": "sha512-e+cOJXrJ2L3zx6YzqPg+f6Wbk3V1cKB8bOhbaYdVYN3DdquzNdRAmrbETz1qnt5yp/c7JNlNjmITiA2cVneQ7w==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.5.tgz", + "integrity": "sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg==", "cpu": [ "arm" ], @@ -2969,13 +2973,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.2.tgz", - "integrity": "sha512-JsSMsj6sNat/MuhG5fnBD7QgbtpHKVe30x5/bAVirDHdhoQRXJkF6xc0Jqk8O4fiCUQAzMOoH9wZi3m60c8wtg==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.5.tgz", + "integrity": "sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2986,13 +2993,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.2.tgz", - "integrity": "sha512-B5G/zJdHaoJn9vD50eGHWkiWfmq8Uhi3IiLPJTzmZTrAalk1bztUikSXo0qga18ibE0IXboyeMUnhPjhAJ45wQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.5.tgz", + "integrity": "sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3003,13 +3013,16 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.2.tgz", - "integrity": "sha512-6mC/awzKka8W6EoekjegpfGkjz8jXWDX63pqu/HYVpyKtZfu65Jsh4QAH3Kej3CAv/c1oGX7psTmFEbr0mDxLA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.5.tgz", + "integrity": "sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3020,13 +3033,16 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.2.tgz", - "integrity": "sha512-412MX9fJLdA1IK28EZnc8jYv2HRTleOZgfLQumJ5zy7OeJLZlg/CETwFaXjNmGVxG51cFHpKLqb5LKvBC+HsHA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.5.tgz", + "integrity": "sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3037,13 +3053,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.2.tgz", - "integrity": "sha512-Q/+HI/ToJafZ1iCqGgVQXUEkIjufHCTF0gBQ2a5o3cg7GJ2h0qyq3nvvSmU+bGda2/7ygXpTY4TM6gO9OhQ0ZA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.5.tgz", + "integrity": "sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3054,13 +3073,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.2.tgz", - "integrity": "sha512-ZKp/w41n6wCvxzxQHtQSbuphfX3Y4cCvbjkKHusrLx4lh+JWLTU7StSltO/DKARISzbj368d+qUaCjI8K2wzXw==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.5.tgz", + "integrity": "sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3071,9 +3093,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.2.tgz", - "integrity": "sha512-pxE6xD4KS3eAROkKK5yrhB9/3+vhlhVGMvlQLbdpzrBGDbKrnzx3RLwPaHvasLo6jgaiBLn7e04Df9C4tYhjmA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.5.tgz", + "integrity": "sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw==", "cpu": [ "arm64" ], @@ -3088,9 +3110,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.2.tgz", - "integrity": "sha512-4MqEue5re+xIZzAWsB8sj0P1kqZySWqIuN4t6QaIO/YA6SFwySOLruvWQFzfmqk8LBK2P30KCSJwf6mJCZZ5/A==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.5.tgz", + "integrity": "sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw==", "cpu": [ "arm64" ], @@ -3105,9 +3127,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.2.tgz", - "integrity": "sha512-NweNxxD0Nf9t8v7kodun45Ijp3EIwYY+uydPP6qBEYvfBqhIjN6dZMzlQja3tqX/aLs3F3Uz+AxDpKgRhpOZQg==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.5.tgz", + "integrity": "sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw==", "cpu": [ "x64" ], @@ -3268,13 +3290,33 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/@tanstack/react-table": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-9.1.2.tgz", + "integrity": "sha512-YQPZFJ1nIi/bjjwsPZVouABgahDcl7Gdm33CdTStUJBn0DjEVJ2uhSTVmIoWt9MVKdQziXGAsXipSzy949Hygg==", + "license": "MIT", + "dependencies": { + "@tanstack/react-store": "^0.11.0", + "@tanstack/table-core": "9.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=18" + } + }, "node_modules/@tanstack/react-virtual": { - "version": "3.14.9", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.14.9.tgz", - "integrity": "sha512-qZyr0FZDP8rDC4WBhsryIZmAd9bveJvFGUJJtskWaew6/0dTRS6wZxnR6VQ5bY2KwL3LjerrHqQLk3a0GKcPXQ==", + "version": "3.14.10", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.14.10.tgz", + "integrity": "sha512-SRyoUbdFMRHuYXMijV5H4ZarQWpXkj3iANq8OFre+pybeVap8ZJjZ3Nz9bVjx4d8PfobVUQUdKyyyHYk3E+djw==", "license": "MIT", "dependencies": { - "@tanstack/virtual-core": "3.17.7" + "@tanstack/virtual-core": "3.17.8" }, "funding": { "type": "github", @@ -3295,10 +3337,26 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@tanstack/table-core": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-9.1.2.tgz", + "integrity": "sha512-ONpWQeass1sfg80CWF1NSwQ8r3GiqxA2lT/EdqIcrDEPZ0Z+0mM94eQoFYLPN0Kztzj8TQVb2+PrSZSItqA61g==", + "license": "MIT", + "dependencies": { + "@tanstack/store": "^0.11.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tanstack/virtual-core": { - "version": "3.17.7", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.17.7.tgz", - "integrity": "sha512-bp+v10y65sp2H7WpWfIMyxTNfl8ZVfxFTLRjPIFRryi6FV/J33z4IS53WO4pTk36KlvJ4iLiQz+oaydDC1xbcA==", + "version": "3.17.8", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.17.8.tgz", + "integrity": "sha512-BfEvehNpOT75r5Ksc5xW6NZuXujTfb7nlSEyVu4XHG3gdxNg1KqXruWbDewXOUaUYIo4oRbSfkjIajz4MAT8tA==", "license": "MIT", "funding": { "type": "github", @@ -3503,17 +3561,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.66.0.tgz", - "integrity": "sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", + "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.66.0", - "@typescript-eslint/type-utils": "8.66.0", - "@typescript-eslint/utils": "8.66.0", - "@typescript-eslint/visitor-keys": "8.66.0", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/type-utils": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -3526,7 +3584,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.66.0", + "@typescript-eslint/parser": "^8.67.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -3542,16 +3600,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.66.0.tgz", - "integrity": "sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz", + "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.66.0", - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/typescript-estree": "8.66.0", - "@typescript-eslint/visitor-keys": "8.66.0", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "debug": "^4.4.3" }, "engines": { @@ -3567,14 +3625,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.66.0.tgz", - "integrity": "sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", + "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.66.0", - "@typescript-eslint/types": "^8.66.0", + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", "debug": "^4.4.3" }, "engines": { @@ -3589,14 +3647,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.66.0.tgz", - "integrity": "sha512-8TGcH25j9zqJ/IULB/ppyhRvxA8QYfFEZ7nfbg6/BN9spDgb8fPWQXlE5l8TWBL50EtUx007uZ1o9VOwrq2/9g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", + "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/visitor-keys": "8.66.0" + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3607,9 +3665,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.66.0.tgz", - "integrity": "sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", + "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", "dev": true, "license": "MIT", "engines": { @@ -3624,15 +3682,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.66.0.tgz", - "integrity": "sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", + "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/typescript-estree": "8.66.0", - "@typescript-eslint/utils": "8.66.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -3649,9 +3707,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.66.0.tgz", - "integrity": "sha512-H6gcYaSDOyvL3AD/jHUtUFo2jqGgn/F6nuyuZSu0QTesxL+cP4dQoIMrODRofuJC09g64+WgZ6tE19Y1N2YIFQ==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", + "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", "dev": true, "license": "MIT", "engines": { @@ -3663,16 +3721,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.66.0.tgz", - "integrity": "sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", + "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.66.0", - "@typescript-eslint/tsconfig-utils": "8.66.0", - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/visitor-keys": "8.66.0", + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -3730,16 +3788,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.66.0.tgz", - "integrity": "sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", + "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.66.0", - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/typescript-estree": "8.66.0" + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3754,13 +3812,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.66.0.tgz", - "integrity": "sha512-dkKR8q+lKciskj1Y3vthHktl+3cMLWGyVUP23bRiPZ5O9BRT++4EqDDV+TVeIKBL1VXVEqrJlz8MYbcnvJcAlg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", + "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/types": "8.67.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -3785,9 +3843,9 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.5.tgz", - "integrity": "sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.0.tgz", + "integrity": "sha512-qd2BzUBehkov86WFhg0JkEFEYyCLG9uPCe6qWTY/kRlss9OvJrOF2UbIWT7p+8IzZHkEu0DNGHc4HSv+JdDLsw==", "dev": true, "license": "MIT", "dependencies": { @@ -3799,6 +3857,7 @@ "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": "^0.145.0", "vite": "^8.0.0" }, "peerDependenciesMeta": { @@ -3807,6 +3866,9 @@ }, "babel-plugin-react-compiler": { "optional": true + }, + "oxc-transform-react": { + "optional": true } } }, @@ -4092,9 +4154,9 @@ "license": "MIT" }, "node_modules/@zip.js/zip.js": { - "version": "2.8.53", - "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.8.53.tgz", - "integrity": "sha512-Vr55wt21XKD0kZFCQEZOtbViUMUK/U0MH2MDsHxu6FSyYk3Wu+afh+f9xD01x0kerDNfHmUVrNpcgYAgtnRtjA==", + "version": "2.8.54", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.8.54.tgz", + "integrity": "sha512-Nr2JRdMBxmdh6v8JYb/BBipvTvBr3Ztwf6+bIdVxb1EMaNNm5gsM9R80pB+c9S2WMdODb3LIWiXfYekDA3+JSw==", "license": "BSD-3-Clause", "engines": { "bun": ">=0.7.0", @@ -4198,9 +4260,9 @@ } }, "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", "dev": true, "license": "MIT", "engines": { @@ -4494,12 +4556,12 @@ "license": "MIT" }, "node_modules/attr-accept": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", - "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-4.0.0.tgz", + "integrity": "sha512-hmCnJClmeKNKlsBHgbM8yLZRiQZ4/20UXbLJb6OUT16eWcM5/xNZerr80a/zCYob768KIGq++aLrQNTuwPsIOQ==", "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 22" } }, "node_modules/available-typed-arrays": { @@ -4541,9 +4603,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.11.12", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.12.tgz", - "integrity": "sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==", + "version": "2.11.16", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.16.tgz", + "integrity": "sha512-H/bNPUFHewJHyCTdjn1n3Pit5+2GmWT6mmeHImPX+8MA9NA6b67jO4gYmi4jTbCJb2otq34KMZnovndDPqJwhQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4625,9 +4687,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", - "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "version": "4.28.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", "dev": true, "funding": [ { @@ -4645,11 +4707,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.44", - "caniuse-lite": "^1.0.30001806", - "electron-to-chromium": "^1.5.393", - "node-releases": "^2.0.51", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" }, "bin": { "browserslist": "cli.js" @@ -4788,9 +4850,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001806", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", - "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "version": "1.0.30001809", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz", + "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==", "dev": true, "funding": [ { @@ -4991,9 +5053,9 @@ "license": "MIT" }, "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.10.0.tgz", + "integrity": "sha512-AidJptpBJmjTclAp9BkLwJi0T93fo5epJnbaZslpg6QVzpHjAiveF55mE9AcUJiGMqRHgMDY8soMsQtuNYMHfw==", "dev": true, "license": "MIT" }, @@ -5141,13 +5203,16 @@ } }, "node_modules/core-js-compat": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", - "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.50.0.tgz", + "integrity": "sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.28.1" + "browserslist": "^4.28.7" + }, + "engines": { + "node": ">=6.4.0" }, "funding": { "type": "opencollective", @@ -5883,9 +5948,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.400", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.400.tgz", - "integrity": "sha512-96EWDNjM59SYflgeV5Ylsf4EMiq1a25YjCnJH7cxn/AF2H3pILRweaUnoLax0yKHWdpOzY6JKEu45e8irqZIHA==", + "version": "1.5.412", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.412.tgz", + "integrity": "sha512-z4rMe3esBzlzovKHj4gxJnsCGZRK5l4baUvm+gCGJBPE+gsyUMKsuU9tnEUtI1dOebXz1ytAPGjvXhmQ7rIPwA==", "dev": true, "license": "ISC" }, @@ -6066,9 +6131,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", - "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "dev": true, "license": "MIT" }, @@ -6161,6 +6226,7 @@ "version": "9.39.5", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { @@ -6253,9 +6319,9 @@ } }, "node_modules/eslint-config-cheminfo-react/node_modules/globals": { - "version": "17.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.9.0.tgz", - "integrity": "sha512-m/MvAW61QVU5VDNF1Vj8axt016h8w7L5TU1e9zlab7XIttAT2YAlCwl75K1fOqvMM9apmD7lbCIRhpfkhmxhCg==", + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.11.0.tgz", + "integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==", "dev": true, "license": "MIT", "engines": { @@ -6281,9 +6347,9 @@ } }, "node_modules/eslint-config-cheminfo/node_modules/globals": { - "version": "17.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.9.0.tgz", - "integrity": "sha512-m/MvAW61QVU5VDNF1Vj8axt016h8w7L5TU1e9zlab7XIttAT2YAlCwl75K1fOqvMM9apmD7lbCIRhpfkhmxhCg==", + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.11.0.tgz", + "integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==", "dev": true, "license": "MIT", "engines": { @@ -6535,9 +6601,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.3.tgz", - "integrity": "sha512-5EMmLCV98Pi4o/f/3DP/v/tNqLHMIc9I8LKClNDWhZ9JTho89/kQcitCXQBMG7sAfVRK0Ie3T2EDOzp1YXYiVA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.4.tgz", + "integrity": "sha512-7bqTKz7T0r+HKWFarNXByDE9/5+73wI2ru+M3zuqGbR7s/b/5/pQJXZoufWlrngqGqoZto73ZkGumCdLxk+4rw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -6545,9 +6611,9 @@ } }, "node_modules/eslint-plugin-react-you-might-not-need-an-effect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-you-might-not-need-an-effect/-/eslint-plugin-react-you-might-not-need-an-effect-1.0.1.tgz", - "integrity": "sha512-oOhQTYhor88Xp8RVytq25tvBfiAjU0r9SCDC51Qop+3Wg5BR1xGMAkM+/dV4MZbcMhdaU1L9bkv6LC95JmiTig==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-you-might-not-need-an-effect/-/eslint-plugin-react-you-might-not-need-an-effect-1.0.2.tgz", + "integrity": "sha512-HgYol2zhH3KbnW9Q4FY/FcIINbsYVL6rwQESChqBC2s9SLWtw1wg05+J7SCrr3BfQpNY2ReYhf8xjpd2JhKJOQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6651,9 +6717,9 @@ "license": "MIT" }, "node_modules/eslint-plugin-unicorn/node_modules/globals": { - "version": "17.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.9.0.tgz", - "integrity": "sha512-m/MvAW61QVU5VDNF1Vj8axt016h8w7L5TU1e9zlab7XIttAT2YAlCwl75K1fOqvMM9apmD7lbCIRhpfkhmxhCg==", + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.11.0.tgz", + "integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==", "dev": true, "license": "MIT", "engines": { @@ -7033,12 +7099,12 @@ "license": "MIT" }, "node_modules/file-selector": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-4.1.0.tgz", - "integrity": "sha512-Io1mP8CI3zec5Bxy3P3TxdrKnt35Cm8vNIHnZsvyj43l4YFjD4NRInBp240S5bDJQ0EP1jnh7nCAwXsO818OCg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-5.0.1.tgz", + "integrity": "sha512-v0g/PTeuQgvKCBrVRsfVudvwXlRHSWHEQkVgKawgCGHkEpKA1clp3Om5jvEVhz8G9W/mOYjJH9FhkH4C888PgQ==", "license": "MIT", "engines": { - "node": ">= 20" + "node": ">= 22" } }, "node_modules/fill-range": { @@ -7513,9 +7579,9 @@ } }, "node_modules/globby": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.2.tgz", - "integrity": "sha512-NLvV9ubZ6NDsJaOpKPy3cQeJpKi9DcWiyCiFUpJPA0YihRqiE6RWaLUmgNNPr8MgPpLZjnBjSmou7uZBRJv9wA==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.4.tgz", + "integrity": "sha512-c8B/VNLmxRcmqqenRA9t+9IyOjf9+V6lTxPaUJLqOCONdQkWZ0ETYgX0qbtJqPsgCNusT9MZ5Jeidw8Eb9tn2g==", "dev": true, "license": "MIT", "dependencies": { @@ -7523,6 +7589,7 @@ "fast-glob": "^3.3.3", "ignore": "^7.0.5", "is-path-inside": "^4.0.0", + "micromatch": "^4.0.8", "slash": "^5.1.0", "unicorn-magic": "^0.4.0" }, @@ -8841,6 +8908,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -8862,6 +8932,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -8883,6 +8956,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -8904,6 +8980,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -9661,9 +9740,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.17", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", - "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, "funding": [ { @@ -9781,9 +9860,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.52", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.52.tgz", - "integrity": "sha512-MRlTqhAfoMx/4mhEbPo3Hi02g9LJZaJkka69V6h67Cb1gjrAG0jsTE4CZX1eptNx+VCAwJmfpnDIF4P0Nh1A7A==", + "version": "2.0.53", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", + "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", "dev": true, "license": "MIT", "engines": { @@ -10107,16 +10186,6 @@ "@oxc-parser/binding-win32-x64-msvc": "0.143.0" } }, - "node_modules/oxc-parser/node_modules/@oxc-project/types": { - "version": "0.143.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.143.0.tgz", - "integrity": "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, "node_modules/oxc-resolver": { "version": "11.24.2", "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.24.2.tgz", @@ -10481,9 +10550,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", - "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.5.tgz", + "integrity": "sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==", "dev": true, "license": "MIT", "dependencies": { @@ -10814,13 +10883,13 @@ } }, "node_modules/react-dropzone": { - "version": "20.1.0", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-20.1.0.tgz", - "integrity": "sha512-id1t9JDYNQeFzzIfB5/C6TrpLchy29rTSDxsBH/pcxhILyv/6bSOTJwOlvUsTWXkC2GduuHIldDV6UQXNWfIuA==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-20.1.1.tgz", + "integrity": "sha512-2cilRFP8bsjDOHpV0sJ6XY8pzJhmz4/cQ6s9yeckOACWYDR+n4MGGtnJh3Rycq/9SLhDWugqDx1z5mtfmOOZhw==", "license": "MIT", "dependencies": { - "attr-accept": "^2.2.5", - "file-selector": "^4.1.0" + "attr-accept": "^4.0.0", + "file-selector": "^5.0.0" }, "engines": { "node": ">= 22" @@ -11081,42 +11150,6 @@ "react-dom": "^18.3.1 || ^19.2.7" } }, - "node_modules/react-science/node_modules/@tanstack/react-table": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-9.1.2.tgz", - "integrity": "sha512-YQPZFJ1nIi/bjjwsPZVouABgahDcl7Gdm33CdTStUJBn0DjEVJ2uhSTVmIoWt9MVKdQziXGAsXipSzy949Hygg==", - "license": "MIT", - "dependencies": { - "@tanstack/react-store": "^0.11.0", - "@tanstack/table-core": "9.1.2" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": ">=18" - } - }, - "node_modules/react-science/node_modules/@tanstack/table-core": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-9.1.2.tgz", - "integrity": "sha512-ONpWQeass1sfg80CWF1NSwQ8r3GiqxA2lT/EdqIcrDEPZ0Z+0mM94eQoFYLPN0Kztzj8TQVb2+PrSZSItqA61g==", - "license": "MIT", - "dependencies": { - "@tanstack/store": "^0.11.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, "node_modules/react-table": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", @@ -11330,13 +11363,13 @@ } }, "node_modules/rolldown": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.2.tgz", - "integrity": "sha512-opwpo1tQBAcpSUJDt94B7hhLNGOKjCdE//XXjeLrnx9b83bjnw45tXdg1b09yEw/VLFBJGZpwRULMmOZo7ol+A==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.5.tgz", + "integrity": "sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.142.0", + "@oxc-project/types": "=0.146.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -11346,20 +11379,31 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.2.2", - "@rolldown/binding-darwin-arm64": "1.2.2", - "@rolldown/binding-darwin-x64": "1.2.2", - "@rolldown/binding-freebsd-x64": "1.2.2", - "@rolldown/binding-linux-arm-gnueabihf": "1.2.2", - "@rolldown/binding-linux-arm64-gnu": "1.2.2", - "@rolldown/binding-linux-arm64-musl": "1.2.2", - "@rolldown/binding-linux-ppc64-gnu": "1.2.2", - "@rolldown/binding-linux-s390x-gnu": "1.2.2", - "@rolldown/binding-linux-x64-gnu": "1.2.2", - "@rolldown/binding-linux-x64-musl": "1.2.2", - "@rolldown/binding-openharmony-arm64": "1.2.2", - "@rolldown/binding-win32-arm64-msvc": "1.2.2", - "@rolldown/binding-win32-x64-msvc": "1.2.2" + "@rolldown/binding-android-arm-eabi": "1.2.5", + "@rolldown/binding-android-arm64": "1.2.5", + "@rolldown/binding-darwin-arm64": "1.2.5", + "@rolldown/binding-darwin-x64": "1.2.5", + "@rolldown/binding-freebsd-x64": "1.2.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.5", + "@rolldown/binding-linux-arm64-gnu": "1.2.5", + "@rolldown/binding-linux-arm64-musl": "1.2.5", + "@rolldown/binding-linux-ppc64-gnu": "1.2.5", + "@rolldown/binding-linux-s390x-gnu": "1.2.5", + "@rolldown/binding-linux-x64-gnu": "1.2.5", + "@rolldown/binding-linux-x64-musl": "1.2.5", + "@rolldown/binding-openharmony-arm64": "1.2.5", + "@rolldown/binding-win32-arm64-msvc": "1.2.5", + "@rolldown/binding-win32-x64-msvc": "1.2.5" + } + }, + "node_modules/rolldown/node_modules/@oxc-project/types": { + "version": "0.146.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.146.0.tgz", + "integrity": "sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" } }, "node_modules/run-parallel": { @@ -11830,9 +11874,9 @@ } }, "node_modules/smol-toml": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.1.tgz", - "integrity": "sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.8.0.tgz", + "integrity": "sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -12717,16 +12761,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.66.0.tgz", - "integrity": "sha512-QlEbBPz/RuJ1XUHj29nm3t0F/O/cSlEnntozqPOYHnnTGAXFamnMBu5i9Vn6vhUPHGAjR+Vl+5J8vPN/BMUrJw==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.67.0.tgz", + "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.66.0", - "@typescript-eslint/parser": "8.66.0", - "@typescript-eslint/typescript-estree": "8.66.0", - "@typescript-eslint/utils": "8.66.0" + "@typescript-eslint/eslint-plugin": "8.67.0", + "@typescript-eslint/parser": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -12796,9 +12840,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", + "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", "dev": true, "funding": [ { @@ -12907,16 +12951,16 @@ } }, "node_modules/vite": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", - "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", + "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", - "postcss": "^8.5.25", - "rolldown": "~1.2.1", + "postcss": "^8.5.26", + "rolldown": "~1.2.4", "tinyglobby": "^0.2.17" }, "bin": { @@ -12933,7 +12977,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.4.0", + "@vitejs/devtools": "^0.4.0 || ^0.5.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", diff --git a/src/data/data2d/Spectrum2D/contours.ts b/src/data/data2d/Spectrum2D/contours.ts index eb5665a55..6fee79c9d 100644 --- a/src/data/data2d/Spectrum2D/contours.ts +++ b/src/data/data2d/Spectrum2D/contours.ts @@ -2,10 +2,12 @@ import type { Spectrum2D, Spectrum } from '@zakodium/nmrium-core'; import { isSpectrum2DFt } from '@zakodium/nmrium-core'; import type { NmrData2DFt } from 'cheminfo-types'; import { Conrec } from 'ml-conrec'; -import { xMaxAbsoluteValue } from 'ml-spectra-processing'; +import { matrixToArray } from 'ml-spectra-processing'; import type { SpectrumFTData } from '../../../component/hooks/use2DReducer.tsx'; -import { calculateSanPlot } from '../../utilities/calculateSanPlot.js'; + +import { estimateNoiseLevel } from './findBestMinContour/estimateNoiseLevel.js'; +import { findAutomaticContourLevels } from './findBestMinContour/findAutomaticContourLevels.ts'; interface Level { positive: ContourItem; @@ -26,6 +28,7 @@ interface BaseWheelOptions { altKey: boolean; invertScroll?: boolean; } + interface WheelOptions extends BaseWheelOptions { contourOptions: ContourOptions; } @@ -40,6 +43,7 @@ const DEFAULT_CONTOURS_OPTIONS: ContourOptions = { numberOfLayers: 10, }, }; + type LevelSign = keyof Level; const LEVEL_SIGNS: Readonly<[LevelSign, LevelSign]> = ['positive', 'negative']; @@ -56,21 +60,43 @@ function getDefaultContoursLevel(spectrum: Spectrum2D, quadrant = 'rr') { // @ts-expect-error type of NmrData2D should have a discriminator field to separate fid and ft const quadrantData = data[quadrant]; - //@ts-expect-error will be included in nexts versions - const { noise = calculateSanPlot('2D', quadrantData) } = info; - - const { positive = 0, negative = 0 } = noise; - const max = Math.max( - Math.abs(quadrantData.minZ), - Math.abs(quadrantData.maxZ), + const { + experiment = '', + // @ts-expect-error will be included in nexts versions + noise, + } = info; + const matrix = matrixToArray(quadrantData.z); + + const noiseLevel = (noise as { positive: number; negative: number }) + ? Math.max(noise.positive, noise.negative) + : estimateNoiseLevel(matrix); + + const bestMinLevel = findAutomaticContourLevels( + matrix, + quadrantData.z.length, + quadrantData.z[0].length, + noiseLevel, + { + maxFdr: 0.005, + diagnostics: false, + maxOccupancy: 0.005, + persistenceLevels: 5, + contourRatio: 1.8, + minPersistence: 0.5, + maxVerticalRidgeScore: 0.05, + maxHorizontalRidgeScore: 0.05, + ridgeCoverageThreshold: experiment.includes('jres') ? 0.9 : 0.1, + }, ); - const minAbsPeakBase = 0.005 * max; - const minAllowed = 3 * xMaxAbsoluteValue([positive, negative]); - - const minLevel = Math.max(minAbsPeakBase, minAllowed); + const { hasT1Noise, minLevelWithoutT1Noise, minLevel, maxAbsoluteValue } = + bestMinLevel; const minContourLevel = Math.min( - calculateValueOfLevel(minLevel, max, true), + calculateValueOfLevel( + hasT1Noise ? minLevelWithoutT1Noise : minLevel, + maxAbsoluteValue, + true, + ), DEFAULT_CONTOURS_OPTIONS.positive.contourLevels[1] - DEFAULT_CONTOURS_OPTIONS.positive.numberOfLayers, ); diff --git a/src/data/data2d/Spectrum2D/findBestMinContour/estimateNoiseLevel.ts b/src/data/data2d/Spectrum2D/findBestMinContour/estimateNoiseLevel.ts new file mode 100644 index 000000000..a18ee170a --- /dev/null +++ b/src/data/data2d/Spectrum2D/findBestMinContour/estimateNoiseLevel.ts @@ -0,0 +1,67 @@ +import { xMedian } from 'ml-spectra-processing'; + +const GAUSSIAN_MAD_SCALE = 1 / 0.6744897501960817; +const DEFAULT_MAX_SAMPLES = 65_536; + +export interface EstimateNoiseLevelOptions { + /** + * Maximum number of evenly spaced finite values used for estimation. + * @default 65536 + */ + maxSamples?: number; +} + +/** + * Estimates the standard deviation of signed Gaussian noise using a + * deterministically downsampled median absolute deviation. + */ +export function estimateNoiseLevel( + matrix: ArrayLike, + options: EstimateNoiseLevelOptions = {}, +): number { + const { maxSamples = DEFAULT_MAX_SAMPLES } = options; + + if (!Number.isInteger(maxSamples) || maxSamples <= 0) { + throw new Error('maxSamples must be a positive integer'); + } + + const sample = collectFiniteSample(matrix, maxSamples); + + if (sample.length === 0) { + throw new Error('matrix must contain at least one finite value'); + } + + const median = xMedian(sample); + + if (median === 0) { + return 1; //assume NUS reconstruction. + } + + const averageDeviations = new Float64Array(sample.length); + for (let i = 0; i < sample.length; i++) { + averageDeviations[i] = Math.abs(sample[i] - median); + } + + const noiseLevel = xMedian(averageDeviations) * GAUSSIAN_MAD_SCALE; + + if (!Number.isFinite(noiseLevel) || noiseLevel < 0) { + throw new Error('unable to estimate a positive noise level'); + } + + return noiseLevel; +} + +function collectFiniteSample( + matrix: ArrayLike, + maxSamples: number, +): Float64Array { + const step = Math.max(1, Math.ceil(matrix.length / maxSamples)); + const sample = new Float64Array(Math.ceil(matrix.length / step)); + let sampleIndex = 0; + + for (let index = 0; index < matrix.length; index += step) { + sample[sampleIndex++] = matrix[index]; + } + + return sample; +} diff --git a/src/data/data2d/Spectrum2D/findBestMinContour/evaluation.ts b/src/data/data2d/Spectrum2D/findBestMinContour/evaluation.ts new file mode 100644 index 000000000..fbd5f0eb2 --- /dev/null +++ b/src/data/data2d/Spectrum2D/findBestMinContour/evaluation.ts @@ -0,0 +1,432 @@ +import { gaussianTwoSidedTail, violation } from './math.js'; + +export interface EvaluatedThreshold { + sigmaMultiplier: number; + + activePixels: number; + occupancy: number; + fdr: number; + persistence: number; + + verticalRidgeScore: number; + horizontalRidgeScore: number; +} + +export interface AnalysisMatrix { + values: Float64Array; + rows: number; + cols: number; + maxAbsoluteValue: number; +} + +interface RidgeScores { + vertical: number; + horizontal: number; +} + +export interface ThresholdIndex { + countAtLeast(threshold: number): number; +} + +interface ThresholdMetrics { + activePixels: number; + ridge: RidgeScores; +} + +export function createThresholdIndex(matrix: Float64Array): ThresholdIndex { + const sortedValues = new Float64Array(matrix.length); + let valueCount = 0; + + for (const value of matrix) { + if (!Number.isNaN(value)) { + sortedValues[valueCount++] = value; + } + } + + const values = sortedValues.subarray(0, valueCount); + values.sort(); + + return { + countAtLeast(threshold) { + if (Number.isNaN(threshold)) return 0; + + let low = 0; + let high = values.length; + + while (low < high) { + const middle = low + Math.floor((high - low) / 2); + + if (values[middle] < threshold) { + low = middle + 1; + } else { + high = middle; + } + } + + return values.length - low; + }, + }; +} + +function evaluateThresholdMetrics( + matrix: Float64Array, + rows: number, + cols: number, + threshold: number, + ridgeCoverageThreshold: number, + includeRidgeScores: boolean, + thresholdIndex?: ThresholdIndex, +): ThresholdMetrics { + if (!includeRidgeScores && thresholdIndex) { + return { + activePixels: thresholdIndex.countAtLeast(threshold), + ridge: { vertical: 0, horizontal: 0 }, + }; + } + + const rowCounts = includeRidgeScores ? new Uint32Array(rows) : undefined; + const colCounts = includeRidgeScores ? new Uint32Array(cols) : undefined; + let activePixels = 0; + + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + const index = row * cols + col; + + if (!(matrix[index] >= threshold)) continue; + + activePixels++; + if (rowCounts && colCounts) { + rowCounts[row]++; + colCounts[col]++; + } + } + } + + const ridge = + rowCounts && colCounts + ? calculateRidgeScores( + rowCounts, + colCounts, + rows, + cols, + activePixels, + ridgeCoverageThreshold, + ) + : { vertical: 0, horizontal: 0 }; + + return { activePixels, ridge }; +} + +export function evaluateThreshold( + matrix: Float64Array, + rows: number, + cols: number, + noiseLevel: number, + sigmaMultiplier: number, + contourRatio: number, + persistenceLevels: number, + ridgeCoverageThreshold: number, + includeRidgeScores = true, + thresholdIndex?: ThresholdIndex, +): EvaluatedThreshold { + const threshold = sigmaMultiplier * noiseLevel; + const { activePixels, ridge } = evaluateThresholdMetrics( + matrix, + rows, + cols, + threshold, + ridgeCoverageThreshold, + includeRidgeScores, + thresholdIndex, + ); + const totalPixels = rows * cols; + const occupancy = activePixels / totalPixels; + + if (activePixels === 0) { + return { + sigmaMultiplier, + activePixels: 0, + occupancy: 0, + fdr: 0, + persistence: 0, + verticalRidgeScore: 0, + horizontalRidgeScore: 0, + }; + } + + const expectedNoisePixels = + totalPixels * gaussianTwoSidedTail(sigmaMultiplier); + const fdr = Math.min(1, expectedNoisePixels / activePixels); + const persistence = computePersistence( + matrix, + rows, + cols, + threshold, + contourRatio, + persistenceLevels, + thresholdIndex, + ); + + return { + sigmaMultiplier, + activePixels, + occupancy, + fdr, + persistence, + verticalRidgeScore: ridge.vertical, + horizontalRidgeScore: ridge.horizontal, + }; +} + +export function evaluateFallbackThreshold( + evaluations: EvaluatedThreshold[], + maxFdr: number, + maxOccupancy: number, + minPersistence: number, +): EvaluatedThreshold { + let best: EvaluatedThreshold | undefined; + let bestScore = Infinity; + + for (const evaluation of evaluations) { + const score = + violation(evaluation.fdr, maxFdr) * 4 + + violation(evaluation.occupancy, maxOccupancy) * 2 + + violation(minPersistence, evaluation.persistence) * 2; + + if (score < bestScore) { + bestScore = score; + best = evaluation; + } + } + + if (!best) { + throw new Error('Unable to evaluate contour thresholds'); + } + + return best; +} + +export function findRidgeFreeThresholdTopDown( + matrix: Float64Array, + rows: number, + cols: number, + noiseLevel: number, + minLevel: number, + maxLevel: number, + contourRatio: number, + maxVerticalRidgeScore: number, + maxHorizontalRidgeScore: number, + ridgeCoverageThreshold: number, + ridgePersistenceLevels = 1, +): number { + let level = maxLevel; + let lastCleanLevel = level; + let consecutiveRidgeLevels = 0; + + while (level >= minLevel) { + const ridge = evaluateRidgeScores( + matrix, + rows, + cols, + (level / noiseLevel) * noiseLevel, + ridgeCoverageThreshold, + ); + + const hasRidge = + ridge.vertical > maxVerticalRidgeScore || + ridge.horizontal > maxHorizontalRidgeScore; + + if (hasRidge) { + consecutiveRidgeLevels++; + + if (consecutiveRidgeLevels >= ridgePersistenceLevels) { + return lastCleanLevel; + } + } else { + consecutiveRidgeLevels = 0; + lastCleanLevel = level; + } + + level /= contourRatio; + } + + return lastCleanLevel; +} + +export function generateCandidateSigmaLevels( + minSigma: number, + maxSigma: number, + contourRatio: number, + exponent: number, +): number[] { + const step = contourRatio ** exponent; + const candidates: number[] = []; + + let sigma = minSigma; + + while (sigma <= maxSigma * (1 + 1e-12)) { + candidates.push(sigma); + sigma *= step; + } + + return candidates; +} + +export function computePersistence( + matrix: Float64Array, + rows: number, + cols: number, + initialThreshold: number, + contourRatio: number, + levels: number, + thresholdIndex?: ThresholdIndex, +): number { + let threshold = initialThreshold; + let previousArea = thresholdIndex + ? thresholdIndex.countAtLeast(threshold) + : countValuesAboveThreshold(matrix, threshold); + + if (previousArea === 0) { + return 0; + } + + let minimumPersistence = 1; + + for (let level = 1; level < levels; level++) { + threshold *= contourRatio; + + const currentArea = thresholdIndex + ? thresholdIndex.countAtLeast(threshold) + : countValuesAboveThreshold(matrix, threshold); + + if (currentArea === 0) { + return 0; + } + + const persistence = currentArea / previousArea; + minimumPersistence = Math.min(minimumPersistence, persistence); + previousArea = currentArea; + } + + return minimumPersistence; +} + +function calculateRidgeScores( + rowCounts: Uint32Array, + colCounts: Uint32Array, + rows: number, + cols: number, + activePixels: number, + coverageThreshold: number, +): RidgeScores { + if (activePixels === 0) { + return { + vertical: 0, + horizontal: 0, + }; + } + + let verticalRidgePixels = 0; + + for (let col = 0; col < cols; col++) { + const coverage = colCounts[col] / rows; + + if (coverage >= coverageThreshold) { + verticalRidgePixels += colCounts[col]; + } + } + + let horizontalRidgePixels = 0; + + for (let row = 0; row < rows; row++) { + const coverage = rowCounts[row] / cols; + + if (coverage >= coverageThreshold) { + horizontalRidgePixels += rowCounts[row]; + } + } + + return { + vertical: verticalRidgePixels / activePixels, + horizontal: horizontalRidgePixels / activePixels, + }; +} + +function evaluateRidgeScores( + matrix: Float64Array, + rows: number, + cols: number, + threshold: number, + coverageThreshold: number, +): RidgeScores { + return evaluateThresholdMetrics( + matrix, + rows, + cols, + threshold, + coverageThreshold, + true, + ).ridge; +} + +export function maxPoolAbsolute( + matrix: ArrayLike, + rows: number, + cols: number, + maxDimension: number, +): AnalysisMatrix { + const scale = Math.max(1, Math.ceil(Math.max(rows, cols) / maxDimension)); + const analysisRows = Math.ceil(rows / scale); + const analysisCols = Math.ceil(cols / scale); + const values = new Float64Array(analysisRows * analysisCols); + let maxAbsoluteValue = 0; + + for (let analysisRow = 0; analysisRow < analysisRows; analysisRow++) { + const sourceRowStart = analysisRow * scale; + const sourceRowEnd = Math.min(rows, sourceRowStart + scale); + + for (let analysisCol = 0; analysisCol < analysisCols; analysisCol++) { + const sourceColStart = analysisCol * scale; + const sourceColEnd = Math.min(cols, sourceColStart + scale); + + let maximum = 0; + + for (let row = sourceRowStart; row < sourceRowEnd; row++) { + const offset = row * cols; + + for (let col = sourceColStart; col < sourceColEnd; col++) { + const value = Math.abs(matrix[offset + col]); + + if (Number.isFinite(value) && value > maximum) { + maximum = value; + } + } + } + + values[analysisRow * analysisCols + analysisCol] = maximum; + maxAbsoluteValue = Math.max(maxAbsoluteValue, maximum); + } + } + + return { + values, + rows: analysisRows, + cols: analysisCols, + maxAbsoluteValue: maxAbsoluteValue * 0.95, + }; +} + +export function countValuesAboveThreshold( + matrix: Float64Array, + threshold: number, +): number { + let count = 0; + + for (const value of matrix) { + if (value >= threshold) { + count++; + } + } + + return count; +} diff --git a/src/data/data2d/Spectrum2D/findBestMinContour/findAutomaticContourLevels.ts b/src/data/data2d/Spectrum2D/findBestMinContour/findAutomaticContourLevels.ts new file mode 100644 index 000000000..d4517bad7 --- /dev/null +++ b/src/data/data2d/Spectrum2D/findBestMinContour/findAutomaticContourLevels.ts @@ -0,0 +1,431 @@ +import { + createThresholdIndex, + evaluateFallbackThreshold, + evaluateThreshold, + findRidgeFreeThresholdTopDown, + generateCandidateSigmaLevels, + maxPoolAbsolute, +} from './evaluation.js'; +import { validateInput } from './math.js'; + +interface AutoContourDiagnostics { + recommended: ThresholdDiagnostics; + ridgeFree: ThresholdDiagnostics; +} + +interface ThresholdDiagnostics { + sigmaMultiplier: number; + minLevel: number; + + occupancy: number; + fdr: number; + persistence: number; + + verticalRidgeScore: number; + horizontalRidgeScore: number; +} + +export type AutoContourResult = { + /** + * Recommended minimum contour level. + */ + minLevel: number; + + /** + * Minimum contour level at which detected directional + * ridge structures (e.g. t1-noise) are no longer present. + */ + minLevelWithoutT1Noise: number; + + /** + * True when directional ridge structures were detected + * at the recommended minimum. + */ + hasT1Noise: boolean; + + /** + * Same values expressed in multiples of the noise level. + */ + sigmaMultiplier: number; + sigmaMultiplierWithoutT1Noise: number; + /** + * Maximum absolute value in the analysis matrix used for contour level selection. + */ + maxAbsoluteValue: number; +} & (TDiagnostics extends true + ? { + diagnostics: AutoContourDiagnostics; + } + : { + diagnostics?: AutoContourDiagnostics; + }); + +export interface AutoContourOptions { + /** + * Multiplicative ratio between consecutive contour levels. + * + * The contour levels are generated as: + * + * Lₙ = L₀ × contourRatioⁿ + * + * Must be greater than 1. + * + * A smaller value produces more closely spaced contour levels, + * while a larger value produces fewer, more widely spaced levels. + * + * @default 1.4 + */ + contourRatio?: number; + + /** + * Maximum acceptable false discovery rate (FDR) for the + * automatically selected minimum contour level. + * + * The FDR estimates the fraction of pixels above the selected + * threshold that could be explained by Gaussian noise, assuming + * the supplied noiseLevel is accurate. + * + * Smaller values produce a cleaner visualization but may suppress + * weak peaks. + * + * @default 0.05 + */ + maxFdr?: number; + + /** + * Maximum fraction of the spectrum that may be above the + * automatically selected minimum contour level. + * + * This prevents very low thresholds from producing contours over + * a large fraction of the spectrum. + * + * Expressed as a fraction in the range [0, 1]. + * + * For example, 0.02 means that at most 2% of pixels may be above + * the minimum contour threshold. + * + * @default 0.02 + */ + maxOccupancy?: number; + + /** + * Minimum persistence required for spectral structures across + * successive exponential contour levels. + * + * Persistence measures how much of the active spectral structure + * remains when moving from one contour level to the next. + * + * Higher values favor stable, coherent peaks and reject fragmented + * noise structures more aggressively. + * + * Expressed as a fraction in the range [0, 1]. + * + * @default 0.35 + */ + minPersistence?: number; + + /** + * Maximum acceptable score for vertical ridge-like structures. + * + * This is primarily intended to detect structured artifacts such + * as t1 noise, which often appears as elongated structures along + * the F1 dimension. + * + * Smaller values make the ridge-free threshold more conservative. + * + * @default 0.25 + */ + maxVerticalRidgeScore?: number; + + /** + * Maximum acceptable score for horizontal ridge-like structures. + * + * This can detect horizontal streaks or other structured artifacts + * extending predominantly along the F2 dimension. + * + * Smaller values make the ridge-free threshold more conservative. + * + * @default 0.25 + */ + maxHorizontalRidgeScore?: number; + + /** + * Fraction of a row or column that must be occupied by active + * contour pixels for the row or column to be considered + * ridge-like. + * + * For vertical ridge detection, a column is considered suspicious + * when the fraction of active pixels along F1 is greater than or + * equal to this value. + * + * For horizontal ridge detection, the same criterion is applied + * along F2. + * + * Expressed as a fraction in the range [0, 1]. + * + * Lower values make ridge detection more sensitive. + * + * @default 0.5 + */ + ridgeCoverageThreshold?: number; + + /** + * Lowest threshold considered by the automatic contour search, + * expressed as a multiple of the supplied noise level. + * + * For example, minSigma = 2 means that thresholds below 2 × + * noiseLevel are never considered. + * + * Lower values can preserve weaker peaks but increase the risk + * of including noise. + * + * @default 2 + */ + minSigma?: number; + + /** + * Exponent used to determine the spacing between candidate + * minimum contour levels. + * + * Candidate thresholds are generated multiplicatively using: + * + * candidateStep = contourRatio ** candidateRatioExponent + * + * For example, with contourRatio = 1.4 and exponent = 0.5: + * + * candidateStep = sqrt(1.4) + * + * A value of 1 evaluates candidates directly on the contour-level + * hierarchy. Smaller values provide finer threshold resolution + * while increasing the amount of analysis. + * + * @default 0.5 + */ + candidateRatioExponent?: number; + + /** + * Number of successive contour levels used when evaluating + * spectral persistence. + * + * A larger value requires spectral structures to remain stable + * across more contour levels and therefore produces a more + * conservative threshold. + * + * @default 4 + */ + persistenceLevels?: number; + + /** + * Maximum dimension used for spatial analysis. + * + * Large input matrices are reduced using max pooling before + * calculating occupancy, persistence, and ridge metrics. + * + * The original matrix is not modified. + * + * Max pooling is used instead of averaging so that narrow, + * high-intensity spectral features are preserved during analysis. + * + * Larger values improve spatial resolution but increase + * computational cost and memory usage. + * + * @default 384 + */ + maxAnalysisDimension?: number; + /** + * Whether diagnostic information should be returned. + * + * Diagnostics include the selected thresholds, FDR, occupancy, + * persistence, vertical and horizontal ridge scores, and the + * individual criteria used by the automatic threshold selection. + * + * Disable this when diagnostics are not needed and the smallest + * possible result object is preferred. + * + * @default true + */ + diagnostics?: boolean; +} + +export function findAutomaticContourLevels( + matrix: ArrayLike, + rows: number, + cols: number, + noiseLevel: number, + options: AutoContourOptions & { diagnostics: true }, +): AutoContourResult; +export function findAutomaticContourLevels( + matrix: ArrayLike, + rows: number, + cols: number, + noiseLevel: number, + options?: AutoContourOptions, +): AutoContourResult; +export function findAutomaticContourLevels( + matrix: ArrayLike, + rows: number, + cols: number, + noiseLevel: number, + options: AutoContourOptions & { diagnostics: false }, +): AutoContourResult; + +export function findAutomaticContourLevels( + matrix: ArrayLike, + rows: number, + cols: number, + noiseLevel: number, + options: AutoContourOptions = {}, +): AutoContourResult { + validateInput(matrix, rows, cols, noiseLevel); + + const { + contourRatio = 1.4, + maxFdr = 0.05, + maxOccupancy = 0.02, + minPersistence = 0.35, + maxVerticalRidgeScore = 0.25, + maxHorizontalRidgeScore = 0.25, + ridgeCoverageThreshold = 0.5, + minSigma = 5, + candidateRatioExponent = 0.5, + persistenceLevels = 4, + maxAnalysisDimension = 384, + diagnostics = true, + } = options; + + if (contourRatio <= 1) { + throw new Error('contourRatio must be > 1'); + } + + const analysis = maxPoolAbsolute(matrix, rows, cols, maxAnalysisDimension); + const { + values, + rows: analysisRows, + cols: analysisCols, + maxAbsoluteValue, + } = analysis; + const thresholdIndex = createThresholdIndex(values); + + const maxSigmaInData = maxAbsoluteValue / noiseLevel; + const candidates = generateCandidateSigmaLevels( + minSigma, + maxSigmaInData, + contourRatio, + candidateRatioExponent, + ); + + const evaluations: Array> = []; + let recommended: ReturnType | undefined; + + for (const sigmaMultiplier of candidates) { + const evaluation = evaluateThreshold( + values, + analysisRows, + analysisCols, + noiseLevel, + sigmaMultiplier, + contourRatio, + persistenceLevels, + ridgeCoverageThreshold, + false, + thresholdIndex, + ); + evaluations.push(evaluation); + + const passed = + evaluation.fdr <= maxFdr && + evaluation.occupancy <= maxOccupancy && + evaluation.persistence >= minPersistence; + + if (passed) { + recommended = evaluation; + break; + } + } + + if (!recommended) { + recommended = evaluateFallbackThreshold( + evaluations, + maxFdr, + maxOccupancy, + minPersistence, + ); + } + + const ridgeFreeLevel = findRidgeFreeThresholdTopDown( + values, + analysisRows, + analysisCols, + noiseLevel, + recommended.sigmaMultiplier * noiseLevel, + maxAbsoluteValue, + contourRatio, + maxVerticalRidgeScore, + maxHorizontalRidgeScore, + ridgeCoverageThreshold, + ); + + const hasT1Noise = + recommended.verticalRidgeScore > maxVerticalRidgeScore || + recommended.horizontalRidgeScore > maxHorizontalRidgeScore; + + const result: AutoContourResult = { + maxAbsoluteValue, + minLevel: recommended.sigmaMultiplier * noiseLevel, + minLevelWithoutT1Noise: ridgeFreeLevel, + hasT1Noise, + + sigmaMultiplier: recommended.sigmaMultiplier, + sigmaMultiplierWithoutT1Noise: ridgeFreeLevel / noiseLevel, + }; + + if (diagnostics) { + const recommendedDiagnostics = evaluateThreshold( + values, + analysisRows, + analysisCols, + noiseLevel, + recommended.sigmaMultiplier, + contourRatio, + persistenceLevels, + ridgeCoverageThreshold, + true, + thresholdIndex, + ); + const ridgeFree = evaluateThreshold( + values, + analysisRows, + analysisCols, + noiseLevel, + ridgeFreeLevel / noiseLevel, + contourRatio, + 1, + ridgeCoverageThreshold, + true, + thresholdIndex, + ); + + result.diagnostics = { + recommended: { + sigmaMultiplier: recommendedDiagnostics.sigmaMultiplier, + minLevel: recommendedDiagnostics.sigmaMultiplier * noiseLevel, + occupancy: recommendedDiagnostics.occupancy, + fdr: recommendedDiagnostics.fdr, + persistence: recommendedDiagnostics.persistence, + verticalRidgeScore: recommendedDiagnostics.verticalRidgeScore, + horizontalRidgeScore: recommendedDiagnostics.horizontalRidgeScore, + }, + ridgeFree: { + sigmaMultiplier: ridgeFree.sigmaMultiplier, + minLevel: ridgeFreeLevel, + occupancy: ridgeFree.occupancy, + fdr: ridgeFree.fdr, + persistence: ridgeFree.persistence, + verticalRidgeScore: ridgeFree.verticalRidgeScore, + horizontalRidgeScore: ridgeFree.horizontalRidgeScore, + }, + }; + } + + return result; +} diff --git a/src/data/data2d/Spectrum2D/findBestMinContour/math.ts b/src/data/data2d/Spectrum2D/findBestMinContour/math.ts new file mode 100644 index 000000000..b8e33c719 --- /dev/null +++ b/src/data/data2d/Spectrum2D/findBestMinContour/math.ts @@ -0,0 +1,59 @@ +export function gaussianTwoSidedTail(k: number): number { + return erfc(k / Math.SQRT2); +} + +/** + * Approximation of the complementary error function. + * @TODO it would be replaced by the same implementation from ml-spectra-processing. + */ +function erfc(x: number): number { + const sign = x < 0 ? -1 : 1; + const ax = Math.abs(x); + + const p = 0.3275911; + + const a1 = 0.254829592; + const a2 = -0.284496736; + const a3 = 1.421413741; + const a4 = -1.453152027; + const a5 = 1.061405429; + + const t = 1 / (1 + p * ax); + + const polynomial = ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t; + + const result = polynomial * Math.exp(-ax * ax); + + return sign >= 0 ? result : 2 - result; +} + +export function violation(value: number, limit: number): number { + if (limit === 0) { + return value === 0 ? 0 : 1; + } + + return Math.max(0, value / limit - 1); +} + +export function validateInput( + matrix: ArrayLike, + rows: number, + cols: number, + noiseLevel: number, +): void { + if (!Number.isInteger(rows) || rows <= 0) { + throw new Error('rows must be a positive integer'); + } + + if (!Number.isInteger(cols) || cols <= 0) { + throw new Error('cols must be a positive integer'); + } + + if (matrix.length !== rows * cols) { + throw new Error('matrix.length must equal rows * cols'); + } + + if (!Number.isFinite(noiseLevel) || noiseLevel <= 0) { + throw new Error('noiseLevel must be a positive finite number'); + } +} diff --git a/src/data/data2d/__tests__/estimateNoiseLevel.test.ts b/src/data/data2d/__tests__/estimateNoiseLevel.test.ts new file mode 100644 index 000000000..cf369fc96 --- /dev/null +++ b/src/data/data2d/__tests__/estimateNoiseLevel.test.ts @@ -0,0 +1,19 @@ +import { expect, test } from 'vitest'; + +import { estimateNoiseLevel } from '../Spectrum2D/findBestMinContour/estimateNoiseLevel.js'; + +const GAUSSIAN_MAD_SCALE = 1 / 0.6744897501960817; + +test('estimates Gaussian noise from a downsampled matrix', () => { + const matrix = new Float64Array([-2, 99, -1, 99, 1, 99, 2, 99]); + + expect(estimateNoiseLevel(matrix, { maxSamples: 4 })).toBeCloseTo( + GAUSSIAN_MAD_SCALE, + ); +}); + +test('rejects invalid sample sizes and zero MAD', () => { + expect(() => + estimateNoiseLevel(new Float64Array([0, 1]), { maxSamples: 0 }), + ).toThrow('maxSamples must be a positive integer'); +}); diff --git a/src/data/data2d/__tests__/findAutomaticContourLevels.test.ts b/src/data/data2d/__tests__/findAutomaticContourLevels.test.ts new file mode 100644 index 000000000..9e375faa3 --- /dev/null +++ b/src/data/data2d/__tests__/findAutomaticContourLevels.test.ts @@ -0,0 +1,153 @@ +import { expect, test } from 'vitest'; + +import { + computePersistence, + countValuesAboveThreshold, + createThresholdIndex, + evaluateThreshold, + maxPoolAbsolute, +} from '../Spectrum2D/findBestMinContour/evaluation.js'; +import { findAutomaticContourLevels } from '../Spectrum2D/findBestMinContour/findAutomaticContourLevels.js'; + +test('maxPoolAbsolute returns the global finite absolute maximum', () => { + const analysis = maxPoolAbsolute( + new Float64Array([-3, Number.NaN, 2, -7, Number.POSITIVE_INFINITY, 4]), + 2, + 3, + 2, + ); + + expect(analysis.maxAbsoluteValue).toBe(7 * 0.95); +}); + +test('sorted threshold counts preserve direct comparison semantics', () => { + const matrix = new Float64Array([ + Number.NaN, + Number.NEGATIVE_INFINITY, + -1, + 0, + 0, + 2, + 2, + Number.POSITIVE_INFINITY, + ]); + const thresholds = [ + Number.NEGATIVE_INFINITY, + -1, + 0, + 1, + 2, + Number.POSITIVE_INFINITY, + Number.NaN, + ]; + + const thresholdIndex = createThresholdIndex(matrix); + + for (const threshold of thresholds) { + expect(thresholdIndex.countAtLeast(threshold)).toBe( + countValuesAboveThreshold(matrix, threshold), + ); + } + + expect( + computePersistence(matrix, 1, matrix.length, -1, 2, 4, thresholdIndex), + ).toBe(computePersistence(matrix, 1, matrix.length, -1, 2, 4)); + + expect( + evaluateThreshold( + matrix, + 1, + matrix.length, + 1, + 0, + 2, + 4, + 0.5, + false, + thresholdIndex, + ), + ).toEqual( + evaluateThreshold(matrix, 1, matrix.length, 1, 0, 2, 4, 0.5, false), + ); +}); + +test('skipping ridge metrics preserves threshold selection metrics', () => { + const matrix = new Float64Array([0, 2, 3, 0]); + const withRidgeMetrics = evaluateThreshold(matrix, 2, 2, 1, 2, 1.5, 2, 0.5); + const withoutRidgeMetrics = evaluateThreshold( + matrix, + 2, + 2, + 1, + 2, + 1.5, + 2, + 0.5, + false, + ); + + expect(withoutRidgeMetrics).toEqual({ + ...withRidgeMetrics, + verticalRidgeScore: 0, + horizontalRidgeScore: 0, + }); + expect(withRidgeMetrics.verticalRidgeScore).toBe(1); + expect(withRidgeMetrics.horizontalRidgeScore).toBe(1); +}); + +test('diagnostics do not change selected contour levels', () => { + const matrix = new Float64Array(36); + matrix[7] = 3; + matrix[21] = 8; + const options = { + contourRatio: 1.6, + maxFdr: 0.01, + maxOccupancy: 0.2, + minPersistence: 0.35, + minSigma: 2, + candidateRatioExponent: 0.5, + persistenceLevels: 1, + maxVerticalRidgeScore: 0.25, + maxHorizontalRidgeScore: 0.25, + ridgeCoverageThreshold: 0.5, + }; + + const withDiagnostics = findAutomaticContourLevels(matrix, 6, 6, 1, { + ...options, + diagnostics: true, + }); + const withoutDiagnostics = findAutomaticContourLevels(matrix, 6, 6, 1, { + ...options, + diagnostics: false, + }); + + expect(withoutDiagnostics).toMatchObject({ + minLevel: withDiagnostics.minLevel, + minLevelWithoutT1Noise: withDiagnostics.minLevelWithoutT1Noise, + sigmaMultiplier: withDiagnostics.sigmaMultiplier, + sigmaMultiplierWithoutT1Noise: + withDiagnostics.sigmaMultiplierWithoutT1Noise, + hasT1Noise: withDiagnostics.hasT1Noise, + }); + expect(withDiagnostics.diagnostics).toBeDefined(); + expect(withoutDiagnostics.diagnostics).toBeUndefined(); + expect(withoutDiagnostics.hasT1Noise).toBe(withDiagnostics.hasT1Noise); +}); + +test('fallback uses the first cached candidate when scores tie', () => { + const matrix = new Float64Array([ + 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + + const result = findAutomaticContourLevels(matrix, 4, 4, 1, { + contourRatio: 2, + candidateRatioExponent: 1, + minSigma: 2, + maxFdr: 0, + maxOccupancy: 0, + minPersistence: 1, + diagnostics: false, + }); + + expect(result.sigmaMultiplier).toBe(2); +}); diff --git a/src/data/utilities/calculateSanPlot.ts b/src/data/utilities/calculateSanPlot.ts index c3548797f..9a27f5e82 100644 --- a/src/data/utilities/calculateSanPlot.ts +++ b/src/data/utilities/calculateSanPlot.ts @@ -4,13 +4,14 @@ import { xNoiseSanPlot } from 'ml-spectra-processing'; export function calculateSanPlot( dimension: T, data: T extends '1D' ? NmrData1D : NmrData2DFt['rr'], + options?: { magnitudeMode?: boolean }, ) { const input = dimension === '1D' ? prepare1DData(data as NmrData1D) : prepare2DData(data as NmrData2DFt['rr']); - return xNoiseSanPlot(input); + return xNoiseSanPlot(input, options); } function prepare1DData(data: NmrData1D) {