diff --git a/docs/user/web/monitor_jobs.md b/docs/user/web/monitor_jobs.md index 95fc67c4..e82ea856 100644 --- a/docs/user/web/monitor_jobs.md +++ b/docs/user/web/monitor_jobs.md @@ -17,6 +17,7 @@ ## Use the table +By default, the jobs are displayed in a table. If you are viewing them in another chart, you can click the table button next to the search bar to switch back to the table view. The table displays the jobs that match the criteria specified in the search bar. Each row represents a job, and the columns show various attributes of the job, such as its ID, status, type, and submission date. ### Actions on the table @@ -32,3 +33,5 @@ You can select one or more jobs by clicking on the checkboxes next to each job. - **Kill**: This button will kill the selected jobs. - **Delete**: This button will delete the selected jobs. +## Use the Pie Chart +You can change the visualization to use a pie chart with the button next to the search bar. The pie chart provides a hierarchical view of the jobs based on their attributes. The `Columns to plot` component lets you choose your criteria for visualizing the jobs. The chart can display two levels, and you can then click on a section of the chart to zoom into that category and see more details. diff --git a/package-lock.json b/package-lock.json index 4224a408..e7916c46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -174,23 +174,23 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "dev": true, "license": "MIT", "engines": { @@ -198,22 +198,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", - "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.10", - "@babel/types": "^7.26.10", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -239,15 +239,15 @@ } }, "node_modules/@babel/generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", - "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -255,27 +255,27 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.9" + "@babel/types": "^7.27.3" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", - "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.8", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -295,18 +295,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.0.tgz", - "integrity": "sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", + "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/helper-replace-supers": "^7.26.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/traverse": "^7.27.0", + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.27.1", "semver": "^6.3.1" }, "engines": { @@ -327,13 +327,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.0.tgz", - "integrity": "sha512-fO8l08T76v48BhpNRW/nQ0MxfnSdoSKUJBMjubOAYffsVuGG5qOfMq7N6Es7UJvi7Y8goXXo07EfcHZXDPuELQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-annotate-as-pure": "^7.27.1", "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, @@ -355,59 +355,68 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", - "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", - "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" }, "engines": { "node": ">=6.9.0" @@ -417,22 +426,22 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", - "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.9" + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", "engines": { @@ -440,15 +449,15 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", - "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-wrap-function": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -458,15 +467,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", - "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.26.5" + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -476,41 +485,41 @@ } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", - "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { @@ -518,41 +527,41 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", - "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", + "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", - "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.0" + "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -562,14 +571,14 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", - "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", + "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -579,13 +588,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", - "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -595,13 +604,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", - "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -611,15 +620,15 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", - "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -629,14 +638,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", - "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", + "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -727,13 +736,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", - "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -743,13 +752,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -944,13 +953,13 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", - "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -960,15 +969,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", - "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.26.8" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -978,15 +987,15 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", - "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-remap-async-to-generator": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -996,13 +1005,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", - "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1012,13 +1021,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.0.tgz", - "integrity": "sha512-u1jGphZ8uDI2Pj/HJj6YQ6XQLZCNjOlprjxB5SVz6rq2T6SwAR+CdrWK0CP7F+9rDVMXdB0+r6Am5G5aobOjAQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", + "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1028,14 +1037,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", - "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1045,14 +1054,14 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", - "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", + "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1062,18 +1071,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", - "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", + "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", - "@babel/traverse": "^7.25.9", - "globals": "^11.1.0" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1083,14 +1092,14 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", - "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/template": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1100,13 +1109,14 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", - "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1116,14 +1126,14 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", - "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1133,13 +1143,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", - "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1149,14 +1159,14 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", - "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1166,13 +1176,30 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", - "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", + "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1182,13 +1209,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", - "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1198,13 +1225,13 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", - "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1214,14 +1241,14 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", - "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1231,15 +1258,15 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", - "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1249,13 +1276,13 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", - "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1265,13 +1292,13 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", - "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1281,13 +1308,13 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", - "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1297,13 +1324,13 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", - "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1313,14 +1340,14 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", - "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1330,14 +1357,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", - "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1347,16 +1374,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", - "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1366,14 +1393,14 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", - "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1383,14 +1410,14 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", - "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1400,13 +1427,13 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", - "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1416,13 +1443,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.26.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", - "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1432,13 +1459,13 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", - "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1448,15 +1475,17 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", - "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", + "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9" + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1466,14 +1495,14 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", - "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1483,13 +1512,13 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", - "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1499,14 +1528,14 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", - "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1516,13 +1545,13 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", - "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1532,14 +1561,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", - "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1549,15 +1578,15 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", - "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1567,13 +1596,13 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", - "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1652,14 +1681,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.0.tgz", - "integrity": "sha512-LX/vCajUJQDqE7Aum/ELUMZAY19+cDpghxrnyt5I1tV6X5PyC86AOoWXWFYFeIvauyeSA6/ktn4tQVn/3ZifsA==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.0.tgz", + "integrity": "sha512-LOAozRVbqxEVjSKfhGnuLoE4Kz4Oc5UJzuvFUhSsQzdCdaAQu06mG8zDv2GFSerM62nImUZ7K92vxnQcLSDlCQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "regenerator-transform": "^0.15.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1669,14 +1697,14 @@ } }, "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", - "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1686,13 +1714,13 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", - "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1733,13 +1761,13 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", - "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1749,14 +1777,14 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", - "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1766,13 +1794,13 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", - "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1782,13 +1810,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", - "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1798,13 +1826,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.0.tgz", - "integrity": "sha512-+LLkxA9rKJpNoGsbLnAgOCdESl73vwYn+V6b+5wHbrE7OGKVDPHIQvbFSzqE6rwqaCw2RE+zdJrlLkcf8YOA0w==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1834,13 +1862,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", - "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1850,14 +1878,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", - "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1867,14 +1895,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", - "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1884,14 +1912,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", - "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1901,80 +1929,81 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", - "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.0.tgz", + "integrity": "sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.8", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", + "@babel/compat-data": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.26.0", - "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.26.8", - "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.26.5", - "@babel/plugin-transform-block-scoping": "^7.25.9", - "@babel/plugin-transform-class-properties": "^7.25.9", - "@babel/plugin-transform-class-static-block": "^7.26.0", - "@babel/plugin-transform-classes": "^7.25.9", - "@babel/plugin-transform-computed-properties": "^7.25.9", - "@babel/plugin-transform-destructuring": "^7.25.9", - "@babel/plugin-transform-dotall-regex": "^7.25.9", - "@babel/plugin-transform-duplicate-keys": "^7.25.9", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.26.3", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.26.9", - "@babel/plugin-transform-function-name": "^7.25.9", - "@babel/plugin-transform-json-strings": "^7.25.9", - "@babel/plugin-transform-literals": "^7.25.9", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", - "@babel/plugin-transform-member-expression-literals": "^7.25.9", - "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.26.3", - "@babel/plugin-transform-modules-systemjs": "^7.25.9", - "@babel/plugin-transform-modules-umd": "^7.25.9", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", - "@babel/plugin-transform-numeric-separator": "^7.25.9", - "@babel/plugin-transform-object-rest-spread": "^7.25.9", - "@babel/plugin-transform-object-super": "^7.25.9", - "@babel/plugin-transform-optional-catch-binding": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9", - "@babel/plugin-transform-private-methods": "^7.25.9", - "@babel/plugin-transform-private-property-in-object": "^7.25.9", - "@babel/plugin-transform-property-literals": "^7.25.9", - "@babel/plugin-transform-regenerator": "^7.25.9", - "@babel/plugin-transform-regexp-modifiers": "^7.26.0", - "@babel/plugin-transform-reserved-words": "^7.25.9", - "@babel/plugin-transform-shorthand-properties": "^7.25.9", - "@babel/plugin-transform-spread": "^7.25.9", - "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.26.8", - "@babel/plugin-transform-typeof-symbol": "^7.26.7", - "@babel/plugin-transform-unicode-escapes": "^7.25.9", - "@babel/plugin-transform-unicode-property-regex": "^7.25.9", - "@babel/plugin-transform-unicode-regex": "^7.25.9", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.0", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.0", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.27.1", + "@babel/plugin-transform-classes": "^7.28.0", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.0", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.11.0", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.40.0", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "engines": { @@ -1984,6 +2013,20 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -2063,45 +2106,45 @@ } }, "node_modules/@babel/template": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", - "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", - "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.27.0", - "@babel/parser": "^7.27.0", - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz", + "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -4501,17 +4544,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -4523,15 +4562,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/source-map": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", @@ -4550,9 +4580,9 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -7252,84 +7282,343 @@ "@types/node": "*" } }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "dev": true, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", "license": "MIT", "dependencies": { - "@types/ms": "*" + "@types/d3-selection": "*" } }, - "node_modules/@types/doctrine": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", - "integrity": "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", "license": "MIT", "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "@types/d3-selection": "*" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", "license": "MIT", "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "@types/d3-array": "*", + "@types/geojson": "*" } }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", "license": "MIT" }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, + "node_modules/@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", "license": "MIT", "dependencies": { - "@types/node": "*" + "@types/d3-selection": "*" } }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "dev": true, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", "license": "MIT" }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", "license": "MIT" }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "*" + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/doctrine": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", + "integrity": "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { @@ -9223,14 +9512,14 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz", - "integrity": "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.4", + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { @@ -9262,13 +9551,13 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz", - "integrity": "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.4" + "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -9609,9 +9898,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "funding": [ { "type": "opencollective", @@ -9628,10 +9917,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -9847,9 +10136,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001716", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001716.tgz", - "integrity": "sha512-49/c1+x3Kwz7ZIWt+4DvK3aMJy9oYXXG6/97JKsnjdCk/6n9vVyWL8NAwVt95Lwt9eigI10Hl782kDfZUUlRXw==", + "version": "1.0.30001726", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", + "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", "funding": [ { "type": "opencollective", @@ -10403,13 +10692,13 @@ "license": "MIT" }, "node_modules/core-js-compat": { - "version": "3.42.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.42.0.tgz", - "integrity": "sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==", + "version": "3.43.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz", + "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.24.4" + "browserslist": "^4.25.0" }, "funding": { "type": "opencollective", @@ -10686,202 +10975,612 @@ "domutils": "^2.8.0", "nth-check": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/cypress": { + "version": "14.3.2", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-14.3.2.tgz", + "integrity": "sha512-n+yGD2ZFFKgy7I3YtVpZ7BcFYrrDMcKj713eOZdtxPttpBjCyw/R8dLlFSsJPouneGN7A/HOSRyPJ5+3/gKDoA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@cypress/request": "^3.0.8", + "@cypress/xvfb": "^1.2.4", + "@types/sinonjs__fake-timers": "8.1.1", + "@types/sizzle": "^2.3.2", + "arch": "^2.2.0", + "blob-util": "^2.0.2", + "bluebird": "^3.7.2", + "buffer": "^5.7.1", + "cachedir": "^2.3.0", + "chalk": "^4.1.0", + "check-more-types": "^2.24.0", + "ci-info": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-table3": "~0.6.5", + "commander": "^6.2.1", + "common-tags": "^1.8.0", + "dayjs": "^1.10.4", + "debug": "^4.3.4", + "enquirer": "^2.3.6", + "eventemitter2": "6.4.7", + "execa": "4.1.0", + "executable": "^4.1.1", + "extract-zip": "2.0.1", + "figures": "^3.2.0", + "fs-extra": "^9.1.0", + "getos": "^3.2.1", + "is-installed-globally": "~0.4.0", + "lazy-ass": "^1.6.0", + "listr2": "^3.8.3", + "lodash": "^4.17.21", + "log-symbols": "^4.0.0", + "minimist": "^1.2.8", + "ospath": "^1.2.2", + "pretty-bytes": "^5.6.0", + "process": "^0.11.10", + "proxy-from-env": "1.0.0", + "request-progress": "^3.0.0", + "semver": "^7.7.1", + "supports-color": "^8.1.1", + "tmp": "~0.2.3", + "tree-kill": "1.2.2", + "untildify": "^4.0.0", + "yauzl": "^2.10.0" + }, + "bin": { + "cypress": "bin/cypress" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/cypress/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cypress/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cypress/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cypress/node_modules/eventemitter2": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cypress/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" } }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", "engines": { - "node": ">= 6" + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "engines": { + "node": ">=12" } }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "dev": true, - "license": "MIT" + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", - "dev": true, - "license": "MIT" + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "license": "MIT", + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", "dependencies": { - "cssom": "~0.3.6" + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true, - "license": "MIT" + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } }, - "node_modules/cypress": { - "version": "14.3.2", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-14.3.2.tgz", - "integrity": "sha512-n+yGD2ZFFKgy7I3YtVpZ7BcFYrrDMcKj713eOZdtxPttpBjCyw/R8dLlFSsJPouneGN7A/HOSRyPJ5+3/gKDoA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", "dependencies": { - "@cypress/request": "^3.0.8", - "@cypress/xvfb": "^1.2.4", - "@types/sinonjs__fake-timers": "8.1.1", - "@types/sizzle": "^2.3.2", - "arch": "^2.2.0", - "blob-util": "^2.0.2", - "bluebird": "^3.7.2", - "buffer": "^5.7.1", - "cachedir": "^2.3.0", - "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "ci-info": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.5", - "commander": "^6.2.1", - "common-tags": "^1.8.0", - "dayjs": "^1.10.4", - "debug": "^4.3.4", - "enquirer": "^2.3.6", - "eventemitter2": "6.4.7", - "execa": "4.1.0", - "executable": "^4.1.1", - "extract-zip": "2.0.1", - "figures": "^3.2.0", - "fs-extra": "^9.1.0", - "getos": "^3.2.1", - "is-installed-globally": "~0.4.0", - "lazy-ass": "^1.6.0", - "listr2": "^3.8.3", - "lodash": "^4.17.21", - "log-symbols": "^4.0.0", - "minimist": "^1.2.8", - "ospath": "^1.2.2", - "pretty-bytes": "^5.6.0", - "process": "^0.11.10", - "proxy-from-env": "1.0.0", - "request-progress": "^3.0.0", - "semver": "^7.7.1", - "supports-color": "^8.1.1", - "tmp": "~0.2.3", - "tree-kill": "1.2.2", - "untildify": "^4.0.0", - "yauzl": "^2.10.0" - }, - "bin": { - "cypress": "bin/cypress" + "d3-path": "^3.1.0" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": ">=12" } }, - "node_modules/cypress/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", "dependencies": { - "color-convert": "^2.0.1" + "d3-array": "2 - 3" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=12" } }, - "node_modules/cypress/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "d3-time": "1 - 3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=12" } }, - "node_modules/cypress/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", "dependencies": { - "has-flag": "^4.0.0" + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" } }, - "node_modules/cypress/node_modules/eventemitter2": { - "version": "6.4.7", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", - "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cypress/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/damerau-levenshtein": { @@ -10993,9 +11692,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -11110,6 +11809,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -11416,9 +12124,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.145", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.145.tgz", - "integrity": "sha512-pZ5EcTWRq/055MvSBgoFEyKf2i4apwfoqJbK/ak2jnFq8oHjZ+vzc3AhRcz37Xn+ZJfL58R666FLJx0YOK9yTw==", + "version": "1.5.179", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.179.tgz", + "integrity": "sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==", "license": "ISC" }, "node_modules/elliptic": { @@ -13635,15 +14343,6 @@ "node": ">=10" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -14060,7 +14759,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -14269,6 +14967,15 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/is-arguments": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", @@ -21275,16 +21982,6 @@ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "license": "MIT" }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, "node_modules/regex-parser": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.1.tgz", @@ -21691,6 +22388,12 @@ "inherits": "^2.0.1" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, "node_modules/rollup": { "version": "4.40.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz", @@ -21755,6 +22458,12 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -21845,7 +22554,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, "license": "MIT" }, "node_modules/sass-loader": { @@ -24932,9 +25640,12 @@ "@mui/utils": "^6.1.6", "@mui/x-date-pickers": "^7.28.3", "@tanstack/react-table": "^8.20.5", + "@types/d3": "^7.4.3", "@types/node": "^20.17.6", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", + "d3": "^7.9.0", + "d3-hierarchy": "^3.1.2", "dayjs": "^1.11.13", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -24942,7 +25653,8 @@ "swr": "^2.2.5" }, "devDependencies": { - "@babel/preset-env": "^7.26.9", + "@babel/core": "^7.28.0", + "@babel/preset-env": "^7.28.0", "@babel/preset-react": "^7.24.7", "@babel/preset-typescript": "^7.24.7", "@chromatic-com/storybook": "^3.2.4", diff --git a/packages/diracx-web-components/.storybook/main.ts b/packages/diracx-web-components/.storybook/main.ts index 91784996..9ab99349 100644 --- a/packages/diracx-web-components/.storybook/main.ts +++ b/packages/diracx-web-components/.storybook/main.ts @@ -43,7 +43,7 @@ const config: StorybookConfig = { "../stories/mocks/metadata.mock.tsx", ), "./jobDataService": require.resolve( - "../stories/mocks/jobDataService.mock.ts", + "../stories/mocks/jobDataService.mock.tsx", ), }; return config; diff --git a/packages/diracx-web-components/.storybook/preview.tsx b/packages/diracx-web-components/.storybook/preview.tsx index 208bba51..0f8086fc 100644 --- a/packages/diracx-web-components/.storybook/preview.tsx +++ b/packages/diracx-web-components/.storybook/preview.tsx @@ -1,5 +1,4 @@ import type { Preview } from "@storybook/react"; -import React from "react"; import { CssBaseline } from "@mui/material"; import { ThemeProvider } from "../src/contexts/ThemeProvider"; diff --git a/packages/diracx-web-components/babel.config.cjs b/packages/diracx-web-components/babel.config.js similarity index 87% rename from packages/diracx-web-components/babel.config.cjs rename to packages/diracx-web-components/babel.config.js index 85db31a9..753d9125 100644 --- a/packages/diracx-web-components/babel.config.cjs +++ b/packages/diracx-web-components/babel.config.js @@ -1,4 +1,4 @@ -module.exports = { +export default { presets: [ "@babel/preset-env", ["@babel/preset-react", { runtime: "automatic" }], diff --git a/packages/diracx-web-components/jest.config.js b/packages/diracx-web-components/jest.config.js index 313a7a3d..2f219d67 100644 --- a/packages/diracx-web-components/jest.config.js +++ b/packages/diracx-web-components/jest.config.js @@ -13,7 +13,20 @@ const config = { moduleNameMapper: { "^@axa-fr/react-oidc$": "/stories/mocks/react-oidc.mock.tsx", "^../../hooks/metadata$": "/stories/mocks/metadata.mock.tsx", - "^./jobDataService$": "/stories/mocks/jobDataService.mock.ts", + "^./jobDataService$": "/stories/mocks/jobDataService.mock.tsx", + "\\.css$": "/stories/mocks/style.mock.ts", + }, + + // To support ESM modules in Jest + transformIgnorePatterns: [ + "/node_modules/(?!d3|d3-[^/]+|internmap|delaunator|robust-predicates)", + ], + + // Tell Jest how to transform files + // Use ts-jest for TypeScript files and babel-jest for JavaScript files + transform: { + "^.+\\.(ts|tsx)$": "ts-jest", + "^.+\\.(js|jsx)$": "babel-jest", }, }; diff --git a/packages/diracx-web-components/package.json b/packages/diracx-web-components/package.json index 1e0750c9..37752511 100644 --- a/packages/diracx-web-components/package.json +++ b/packages/diracx-web-components/package.json @@ -31,9 +31,12 @@ "@mui/utils": "^6.1.6", "@mui/x-date-pickers": "^7.28.3", "@tanstack/react-table": "^8.20.5", + "@types/d3": "^7.4.3", "@types/node": "^20.17.6", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", + "d3": "^7.9.0", + "d3-hierarchy": "^3.1.2", "dayjs": "^1.11.13", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -41,7 +44,8 @@ "swr": "^2.2.5" }, "devDependencies": { - "@babel/preset-env": "^7.26.9", + "@babel/core": "^7.28.0", + "@babel/preset-env": "^7.28.0", "@babel/preset-react": "^7.24.7", "@babel/preset-typescript": "^7.24.7", "@chromatic-com/storybook": "^3.2.4", diff --git a/packages/diracx-web-components/src/components/JobMonitor/JobDataTable.tsx b/packages/diracx-web-components/src/components/JobMonitor/JobDataTable.tsx index 991d9b11..9ac7749d 100644 --- a/packages/diracx-web-components/src/components/JobMonitor/JobDataTable.tsx +++ b/packages/diracx-web-components/src/components/JobMonitor/JobDataTable.tsx @@ -32,7 +32,6 @@ import { deleteJobs, getJobHistory, killJobs, - refreshJobs, rescheduleJobs, useJobs, } from "./jobDataService"; @@ -109,11 +108,15 @@ export function JobDataTable({ // State for job history const [isHistoryDialogOpen, setIsHistoryDialogOpen] = useState(false); const [jobHistoryData, setJobHistoryData] = useState([]); - /** * Fetches the jobs from the /api/jobs/search endpoint */ - const { data, error, isLoading, isValidating } = useJobs( + const { + data: results, + headers: dataHeader, + error: dataError, + isLoading, + } = useJobs( diracxUrl, accessToken, searchBody, @@ -121,9 +124,6 @@ export function JobDataTable({ pagination.pageSize, ); - const dataHeader = data?.headers; - const results = useMemo(() => data?.data || [], [data?.data]); - // Parse the headers to get the first item, last item and number of items const contentRange = dataHeader?.get("content-range"); let totalJobs = 0; @@ -148,13 +148,10 @@ export function JobDataTable({ const selectedIds = Object.keys(rowSelection).map(Number); await deleteJobs(diracxUrl, selectedIds, accessToken, accessTokenPayload); setBackdropOpen(false); - refreshJobs( - diracxUrl, - accessToken, - searchBody, - pagination.pageIndex, - pagination.pageSize, - ); + // Refresh the data manually + setSearchBody((prev) => { + return { ...prev }; + }); clearSelected(); setSnackbarInfo({ open: true, @@ -206,13 +203,12 @@ export function JobDataTable({ ); setBackdropOpen(false); - refreshJobs( - diracxUrl, - accessToken, - searchBody, - pagination.pageIndex, - pagination.pageSize, - ); + + // Refresh the data manually + setSearchBody((prev) => { + return { ...prev }; + }); + clearSelected(); // Handle Snackbar Messaging if (successfulJobs.length > 0 && failedJobs.length > 0) { @@ -278,13 +274,10 @@ export function JobDataTable({ ); setBackdropOpen(false); - refreshJobs( - diracxUrl, - accessToken, - searchBody, - pagination.pageIndex, - pagination.pageSize, - ); + // Refresh the data manually + setSearchBody((prev) => { + return { ...prev }; + }); clearSelected(); // Handle Snackbar Messaging if (successfulJobs.length > 0 && failedJobs.length > 0) { @@ -412,7 +405,7 @@ export function JobDataTable({ * Table instance */ const table = useReactTable({ - data: results, + data: useMemo(() => results || [], [results]), columns, state: { columnVisibility, @@ -450,9 +443,8 @@ export function JobDataTable({ totalRows={totalJobs} searchBody={searchBody} setSearchBody={setSearchBody} - error={error} + error={dataError} isLoading={isLoading} - isValidating={isValidating} toolbarComponents={toolbarComponents} menuItems={menuItems} /> diff --git a/packages/diracx-web-components/src/components/JobMonitor/JobMonitor.tsx b/packages/diracx-web-components/src/components/JobMonitor/JobMonitor.tsx index d00dde3c..9ff741fd 100644 --- a/packages/diracx-web-components/src/components/JobMonitor/JobMonitor.tsx +++ b/packages/diracx-web-components/src/components/JobMonitor/JobMonitor.tsx @@ -13,7 +13,11 @@ import { lime, amber, } from "@mui/material/colors"; + import { lighten, darken, useTheme, Box } from "@mui/material"; + +import { TableChart, DonutSmall } from "@mui/icons-material"; + import { createColumnHelper, ColumnPinningState, @@ -25,9 +29,15 @@ import { import { useApplicationId } from "../../hooks/application"; import { Filter } from "../../types/Filter"; -import { Job, SearchBody, CategoryType } from "../../types"; +import { + Job, + SearchBody, + CategoryType, + JobMonitorChartType, +} from "../../types"; import { JobDataTable } from "./JobDataTable"; import { JobSearchBar } from "./JobSearchBar"; +import { JobSunburst } from "./JobSunburst"; /** * Build the Job Monitor application @@ -96,6 +106,8 @@ export default function JobMonitor() { }, ); + const [chartType, setChartType] = useState(JobMonitorChartType.TABLE); + // Save the state of the app in local storage useEffect(() => { const state = { @@ -122,23 +134,6 @@ export default function JobMonitor() { pagination, ]); - // Handle the application of filters - const handleApplyFilters = () => { - setSearchBody((prev) => ({ - ...prev, - search: filters.map(({ parameter, operator, value, values }) => ({ - parameter: fromHumanReadableText(parameter, columns), - operator, - value, - values, - })), - })); - setPagination((prevState) => ({ - ...prevState, - pageIndex: 0, - })); - }; - // Status colors const statusColors: Record = useMemo( () => ({ @@ -197,7 +192,7 @@ export default function JobMonitor() { columnHelper.accessor("JobID", { id: "JobID", header: "ID", - meta: { type: CategoryType.NUMBER, hideSuggestion: true }, + meta: { type: CategoryType.NUMBER, isQuasiUnique: true }, }), columnHelper.accessor("Status", { id: "Status", @@ -206,7 +201,7 @@ export default function JobMonitor() { meta: { type: CategoryType.STRING, values: Object.keys(statusColors).sort(), - hideSuggestion: false, + isQuasiUnique: false, }, }), columnHelper.accessor("MinorStatus", { @@ -236,17 +231,17 @@ export default function JobMonitor() { columnHelper.accessor("LastUpdateTime", { id: "LastUpdateTime", header: "Last Update Time", - meta: { type: CategoryType.DATE, hideSuggestion: true }, + meta: { type: CategoryType.DATE, isQuasiUnique: true }, }), columnHelper.accessor("HeartBeatTime", { id: "HeartBeatTime", header: "Last Sign of Life", - meta: { type: CategoryType.DATE, hideSuggestion: true }, + meta: { type: CategoryType.DATE, isQuasiUnique: true }, }), columnHelper.accessor("SubmissionTime", { id: "SubmissionTime", header: "Submission Time", - meta: { type: CategoryType.DATE, hideSuggestion: true }, + meta: { type: CategoryType.DATE, isQuasiUnique: true }, }), columnHelper.accessor("Owner", { id: "Owner", @@ -263,12 +258,12 @@ export default function JobMonitor() { columnHelper.accessor("StartExecTime", { id: "StartExecTime", header: "Start Execution Time", - meta: { type: CategoryType.DATE, hideSuggestion: true }, + meta: { type: CategoryType.DATE, isQuasiUnique: true }, }), columnHelper.accessor("EndExecTime", { id: "EndExecTime", header: "End Execution Time", - meta: { type: CategoryType.DATE, hideSuggestion: true }, + meta: { type: CategoryType.DATE, isQuasiUnique: true }, }), columnHelper.accessor("UserPriority", { id: "UserPriority", @@ -284,6 +279,23 @@ export default function JobMonitor() { [columnHelper, renderStatusCell, statusColors], ); + // Handle the application of filters + const handleApplyFilters = useCallback(() => { + setSearchBody((prev) => ({ + ...prev, + search: filters.map(({ parameter, operator, value, values }) => ({ + parameter: fromHumanReadableText(parameter, columns), + operator, + value, + values, + })), + })); + setPagination((prev) => ({ + ...prev, + pageIndex: 0, // Reset to the first page when applying filters + })); + }, [filters, columns, setSearchBody, setPagination]); + return ( - - + + , + }, + { + plotName: JobMonitorChartType.SUNBURST, + icon: , + }, + ], + }} + /> + + + {chartType === JobMonitorChartType.TABLE && ( + + )} + {chartType === JobMonitorChartType.SUNBURST && ( + + )} ); } diff --git a/packages/diracx-web-components/src/components/JobMonitor/JobSearchBar.tsx b/packages/diracx-web-components/src/components/JobMonitor/JobSearchBar.tsx index fe716689..ac8810f7 100644 --- a/packages/diracx-web-components/src/components/JobMonitor/JobSearchBar.tsx +++ b/packages/diracx-web-components/src/components/JobMonitor/JobSearchBar.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useRef } from "react"; +import { useEffect } from "react"; import { ColumnDef } from "@tanstack/react-table"; import { useOidcAccessToken } from "@axa-fr/react-oidc"; import { useOIDCContext } from "../../hooks/oidcConfiguration"; @@ -17,6 +17,7 @@ import { Operators, SearchBarTokenNature, CategoryType, + JobMonitorChartType, } from "../../types"; import { getJobSummary } from "./jobDataService"; import { fromHumanReadableText } from "./JobMonitor"; @@ -33,6 +34,15 @@ interface JobSearchBarProps { /** The columns to display in the job monitor */ // eslint-disable-next-line @typescript-eslint/no-explicit-any columns: ColumnDef[]; + /** Props for the plot type selector */ + plotTypeSelectorProps?: { + /** The type of the plot */ + plotType: JobMonitorChartType; + /** Function to set the plot type */ + setPlotType: React.Dispatch>; + /** List of buttons to select the type of plot */ + buttonList?: { plotName: JobMonitorChartType; icon: React.ReactNode }[]; + }; } export function JobSearchBar({ @@ -41,19 +51,15 @@ export function JobSearchBar({ setFilters, handleApplyFilters, columns, + plotTypeSelectorProps, }: JobSearchBarProps) { const { configuration } = useOIDCContext(); const { accessToken } = useOidcAccessToken(configuration?.scope); - const oldFilters = useRef(""); const diracxUrl = useDiracxUrl(); useEffect(() => { - const currentFilters = JSON.stringify(filters); - if (oldFilters.current !== currentFilters) { - oldFilters.current = currentFilters; - handleApplyFilters(); - } + handleApplyFilters(); }, [filters, handleApplyFilters]); return ( @@ -76,6 +82,7 @@ export function JobSearchBar({ ) } allowKeyWordSearch={false} // Disable keyword search for job monitor + plotTypeSelectorProps={plotTypeSelectorProps} /> ); } @@ -135,12 +142,12 @@ async function createSuggestions( previousToken.nature === SearchBarTokenNature.CUSTOM || previousToken.nature === SearchBarTokenNature.VALUE ) { - const items = columns.map((column) => column.header as string); + const items = columns.map((column) => String(column.header)); const type = columns.map( (column) => column.meta?.type || CategoryType.STRING, ) as CategoryType[]; const hideSuggestion = columns.map( - (column) => column.meta?.hideSuggestion || false, + (column) => column.meta?.isQuasiUnique || false, ); return { @@ -165,7 +172,7 @@ async function createSuggestions( if (!previousToken.hideSuggestion) { // Load the suggestions for the selected category const category = fromHumanReadableText( - previousEquation.items[0].label as string, + String(previousEquation.items[0].label), columns, ); await fetchJobSummary(category); diff --git a/packages/diracx-web-components/src/components/JobMonitor/JobSunburst.tsx b/packages/diracx-web-components/src/components/JobMonitor/JobSunburst.tsx new file mode 100644 index 00000000..95ac449b --- /dev/null +++ b/packages/diracx-web-components/src/components/JobMonitor/JobSunburst.tsx @@ -0,0 +1,286 @@ +import { useState, useEffect } from "react"; + +import { scaleOrdinal, quantize, interpolateRainbow } from "d3"; + +import { useOidcAccessToken } from "@axa-fr/react-oidc"; +import { ColumnDef } from "@tanstack/react-table"; +import { useDiracxUrl } from "../../hooks/utils"; + +import type { JobSummary, SearchBody, Job, SunburstTree } from "../../types"; +import { Sunburst } from "../shared/Sunburst"; +import { useOIDCContext } from "../../hooks/oidcConfiguration"; +import { ChartView } from "../shared"; +import { getJobSummary } from "./jobDataService"; + +import { fromHumanReadableText } from "./JobMonitor"; + +/** + * Create the JobSunburst component. + * + * @param searchBody The search body to be used in the search + * @param statusColors The colors to be used for the different job statuses + * @param columns The columns to be used in the table + * @returns + */ +export function JobSunburst({ + searchBody, + statusColors, + columns, +}: { + searchBody: SearchBody; + statusColors: Record; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + columns: ColumnDef[]; +}) { + const { configuration } = useOIDCContext(); + const { accessToken } = useOidcAccessToken(configuration?.scope); + const diracxUrl = useDiracxUrl(); + + const [groupColumns, setGroupColumns] = useState(["Status"]); + const [currentPath, setCurrentPath] = useState([]); + + const [tree, setTree] = useState(undefined); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + const newSearch = currentPath.map((elt, index) => { + return { + parameter: fromHumanReadableText(groupColumns[index], columns), + operator: "eq", + value: elt, + }; + }); + const newSearchBody: SearchBody = { + ...searchBody, + search: searchBody.search + ? searchBody.search.concat(newSearch) + : newSearch, + }; + async function load() { + setIsLoading(true); + const res = await fetchAndBuildTree( + groupColumns.slice(currentPath.length, currentPath.length + 2), + newSearchBody, + diracxUrl, + accessToken, + columns, + ); + setTree({ + name: "", + children: res, + }); + setIsLoading(false); + } + load(); + }, [ + groupColumns[currentPath.length], + groupColumns[currentPath.length + 1], + currentPath, + searchBody, + diracxUrl, + accessToken, + ]); + + const defaultColors = scaleOrdinal( + quantize(interpolateRainbow, (tree?.children?.length ?? 0) + 1), + ); + + function colorScales(name: string, _size: number, _depth: number): string { + if (statusColors[name]) { + return statusColors[name]; + } + if (tree?.children) { + return defaultColors(name); + } + return "#ccc"; + } + + const columnList = columns + .filter((column) => column.meta?.isQuasiUnique !== true) + .map((column) => String(column.header)); + + const hasHiddenLevels = groupColumns.length > currentPath.length + 2; + + const Chart = ( + + ); + + return ( + + ); +} + +/** + * Builds the tree from a given path + * + * @param groupColumns Array of columns to be used in the group by + * @param searchBody The search body to be used in the search + * @param diracxUrl The URL of the DiracX instance + * @param accessToken The access token to be used for authentication + * @param columns The columns to be used in the table + * @returns + */ +export async function fetchAndBuildTree( + groupColumns: string[], + searchBody: SearchBody, + diracxUrl: string | null, + accessToken: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + columns: ColumnDef[], +): Promise { + if (groupColumns.length === 0) { + return []; + } + + let data: JobSummary[] = []; + + const formatedGroupColumns = groupColumns.map((columnName) => + fromHumanReadableText(columnName, columns), + ); + + if (diracxUrl && accessToken) { + data = await getJobSummary( + diracxUrl, + formatedGroupColumns, + accessToken, + searchBody, + ).then((res) => res.data || []); + return buildTree(data, formatedGroupColumns); + } + return []; +} + +/** + * Builds a tree for the charts + * + * @param data Data to be transformed into a tree + * @param groupColumns Array of columns to be used in the group by + * @param parentPath The path to this Data (optional) + * @returns The tree corresponding or the sum if it's a leaf + */ +function buildTree( + data: JobSummary[], + groupColumns: string[], + parentPath: string[] = [], +): SunburstTree[] { + if (groupColumns.length === 0) return []; + + const current = groupColumns[0]; + + const groupedData = data.reduce>((acc, item) => { + const key: string = String(item[current]); + if (!acc[key]) { + acc[key] = []; + } + acc[key].push(item); + return acc; + }, {}); + + const total = data.reduce((sum, item) => sum + Number(item["count"]), 0); + const threshold = total * 0.05; + + const nodes: SunburstTree[] = []; + let othersValue: SunburstTree | null = null; + + for (const key in groupedData) { + const group = groupedData[key]; + const groupTotal = group.reduce( + (sum, item) => sum + Number(item["count"]), + 0, + ); + + if (groupTotal < threshold) { + // Too small group, add to "Others" + if (othersValue === null) { + if (groupColumns.length === 1) + othersValue = { name: key, value: groupTotal }; + else + othersValue = { + name: key, + children: buildTree(group, groupColumns.slice(1), [ + ...parentPath, + key, + ]), + }; + } else if (othersValue) { + othersValue = { + name: "Others", + value: + (othersValue.children + ? othersValue.children[0].value || 0 + : othersValue.value || 0) + groupTotal, + }; + } + } else { + if (groupColumns.length === 1) { + nodes.push({ + name: key, + value: groupTotal, + }); + } else { + nodes.push({ + name: key, + children: buildTree(group, groupColumns.slice(1), [ + ...parentPath, + key, + ]), + }); + } + } + } + + if (othersValue) { + nodes.push(othersValue); + } + + return nodes; +} + +/** + * + * @param size The number of jobs + * @param total The total number of jobs (optional) + * @returns A string with the number of jobs + */ +function sizeToText(size: number, total?: number): string { + if (size > 1e9) + return ( + `${(size / 1e9).toFixed(2)}B \njobs` + + (total ? ` (${((size / total) * 100).toFixed(2)}%)` : "") + ); + if (size > 1e6) + return ( + `${(size / 1e6).toFixed(2)}M \njobs` + + (total ? ` (${((size / total) * 100).toFixed(2)}%)` : "") + ); + if (size > 1e3) + return ( + `${(size / 1e3).toFixed(2)}k \njobs` + + (total ? ` (${((size / total) * 100).toFixed(2)}%)` : "") + ); + if (size > 1) + return ( + `${size} jobs` + (total ? ` (${((size / total) * 100).toFixed(2)}%)` : "") + ); + if (size === 1) + return `1 job` + (total ? ` (${((size / total) * 100).toFixed(2)}%)` : ""); + return ""; +} diff --git a/packages/diracx-web-components/src/components/JobMonitor/jobDataService.ts b/packages/diracx-web-components/src/components/JobMonitor/jobDataService.ts index d0c5cbad..17d7b675 100644 --- a/packages/diracx-web-components/src/components/JobMonitor/jobDataService.ts +++ b/packages/diracx-web-components/src/components/JobMonitor/jobDataService.ts @@ -1,9 +1,10 @@ "use client"; -import useSWR, { mutate } from "swr"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; +import { useEffect, useState } from "react"; + dayjs.extend(utc); import { fetcher } from "../../hooks/utils"; import { Filter, SearchBody, Job, JobHistory } from "../../types"; @@ -11,6 +12,11 @@ import type { JobSummary } from "../../types"; type TimeUnit = "minute" | "hour" | "day" | "month" | "year"; +/** + * Convert the 'last' operator in the search body to a date filter. + * @param searchBody The search body to be processed + * @returns The processed search body with adjusted filters + */ function processSearchBody(searchBody: SearchBody) { searchBody.search = searchBody.search?.map((filter: Filter) => { if (filter.operator == "last") { @@ -42,63 +48,6 @@ function processSearchBody(searchBody: SearchBody) { }); } -/** - * Custom hook for fetching jobs data. - * - * @param diracxUrl - The base URL of the DiracX API. - * @param accessToken - The access token for authentication. - * @param searchBody - The search body for filtering jobs. - * @param page - The page number for pagination. - * @param rowsPerPage - The number of rows per page. - * @returns The response from the API call. - */ -export const useJobs = ( - diracxUrl: string | null, - accessToken: string, - searchBody: SearchBody, - page: number, - rowsPerPage: number, -) => { - const urlGetJobs = diracxUrl - ? `${diracxUrl}/api/jobs/search?page=${page + 1}&per_page=${rowsPerPage}` - : null; - - processSearchBody(searchBody); - - return useSWR( - urlGetJobs ? [urlGetJobs, accessToken, "POST", searchBody] : null, - (args) => fetcher(args), - { - revalidateOnFocus: false, - }, - ); -}; - -/** - * Refreshes the jobs by mutating the SWR cache with the search body and pagination values - * - * @param diracxUrl - The base URL of the DiracX API. - * @param accessToken - The access token for authentication. - * @param searchBody - The search body containing the filters and search criteria. - * @param page - The page number for pagination. - * @param rowsPerPage - The number of rows per page for pagination. - */ -export const refreshJobs = ( - diracxUrl: string | null, - accessToken: string, - searchBody: SearchBody, - page: number, - rowsPerPage: number, -) => { - if (!diracxUrl) { - throw new Error("Invalid URL generated for refreshing jobs."); - } - - const urlGetJobs = `${diracxUrl}/api/jobs/search?page=${page + 1}&per_page=${rowsPerPage}`; - processSearchBody(searchBody); - mutate([urlGetJobs, accessToken, "POST", searchBody]); -}; - /** * Deletes jobs with the specified IDs. * @@ -285,3 +234,82 @@ export async function getJobSummary( return { data }; } + +/** + * Custom hook for fetching jobs data. + * + * @param diracxUrl - The base URL of the DiracX API. + * @param accessToken - The access token for authentication. + * @param searchBody - The search body for filtering jobs. + * @param page - The page number for pagination. + * @param rowsPerPage - The number of rows per page. + * @returns The response from the API call. + */ +export function useJobs( + diracxUrl: string | null, + accessToken: string, + searchBody: SearchBody, + page: number, + rowsPerPage: number, +) { + const [data, setData] = useState(null); + const [headers, setHeaders] = useState(new Headers()); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + if (!diracxUrl) { + setData(null); + setIsLoading(false); + setError(new Error("Invalid URL generated for fetching jobs.")); + return; + } + + let cancelled = false; + + async function fetchJobs() { + setIsLoading(true); + + const urlGetJobs = `${diracxUrl}/api/jobs/search?page=${page + 1}&per_page=${rowsPerPage}`; + try { + processSearchBody(searchBody); + + const body = { + search: searchBody?.search || [], + sort: searchBody?.sort || [], + }; + + // Expect the response to be an array of objects with all the grouping fields + const res = await fetcher([ + urlGetJobs, + accessToken, + "POST", + body, + ]); + + if (!cancelled) { + setData(res.data); + setIsLoading(false); + setHeaders(res.headers); + setError(null); + } + } catch { + setData(null); + setIsLoading(false); + setError(new Error("Failed to fetch jobs")); + } + } + fetchJobs(); + + return () => { + cancelled = true; + }; + }, [diracxUrl, accessToken, searchBody, page, rowsPerPage]); + + return { + headers, + data, + isLoading, + error, + }; +} diff --git a/packages/diracx-web-components/src/components/shared/ChartView/ChartView.tsx b/packages/diracx-web-components/src/components/shared/ChartView/ChartView.tsx new file mode 100644 index 00000000..792e4743 --- /dev/null +++ b/packages/diracx-web-components/src/components/shared/ChartView/ChartView.tsx @@ -0,0 +1,89 @@ +import React from "react"; + +import { Box } from "@mui/material"; + +import { ColumnSelector } from "./ColumnSelector"; + +interface ChartViewProps { + /** The chart to be displayed */ + chart: JSX.Element; + /** List of columns available for selection */ + columnList: string[]; + /** Currently selected group columns */ + groupColumns: string[]; + /** Function to set the group columns */ + setGroupColumns: React.Dispatch>; + /** The current path in the chart */ + currentPath: string[]; + /** Function to set the current path in the chart */ + setCurrentPath: React.Dispatch>; + /** Default group columns to be used */ + defaultColumns: string[]; + /** Optional title for the column selector */ + title?: string; +} + +/** + * Creates a component that displays a chart and allows users to select columns for grouping. + * + * @param props Props for the ChartViewLayout component + * @see ChartDisplayLayoutProps + * @returns + */ +export function ChartView({ + chart, + columnList, + groupColumns, + setGroupColumns, + currentPath, + setCurrentPath, + defaultColumns: defaultGroupColumns, + title = "Level selector", +}: ChartViewProps) { + return ( + + {/* Left Section: The chart */} + + {chart} + + + {/* Right Section: Column selection */} + + + + + ); +} diff --git a/packages/diracx-web-components/src/components/shared/ChartView/ColumnSelector.tsx b/packages/diracx-web-components/src/components/shared/ChartView/ColumnSelector.tsx new file mode 100644 index 00000000..f8ddd260 --- /dev/null +++ b/packages/diracx-web-components/src/components/shared/ChartView/ColumnSelector.tsx @@ -0,0 +1,330 @@ +import React, { useEffect, useRef, useState } from "react"; +import { + Box, + Card, + Typography, + Button, + InputLabel, + MenuItem, + FormControl, + Tooltip, +} from "@mui/material"; +import Select, { SelectChangeEvent } from "@mui/material/Select"; +import RestoreIcon from "@mui/icons-material/Restore"; +import DragIndicatorIcon from "@mui/icons-material/DragIndicator"; +import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine"; +import { + draggable, + dropTargetForElements, +} from "@atlaskit/pragmatic-drag-and-drop/element/adapter"; +import { DropIndicator } from "@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/box"; +import { + Edge, + attachClosestEdge, + extractClosestEdge, +} from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge"; + +interface SelectColumnsProps { + /** The row data*/ + columnList: string[]; + /** The columns used in the group by */ + groupColumns: string[]; + /** Setter for groupColumns */ + setGroupColumns: React.Dispatch>; + /** The current path in the tree */ + currentPath: string[]; + /** Setter for the current path in the tree */ + setCurrentPath: React.Dispatch>; + /** Default columns to use */ + defaultColumns: string[]; + /** Optional title for the column selector */ + title?: string; +} + +/** + * This component is used to select the columns to be used in the group by + * + * @param props See SelectColumnProps for more detials + * @see {@link SelectColumnProps} + * @returns A table which managed the group by on the columns + */ +export function ColumnSelector({ + columnList, + groupColumns, + setGroupColumns, + currentPath, + setCurrentPath, + defaultColumns: defaultGroupColumns, + title = "Column Selector", +}: SelectColumnsProps) { + /** + * Change the columns used for the group by + * + * @param event The event which triggers the change + * @param depth The depth in the tree + */ + const handleChange = (event: SelectChangeEvent, depth: number) => { + let newGroups = [...groupColumns]; + if (event.target.value === "None") { + // Delete a column + newGroups = newGroups.filter((_elt, index) => index !== depth); + if (newGroups.length > 0 && currentPath.length > depth) + setCurrentPath((currentPath) => + currentPath.slice(0, Math.max(0, depth - 1)), + ); + } else { + // Add or change a column + if (newGroups[depth]) { + // Change the column + newGroups[depth] = event.target.value; + if (currentPath.length > depth) + setCurrentPath((currentPath) => currentPath.slice(0, depth)); + } else { + // Add a column + newGroups.push(event.target.value); + } + } + setGroupColumns(newGroups); + }; + + /** + * Reorder columns based on drag and drop + * + * @param fromIndex The original index + * @param toIndex The target index + */ + const handleReorder = (fromIndex: number, toIndex: number) => { + if (fromIndex === toIndex) return; + + const newColumns = [...groupColumns]; + + // Only move existing columns (not the "add new" one) + if (fromIndex < newColumns.length) { + // Remove the item from its original position + const [movedItem] = newColumns.splice(fromIndex, 1); + + newColumns.splice(toIndex, 0, movedItem); + setGroupColumns(newColumns); + + // Reset the current path since we changed the hierarchy + setCurrentPath([]); + } + }; + + const resetColumnsToPlot = () => { + setGroupColumns(defaultGroupColumns); + setCurrentPath([]); + }; + + /** A arrray with one cell per column in the group by */ + const additionalChoice = []; + + for (let i = 0; i < groupColumns.length + 1; i++) { + const availableColumns = columnList.filter( + (column) => column === groupColumns[i] || !groupColumns.includes(column), + ); + + additionalChoice.push( + , + ); + } + + return ( + + + + + {title} + + + + {additionalChoice} + +
+ +
+
+
+ ); +} + +interface ColumnSelectProps { + /** The columns used in the group by */ + groupColumns: string[]; + /** The index of the column in the group by */ + index: number; + /** Function to handle the change of the column */ + handleChange: (event: SelectChangeEvent, index: number) => void; + /** The available columns to select from */ + availableColumns: string[]; + /** Function to handle the reordering of the columns */ + onReorder: (fromIndex: number, toIndex: number) => void; +} + +function ColumnSelect({ + groupColumns, + index, + handleChange, + availableColumns, + onReorder, +}: ColumnSelectProps) { + // Ref to use for the draggable element + const dragRef = useRef(null); + // Ref to use for the handle of the draggable element + const handleRef = useRef(null); + // Represents the closest edge to the mouse cursor + const [closestEdge, setClosestEdge] = useState(null); + + const isLastItem = index === groupColumns.length; + + useEffect(() => { + if (!dragRef.current || !handleRef.current || isLastItem) return; + + const element = dragRef.current; + const handleElement = handleRef.current; + + return combine( + // Makes the element draggable + draggable({ + element, + dragHandle: handleElement, + getInitialData: () => ({ index }), + }), + + // Makes the element a drop target + dropTargetForElements({ + element, + getData: ({ input, element }) => { + return attachClosestEdge( + { index }, + { input, element, allowedEdges: ["top", "bottom"] }, + ); + }, + + onDrag({ self, source }) { + const isSource = source.element === element; + if (isSource) { + setClosestEdge(null); + return; + } + + const closestEdge = extractClosestEdge(self.data); + const sourceIndex = source.data.index; + + if (typeof sourceIndex === "number") { + const isItemBeforeSource = index === sourceIndex - 1; + const isItemAfterSource = index === sourceIndex + 1; + + const isDropIndicatorHidden = + (isItemBeforeSource && closestEdge === "bottom") || + (isItemAfterSource && closestEdge === "top"); + + if (isDropIndicatorHidden) { + setClosestEdge(null); + return; + } + } + + setClosestEdge(closestEdge); + }, + onDragLeave() { + setClosestEdge(null); + }, + onDrop({ self, source }) { + const closestEdge = extractClosestEdge(self.data); + const fromIndex = source.data.index as number; + let toIndex = index; + + if (closestEdge === "bottom") { + toIndex = index + 1; + } + + // Only reorder if from and to indexes are different + if (fromIndex != undefined && toIndex !== fromIndex) { + onReorder(fromIndex, toIndex); + } + + setClosestEdge(null); + }, + }), + ); + }, [index, isLastItem, onReorder]); + + return ( + + {!isLastItem && ( + + + + )} + + {isLastItem && } + + + Level {index + 1} + + + + {closestEdge && } + + ); +} diff --git a/packages/diracx-web-components/src/components/shared/ChartView/index.ts b/packages/diracx-web-components/src/components/shared/ChartView/index.ts new file mode 100644 index 00000000..f15b7b67 --- /dev/null +++ b/packages/diracx-web-components/src/components/shared/ChartView/index.ts @@ -0,0 +1 @@ +export { ChartView } from "./ChartView"; diff --git a/packages/diracx-web-components/src/components/shared/DataTable.tsx b/packages/diracx-web-components/src/components/shared/DataTable.tsx index aae7e16f..e71c5958 100644 --- a/packages/diracx-web-components/src/components/shared/DataTable.tsx +++ b/packages/diracx-web-components/src/components/shared/DataTable.tsx @@ -199,7 +199,7 @@ function DataTableToolbar>({ * @property {function} setSearchBody - the function to call when the search body changes * @property {Column[]} columns - the columns of the table * @property {T[]} rows - the rows of the table - * @property {string | null} error - the error message + * @property {Error | null} error - the error message * @property {JSX.Element} toolbarComponents - the components to display in the toolbar * @property {MenuItem[]} menuItems - the menu items */ @@ -214,10 +214,8 @@ export interface DataTableProps> { searchBody: SearchBody; /** The function to call when the search body changes */ setSearchBody: React.Dispatch>; - /** The error message */ - error: string | null; - /** Whether the table is validating */ - isValidating: boolean; + /** The error or null if no error */ + error: Error | null; /** Whether the table is loading */ isLoading: boolean; /** The components to display in the toolbar */ @@ -239,7 +237,6 @@ export function DataTable>({ setSearchBody, error, isLoading, - isValidating, toolbarComponents, menuItems, }: DataTableProps) { @@ -348,10 +345,10 @@ export function DataTable>({ // Handle no data const noData = !rows || rows.length === 0; - if (isValidating || isLoading || error || noData) { + if (isLoading || error || noData) { return ( - {isValidating || isLoading ? ( + {isLoading ? ( >({ /> ) : error ? ( - An error occurred while fetching data. Reload the page. + {error.message || + "An error occurred while fetching data. Reload the page."} ) : ( diff --git a/packages/diracx-web-components/src/components/shared/SearchBar/PlotTypeSelector.tsx b/packages/diracx-web-components/src/components/shared/SearchBar/PlotTypeSelector.tsx new file mode 100644 index 00000000..886906f5 --- /dev/null +++ b/packages/diracx-web-components/src/components/shared/SearchBar/PlotTypeSelector.tsx @@ -0,0 +1,45 @@ +import { ReactNode } from "react"; + +import { ToggleButton, ToggleButtonGroup, Tooltip } from "@mui/material"; + +interface PlotTypeSelectorProps { + /** The type of the plot */ + plotType: T; + /** Function to set the plot type */ + setPlotType: React.Dispatch>; + /** List of name and JSX elements to display as buttons */ + buttonList?: { plotName: T; icon: ReactNode }[]; +} + +/** + * Component to select the type of plot. + * + * @param plotType The type of the plot. + * @param setPlotType The setter for the plot type. + * @param buttonList List of buttons to select the type of plot. + * @returns A selector for the plot type. + */ +export function PlotTypeSelector({ + plotType, + setPlotType, + buttonList = [], +}: PlotTypeSelectorProps) { + return ( + { + if (val !== null) setPlotType(val); + }} + aria-label="text alignment" + > + {buttonList.map((button) => ( + + + {button.icon} + + + ))} + + ); +} diff --git a/packages/diracx-web-components/src/components/shared/SearchBar/SearchBar.tsx b/packages/diracx-web-components/src/components/shared/SearchBar/SearchBar.tsx index 4987f43c..411dddd9 100644 --- a/packages/diracx-web-components/src/components/shared/SearchBar/SearchBar.tsx +++ b/packages/diracx-web-components/src/components/shared/SearchBar/SearchBar.tsx @@ -3,6 +3,7 @@ import React, { useState, useRef, useEffect } from "react"; import { Box, Menu, MenuItem, IconButton } from "@mui/material"; import DeleteIcon from "@mui/icons-material/Delete"; +import RefreshIcon from "@mui/icons-material/Refresh"; import { Filter, @@ -29,8 +30,9 @@ import { } from "./defaultFunctions"; import SearchField from "./SearchField"; +import { PlotTypeSelector } from "./PlotTypeSelector"; -export interface SearchBarProps { +export interface SearchBarProps { /** The filters to be applied to the search */ filters: Filter[]; /** The function to set the filters */ @@ -55,6 +57,11 @@ export interface SearchBarProps { ) => void; /** Whether to allow keyword search or not (default is true) */ allowKeyWordSearch?: boolean; + plotTypeSelectorProps?: { + plotType: T; + setPlotType: React.Dispatch>; + buttonList?: { plotName: T; icon: React.ReactNode }[]; + }; } /** @@ -64,14 +71,15 @@ export interface SearchBarProps { * @param props - The properties for the SearchBar component. * @returns The rendered SearchBar component. */ -export function SearchBar({ +export function SearchBar({ filters, setFilters, createSuggestions, searchFunction = convertAndApplyFilters, clearFunction = defaultClearFunction, allowKeyWordSearch = true, -}: SearchBarProps) { + plotTypeSelectorProps, +}: SearchBarProps) { const [inputValue, setInputValue] = useState(""); const [anchorEl, setAnchorEl] = useState(null); const [clickedTokenIndex, setClickedTokenIndex] = @@ -121,7 +129,7 @@ export function SearchBar({ } if (filters.length !== 0 && tokenEquations.length === 0) load(); - }, [filters, createSuggestions]); + }, []); // Create a list of options based on the current tokens and data useEffect(() => { @@ -134,7 +142,7 @@ export function SearchBar({ setSuggestions(result); } load(); - }, [previousEquation, previousToken, createSuggestions]); + }, [previousEquation, previousToken, createSuggestions, focusedTokenIndex]); // Timer to delay the search function // This effect will trigger the searchFonction after a delay if the equations are valid @@ -157,7 +165,7 @@ export function SearchBar({ const hasChanged = currentEquationsString !== lastSearchedEquationsRef.current; - if (allEquationsValid && searchFunction && hasChanged) { + if (allEquationsValid && hasChanged) { searchTimerRef.current = setTimeout(() => { lastSearchedEquationsRef.current = currentEquationsString; searchFunction(tokenEquations, setFilters); @@ -282,76 +290,106 @@ export function SearchBar({ return ( { - inputRef.current?.focus(); - }} sx={{ - width: 1, display: "flex", - border: "1px solid", - borderColor: "grey.400", - overflow: "auto", - borderRadius: 1, - ":focus-within": { - borderColor: "primary.main", - }, + flexDirection: "row", + width: 1, }} - data-testid="search-bar" > - - {tokenEquations.map((equation, index) => ( - - handleOptionMenuOpen(e, index, tokenIndex) + {/* The search bar */} + { + inputRef.current?.focus(); + }} + sx={{ + width: 1, + height: "auto", + display: "flex", + border: "1px solid", + borderColor: "grey.400", + overflow: "hidden", + borderRadius: 1, + ":focus-within": { + borderColor: "primary.main", + }, + alignItems: "center", + }} + data-testid="search-bar" + > + + {tokenEquations.map((equation, index) => ( + + handleOptionMenuOpen(e, index, tokenIndex) + } + handleRightClick={() => + setTokenEquations((prev) => [ + ...prev.filter((_, i) => i !== index), + ]) + } // Remove the equation on right click + equationIndex={index} + DynamicSearchField={DynamicSearchField} // The dynamic search field can be in the middle of the equations + focusedTokenIndex={focusedTokenIndex} + /> + ))} + {!focusedTokenIndex && DynamicSearchField} + {/* Otherwise, the search field is at the end */} + + {clickedTokenIndex !== null && + currentSuggestions.items.map((option, idx) => ( + + handleOptionSelect( + option, + currentSuggestions.nature[idx], + currentSuggestions.type[idx], + currentSuggestions.hideSuggestion[idx], + ) + } + > + {option} + + ))} + + + + searchFunction(tokenEquations, setFilters)} + disabled={ + !tokenEquations.every((eq) => eq.status === EquationStatus.VALID) } - handleRightClick={() => - setTokenEquations((prev) => [ - ...prev.filter((_, i) => i !== index), - ]) - } // Remove the equation on right click - equationIndex={index} - DynamicSearchField={DynamicSearchField} // The dynamic search field can be in the middle of the equations - focusedTokenIndex={focusedTokenIndex} - /> - ))} - {!focusedTokenIndex && DynamicSearchField} - {/* Otherwise, the search field is at the end */} - - {clickedTokenIndex !== null && - currentSuggestions.items.map((option, idx) => ( - - handleOptionSelect( - option, - currentSuggestions.nature[idx], - currentSuggestions.type[idx], - currentSuggestions.hideSuggestion[idx], - ) - } - > - {option} - - ))} - + sx={{ width: "40px", height: "40px" }} + > + + + + {tokenEquations.length !== 0 && ( + { + setInputValue(""); + clearFunction(setFilters, setTokenEquations); + }} + sx={{ width: "40px", height: "40px" }} + > + + + )} + - {tokenEquations.length !== 0 && ( - { - setInputValue(""); - clearFunction(setFilters, setTokenEquations); - }} - disabled={tokenEquations.length === 0} - sx={{ marginLeft: "auto", width: "40px", height: "40px" }} - > - - + {/* Plot type selector if provided */} + {plotTypeSelectorProps && ( + )} ); diff --git a/packages/diracx-web-components/src/components/shared/SearchBar/SearchField.tsx b/packages/diracx-web-components/src/components/shared/SearchBar/SearchField.tsx index cf154f78..65266eff 100644 --- a/packages/diracx-web-components/src/components/shared/SearchBar/SearchField.tsx +++ b/packages/diracx-web-components/src/components/shared/SearchBar/SearchField.tsx @@ -287,6 +287,7 @@ export default function SearchField({ setInputValue(value); }} sx={{ + marginTop: "2px", minWidth: "180px", width: "auto", maxWidth: 0.9, diff --git a/packages/diracx-web-components/src/components/shared/SearchBar/index.ts b/packages/diracx-web-components/src/components/shared/SearchBar/index.ts index 76b5bf2a..5932700d 100644 --- a/packages/diracx-web-components/src/components/shared/SearchBar/index.ts +++ b/packages/diracx-web-components/src/components/shared/SearchBar/index.ts @@ -1,2 +1 @@ export { SearchBar } from "./SearchBar"; -export type { SearchBarProps } from "./SearchBar"; diff --git a/packages/diracx-web-components/src/components/shared/Sunburst/BreadCrumbsTrail.tsx b/packages/diracx-web-components/src/components/shared/Sunburst/BreadCrumbsTrail.tsx new file mode 100644 index 00000000..aaaa7a5f --- /dev/null +++ b/packages/diracx-web-components/src/components/shared/Sunburst/BreadCrumbsTrail.tsx @@ -0,0 +1,52 @@ +import { Breadcrumbs, Link } from "@mui/material"; + +/** + * Display a path in the breadcrumb. + * + * @param path The path to display. + * @param setPath The function to set the path. + * @returns The breadcrumb component. + */ +export function BreadCrumbsTrail({ + path, + setPath, +}: { + path: string[]; + setPath?: React.Dispatch>; +}) { + return ( + + { + if (setPath) setPath([]); + }} + sx={{ + cursor: "pointer", + "&:hover": { + textDecoration: "underline", + }, + }} + variant="h6" + > + Top + + {path.map((elt, index) => ( + { + if (setPath) setPath((oldPath) => oldPath.slice(0, index + 1)); + }} + sx={{ + cursor: "pointer", + "&:hover": { + textDecoration: "underline", + }, + }} + variant="h6" + > + {elt} + + ))} + + ); +} diff --git a/packages/diracx-web-components/src/components/shared/Sunburst/Sunburst.tsx b/packages/diracx-web-components/src/components/shared/Sunburst/Sunburst.tsx new file mode 100644 index 00000000..a93f5334 --- /dev/null +++ b/packages/diracx-web-components/src/components/shared/Sunburst/Sunburst.tsx @@ -0,0 +1,364 @@ +import React, { MouseEvent, useEffect, useRef, useMemo } from "react"; + +import { + HierarchyNode, + Arc, + select, + hierarchy, + partition, + arc, + quantize, + interpolateRainbow, +} from "d3"; + +import { scaleOrdinal } from "d3-scale"; + +import { Stack, useTheme, Box, Alert, Skeleton } from "@mui/material"; + +import type { SunburstTree, SunburstNode } from "../../../types"; +import { getPath, sizeToText as defaultSizeToText } from "./Utils"; +import { BreadCrumbsTrail } from "./BreadCrumbsTrail"; + +interface SunburstProps { + /** Formatted data to be displayed in the chart */ + tree: SunburstTree; + /** Boolean indicating if there are hidden levels */ + hasHiddenLevels?: boolean; + /** Function to convert the size to text */ + sizeToText?: (size: number, total?: number) => string; + /** The current path in the data tree */ + currentPath?: string[]; + /** Function to handle right-click events on the chart */ + handleRightClick?: (p: SunburstNode) => void; + /** Function to update the current path */ + setCurrentPath?: React.Dispatch>; + /** Function to generate color scales for the chart */ + colorScales?: (name: string, size: number, depth: number) => string; + /** Boolean indicating if the chart is loading */ + isLoading?: boolean; + /** Error object if there is an error */ + error?: Error | null; +} + +/** + * Create the Sunburst component. + * Adapted from https://observablehq.com/@d3/zoomable-sunburst + * + * @param props The props for the Sunburst. See SunburtProps for details + * @see {@link SunburstProps} + * @returns The Sunurst component + */ +export function Sunburst({ + tree, + hasHiddenLevels = true, + sizeToText = defaultSizeToText, + currentPath, + handleRightClick, + setCurrentPath, + colorScales, + isLoading = false, + error = null, +}: SunburstProps) { + // Create a stable default color scale with useMemo + const defaultColorScale = useMemo(() => { + if (!tree?.children) return () => "#ccc"; + + const colorScale = scaleOrdinal( + quantize(interpolateRainbow, tree.children.length + 1), + ); + return (name: string, _size: number, _depth: number) => colorScale(name); + }, [tree?.children?.length]); + + // Use the provided colorScales or the default one + const finalColorScales = colorScales || defaultColorScale; + + const svgRef = useRef(null); + const tooltipRef: React.RefObject = + useRef(null); + + const theme = useTheme(); + + // Dimensions are for the ViewBox (in px) + const width = 800; + const height = 800; + + useEffect(() => { + // Avoid those specific cases + if (error || isLoading || !tree || tree.children?.length === 0) return; + + const radius: number = width / 7; + + // Compute the layout. + const hierarchyStruct = hierarchy(tree) // Create the tree + .sum((d) => d.value || 0) + .sort( + (a: HierarchyNode, b: HierarchyNode) => { + if (a.value && b.value) return b.value - a.value; + if (a.value) return -1; + if (b.value) return 1; + return 0; + }, + ); + + const root: SunburstNode = partition().size([ + 2 * Math.PI, + hierarchyStruct.height + 1, + ])(hierarchyStruct); + root.each((d) => { + d.current = d; + }); + + // Create the arc generator. + const arcGenerator: Arc = arc() + .startAngle((d) => d.x0) + .endAngle((d) => d.x1) + .padAngle((d) => Math.min((d.x1 - d.x0) / 2, 0.005)) + .padRadius(radius * 1.5) + .innerRadius((d) => d.y0 * radius) + .outerRadius((d) => Math.max(d.y0 * radius, d.y1 * radius - 1)); + + // Create the SVG container. + const svg = select(svgRef.current) + .attr("viewBox", [-width / 2, -height / 2, width, width]) + .style("font", "10px sans-serif"); + + tooltipRef.current!.innerHTML = ""; // Delete the previous tooltip + + // Create the tooltip + const tooltip = select(tooltipRef.current) + .append("div") + .style("position", "absolute") + .style("visibility", "hidden") + .style("border-radius", "4px") + .style("padding", "10px") + .style("background-color", "rgba(230, 230, 230, 0.7)") + .style("color", "black") + .style("pointer-events", "none") + .style("z-index", "99") + .style("-webkit-box-shadow", "7px 7px 10px 4px rgba(0, 0, 0, 0.53)") + .style("box-shadow", "7px 7px 10px 4px rgba(0, 0, 0, 0.53)") + .text(""); + + // Remove any previous elements + svg.selectAll("*").remove(); + + // Cercle in the middle of the Sunburst + svg + .append("circle") + .datum(root) + .attr("r", radius) + .style("cursor", "pointer") + .attr("fill", "none") + .attr("pointer-events", "all") + .on("click", unZoom); + + // Append the arcs. + const path = svg + .append("g") + .selectAll("path") + .data(root.descendants().slice(1)) + .join("path") + .attr("fill", (d) => { + while (d.depth > 1) d = d.parent!; + return finalColorScales(d.data.name, d.x1 - d.x0, d.depth); + }) + .attr("fill-opacity", (d) => + arcVisible(d.current!) + ? d.data.name !== "Others" && (d.children || hasHiddenLevels) + ? 0.8 + : 0.4 + : 0, + ) + .attr("pointer-events", (d) => (arcVisible(d.current!) ? "auto" : "none")) + .attr("d", (d) => arcGenerator(d.current!)); + + function zoom(_event: MouseEvent, p: SunburstNode) { + if (setCurrentPath && currentPath) + setCurrentPath(currentPath.concat(getPath(p))); + } + + function unZoom(_event: MouseEvent, _p: SunburstNode) { + if (setCurrentPath && currentPath) + setCurrentPath(currentPath.slice(0, -1)); + } + + if (setCurrentPath) { + // If the chart can be modified + // Make them clickable if they have children. + path + .filter( + (d: SunburstNode) => + d.data.name !== "Others" && + (Array.isArray(d.children) || hasHiddenLevels), + ) + .style("cursor", "pointer") + .on("click", zoom); + } + + // Make them interact with the mouse + path + .on( + "mouseover", + function (this: SVGPathElement, event: MouseEvent, p: SunburstNode) { + mouseOn.call(this, event, p); + }, + ) + .on( + "mouseout", + function (this: SVGPathElement, event: MouseEvent, p: SunburstNode) { + mouseOut.call(this, event, p); + }, + ) + .on("mousemove", mouseMove) + .on("contextmenu", rightClicked); + + // Text on the chart quarters + svg + .append("g") + .attr("pointer-events", "none") + .attr("text-anchor", "middle") + .style("user-select", "none") + .selectAll("text") + .data(root.descendants().slice(1)) + .join("text") + .attr("dy", "0.35em") + .attr("fill-opacity", (d) => +labelVisible(d.current!)) + .attr("transform", (d) => labelTransform(d.current!)) + .attr("fill", theme.palette.text.primary) + .text((d) => d.data.name); + + // Text with the size in the middle (multi-line support) + const centerText = sizeToText(root.value || 0); + const lines = centerText.split("\n"); + + const textGroup = svg + .append("g") + .attr("text-anchor", "middle") + .attr("fill", theme.palette.text.primary); + + lines.forEach((line, index) => { + textGroup + .append("text") + .attr("x", 0) + .attr("y", (index - (lines.length - 1) / 2) * 35) + .attr("dominant-baseline", "middle") + .attr("font-size", "30px") + .text(line); + }); + + function arcVisible(d: SunburstNode): boolean { + return d.y1 <= 3 && d.y0 >= 1 && d.x1 > d.x0; + } + + function labelVisible(d: SunburstNode): boolean { + return d.y1 <= 3 && d.y0 >= 1 && (d.y1 - d.y0) * (d.x1 - d.x0) > 0.03; + } + + // Move the label to the right place + function labelTransform(d: SunburstNode): string { + const x = (((d.x0 + d.x1) / 2) * 180) / Math.PI; + const y = ((d.y0 + d.y1) / 2) * radius; + return `rotate(${x - 90}) translate(${y},0) rotate(${x < 180 ? 0 : 180})`; + } + + function mouseOn( + this: SVGPathElement | null, + event: MouseEvent, + p: SunburstNode, + ) { + if (p.children && setCurrentPath) { + select(this).transition().duration(30).attr("opacity", "0.85"); + } + tooltip.style("visibility", "visible"); + tooltip + .style("top", event.pageY - 50 + "px") + .style("left", event.pageX - 50 + "px"); + tooltip.text( + (currentPath || []).concat(getPath(p)).join("/") + + ": " + + sizeToText(p.value || 0, root.value), + ); + } + + function mouseOut( + this: SVGPathElement, + _event: MouseEvent, + _p: SunburstNode, + ) { + select(this).transition().duration(30).attr("opacity", "1"); + tooltip.style("visibility", "hidden"); + } + + function mouseMove(event: MouseEvent, _p: SunburstNode) { + tooltip + .style("top", event.pageY - 50 + "px") + .style("left", event.pageX - 50 + "px"); + } + + function rightClicked(event: MouseEvent, p: SunburstNode) { + event.preventDefault(); + if (handleRightClick) handleRightClick(p); + tooltip.style("visibility", "hidden"); + } + }, [ + width, + height, + tree, + handleRightClick, + currentPath, + setCurrentPath, + theme, + finalColorScales, + sizeToText, + ]); + + if (error) { + return ( + + {error.message || "An error occurred while loading the data."} + + ); + } + + if (!tree.children || tree.children.length === 0) + return ( + + No data or no results match your filters. + + ); + + return ( + + + {currentPath && setCurrentPath && ( + + )} + +
+ {isLoading ? ( + + ) : ( + + )} +
+
+ + ); +} diff --git a/packages/diracx-web-components/src/components/shared/Sunburst/Utils.tsx b/packages/diracx-web-components/src/components/shared/Sunburst/Utils.tsx new file mode 100644 index 00000000..7773775e --- /dev/null +++ b/packages/diracx-web-components/src/components/shared/Sunburst/Utils.tsx @@ -0,0 +1,70 @@ +import { SunburstNode } from "../../../types"; + +/** + * Gives the complete path to a node + * + * @param p The target node + * @returns The path + */ +export function getPath(p: SunburstNode): string[] { + const path = [p.data.name]; + let elt: SunburstNode = p; + while (elt.depth > 0) { + elt = elt.parent!; + if (elt.data.name !== "") path.push(elt.data.name); + } + return path.reverse(); +} + +/** + * Converts a size in Bytes to a human-readable format + * + * @param size The size in Bytes + * @param total The total size (optional) to calculate the percentage + * @returns A string with the size in a human-readable format + */ +export function sizeToText(size: number, total?: number): string { + if (size >= 1e18) + return ( + (size / 1e18).toFixed(2) + + " EB" + + (total ? ` (${((size / total) * 100).toFixed(2)}%)` : "") + ); + if (size >= 1e15) + return ( + (size / 1e15).toFixed(2) + + " PB" + + (total ? ` (${((size / total) * 100).toFixed(2)}%)` : "") + ); + if (size >= 1e12) + return ( + (size / 1e12).toFixed(2) + + " TB" + + (total ? ` (${((size / total) * 100).toFixed(2)}%)` : "") + ); + if (size >= 1e9) + return ( + (size / 1e9).toFixed(2) + + " GB" + + (total ? ` (${((size / total) * 100).toFixed(2)}%)` : "") + ); + if (size >= 1e6) + return ( + (size / 1e6).toFixed(2) + + " MB" + + (total ? ` (${((size / total) * 100).toFixed(2)}%)` : "") + ); + if (size >= 1e3) + return ( + (size / 1e3).toFixed(2) + + " KB" + + (total ? ` (${((size / total) * 100).toFixed(2)}%)` : "") + ); + if (size < 1e3 && size >= 0) + return ( + size.toFixed(2) + + "B" + + (total ? ` (${((size / total) * 100).toFixed(2)}%)` : "") + ); + return "none"; +} diff --git a/packages/diracx-web-components/src/components/shared/Sunburst/index.ts b/packages/diracx-web-components/src/components/shared/Sunburst/index.ts new file mode 100644 index 00000000..9ff5138b --- /dev/null +++ b/packages/diracx-web-components/src/components/shared/Sunburst/index.ts @@ -0,0 +1 @@ +export { Sunburst } from "./Sunburst"; diff --git a/packages/diracx-web-components/src/components/shared/index.ts b/packages/diracx-web-components/src/components/shared/index.ts index a44c4698..f0ff014e 100644 --- a/packages/diracx-web-components/src/components/shared/index.ts +++ b/packages/diracx-web-components/src/components/shared/index.ts @@ -1,4 +1,6 @@ export { DataTable } from "./DataTable"; export { ErrorBox } from "./ErrorBox"; export { ApplicationSelector } from "./ApplicationSelector"; -export * from "./SearchBar"; +export { ChartView } from "./ChartView/ChartView"; +export { SearchBar } from "./SearchBar"; +export { Sunburst } from "./Sunburst"; diff --git a/packages/diracx-web-components/src/contexts/DiracXWebProviders.tsx b/packages/diracx-web-components/src/contexts/DiracXWebProviders.tsx index 856a7e2c..947cf839 100644 --- a/packages/diracx-web-components/src/contexts/DiracXWebProviders.tsx +++ b/packages/diracx-web-components/src/contexts/DiracXWebProviders.tsx @@ -3,9 +3,9 @@ import type { ApplicationMetadata, DashboardGroup } from "../types"; import { OIDCSecure } from "../components"; +import { ThemeProvider } from "./ThemeProvider"; import { OIDCConfigurationProvider, - ThemeProvider, NavigationProvider, ApplicationsProvider, } from "./index"; diff --git a/packages/diracx-web-components/src/contexts/index.ts b/packages/diracx-web-components/src/contexts/index.ts index 63c0780d..28acf6d0 100644 --- a/packages/diracx-web-components/src/contexts/index.ts +++ b/packages/diracx-web-components/src/contexts/index.ts @@ -6,10 +6,5 @@ export { OIDCConfigurationContext, OIDCConfigurationProvider, } from "./OIDCConfigurationProvider"; -export { - ThemeContext, - ThemeProvider, - type ThemeProviderProps, -} from "./ThemeProvider"; +export { DiracXWebProviders } from "./DiracXWebProviders"; export * from "./NavigationProvider"; -export * from "./DiracXWebProviders"; diff --git a/packages/diracx-web-components/src/global.d.ts b/packages/diracx-web-components/src/global.d.ts index cab8932a..1163ed64 100644 --- a/packages/diracx-web-components/src/global.d.ts +++ b/packages/diracx-web-components/src/global.d.ts @@ -12,6 +12,6 @@ declare module "@tanstack/react-table" { interface ColumnMeta { type?: CategoryType; values?: string[]; // Optional values for category-type fields - hideSuggestion?: boolean; // Whether to hide suggestions for this column + isQuasiUnique?: boolean; // Some columns are quasi-unique, meaning they can have multiple values but are not fully unique } } diff --git a/packages/diracx-web-components/src/types/JobMonitorChartType.ts b/packages/diracx-web-components/src/types/JobMonitorChartType.ts new file mode 100644 index 00000000..7dc2385e --- /dev/null +++ b/packages/diracx-web-components/src/types/JobMonitorChartType.ts @@ -0,0 +1,7 @@ +/** + * Enum representing the types of charts available in the Job Monitor. + */ +export enum JobMonitorChartType { + TABLE = "CHART_TYPE_TABLE", + SUNBURST = "CHART_TYPE_SUNBURST", +} diff --git a/packages/diracx-web-components/src/types/SunburstData.ts b/packages/diracx-web-components/src/types/SunburstData.ts new file mode 100644 index 00000000..982c5fed --- /dev/null +++ b/packages/diracx-web-components/src/types/SunburstData.ts @@ -0,0 +1,3 @@ +export type SunburstData = { + [key: string]: string | number | boolean; +}; diff --git a/packages/diracx-web-components/src/types/SunburstNode.ts b/packages/diracx-web-components/src/types/SunburstNode.ts new file mode 100644 index 00000000..e758fe7b --- /dev/null +++ b/packages/diracx-web-components/src/types/SunburstNode.ts @@ -0,0 +1,8 @@ +import { HierarchyRectangularNode } from "d3-hierarchy"; + +import type { SunburstTree } from "./SunburstTree"; + +export interface SunburstNode extends HierarchyRectangularNode { + /** The current node in the hierarchy */ + current?: SunburstNode; +} diff --git a/packages/diracx-web-components/src/types/SunburstTree.ts b/packages/diracx-web-components/src/types/SunburstTree.ts new file mode 100644 index 00000000..2ffce20b --- /dev/null +++ b/packages/diracx-web-components/src/types/SunburstTree.ts @@ -0,0 +1,8 @@ +export type SunburstTree = { + /** The name of the node */ + name: string; + /** The value of the node if it's a leaf */ + value?: number; + /** The children of the node */ + children?: SunburstTree[]; +}; diff --git a/packages/diracx-web-components/src/types/index.ts b/packages/diracx-web-components/src/types/index.ts index 880b578e..71b7ff4c 100644 --- a/packages/diracx-web-components/src/types/index.ts +++ b/packages/diracx-web-components/src/types/index.ts @@ -15,3 +15,6 @@ export * from "./EquationStatus"; export * from "./operators"; export * from "./SearchBarTokenNature"; export * from "./CategoryType"; +export * from "./SunburstTree"; +export * from "./SunburstNode"; +export * from "./JobMonitorChartType"; diff --git a/packages/diracx-web-components/stories/ChartView.stories.tsx b/packages/diracx-web-components/stories/ChartView.stories.tsx new file mode 100644 index 00000000..8133c3ca --- /dev/null +++ b/packages/diracx-web-components/stories/ChartView.stories.tsx @@ -0,0 +1,106 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { useState } from "react"; +import { ChartView } from "../src/components/shared"; +import { ThemeProvider } from "../src/contexts/ThemeProvider"; +import { Sunburst } from "../src/components"; + +// Mock data for the story +const mockTree = { + name: "", + children: [ + { + name: "Production", + value: 1500, + children: [ + { name: "Running", value: 800 }, + { name: "Completed", value: 500 }, + { name: "Failed", value: 200 }, + ], + }, + { + name: "Development", + value: 800, + children: [ + { name: "Testing", value: 400 }, + { name: "Debugging", value: 300 }, + { name: "Review", value: 100 }, + ], + }, + { + name: "Maintenance", + value: 600, + children: [ + { name: "Updates", value: 300 }, + { name: "Backups", value: 200 }, + { name: "Monitoring", value: 100 }, + ], + }, + ], +}; + +const meta = { + title: "Shared/ChartView", + component: ChartView, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + decorators: [ + (Story) => { + return ; + }, + ], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + chart:
Nothing
, + columnList: ["Column 1", "Column 2", "Column 3"], + groupColumns: ["Column 1"], + setGroupColumns: () => {}, + currentPath: [], + setCurrentPath: () => {}, + defaultColumns: ["Column 1"], + title: "Select Columns", + }, + argTypes: { + chart: { + control: { type: "select" }, + options: ["Sunburst", "None"], + mapping: { + Sunburst: , + None:
No Chart
, + }, + }, + setGroupColumns: { + control: { disable: true }, + }, + setCurrentPath: { + control: { disable: true }, + }, + }, + render: (args) => { + const [groupColumns, setGroupColumns] = useState(args.groupColumns); + const [currentPath, setCurrentPath] = useState(args.currentPath); + + return ( + + + + ); + }, +}; diff --git a/packages/diracx-web-components/stories/Dashboard.stories.tsx b/packages/diracx-web-components/stories/Dashboard.stories.tsx index 1a05227c..baf7f53d 100644 --- a/packages/diracx-web-components/stories/Dashboard.stories.tsx +++ b/packages/diracx-web-components/stories/Dashboard.stories.tsx @@ -18,6 +18,8 @@ const meta = { argTypes: { children: { control: false }, drawerWidth: { control: { type: "range", min: 200, max: 500, step: 10 } }, + logoURL: { control: { disable: true } }, + documentationURL: { control: { disable: true } }, }, decorators: [ (Story) => { diff --git a/packages/diracx-web-components/stories/DataTable.stories.tsx b/packages/diracx-web-components/stories/DataTable.stories.tsx index fca78756..b59e583c 100644 --- a/packages/diracx-web-components/stories/DataTable.stories.tsx +++ b/packages/diracx-web-components/stories/DataTable.stories.tsx @@ -47,8 +47,6 @@ const meta: Meta> = { totalRows: { control: "number" }, searchBody: { control: false }, setSearchBody: { control: false }, - error: { control: "text" }, - isValidating: { control: "boolean" }, isLoading: { control: "boolean" }, toolbarComponents: { control: false }, menuItems: { control: "object" }, @@ -74,7 +72,6 @@ export const Default: Story = { searchBody: { sort: [{ parameter: "id", direction: "asc" }] }, setSearchBody: () => {}, error: null, - isValidating: false, isLoading: false, toolbarComponents: <>, menuItems: [{ label: "Edit", onClick: () => {} }], diff --git a/packages/diracx-web-components/stories/JobMonitor.stories.tsx b/packages/diracx-web-components/stories/JobMonitor.stories.tsx index b84e860a..a741b957 100644 --- a/packages/diracx-web-components/stories/JobMonitor.stories.tsx +++ b/packages/diracx-web-components/stories/JobMonitor.stories.tsx @@ -2,7 +2,7 @@ import { StoryObj, Meta } from "@storybook/react"; import { Box } from "@mui/material"; import { ThemeProvider } from "../src/contexts/ThemeProvider"; import JobMonitor from "../src/components/JobMonitor/JobMonitor"; -import { setJobsMock, setJobHistoryMock } from "./mocks/jobDataService.mock"; +import { JobMockProvider } from "./mocks/contexts.mock"; const meta = { title: "Job Monitor/JobMonitor", @@ -105,90 +105,74 @@ const jobHistory = [ Source: "Storybook", }, ]; - -export const Loading: Story = { +export const Default: Story = { args: {}, decorators: [ (Story) => { - setJobsMock({ - jobs: null, - error: null, - isLoading: true, - }); - - setJobHistoryMock({ - jobHistory: jobHistory, - error: null, - isLoading: false, - }); - - return ; + return ( + + + + ); }, ], }; -export const Error: Story = { +export const Loading: Story = { args: {}, decorators: [ (Story) => { - setJobsMock({ - jobs: null, - error: { - message: "Error loading jobs", - name: "Error", - }, - isLoading: false, - }); - - setJobHistoryMock({ - jobHistory: jobHistory, - error: null, - isLoading: false, - }); - - return ; + return ( + + + + ); }, ], }; -export const Empty: Story = { +export const WithError: Story = { args: {}, decorators: [ (Story) => { - setJobsMock({ - jobs: [], - error: null, - isLoading: false, - }); - - setJobHistoryMock({ - jobHistory: [], - error: null, - isLoading: false, - }); - - return ; + return ( + + + + ); }, ], }; -export const Default: Story = { +export const Empty: Story = { args: {}, decorators: [ (Story) => { - setJobsMock({ - jobs: jobs, - error: null, - isLoading: false, - }); - - setJobHistoryMock({ - jobHistory: jobHistory, - error: null, - isLoading: false, - }); - - return ; + return ( + + + + ); }, ], }; diff --git a/packages/diracx-web-components/stories/LoginForm.stories.tsx b/packages/diracx-web-components/stories/LoginForm.stories.tsx index 8e88738f..4a28ab20 100644 --- a/packages/diracx-web-components/stories/LoginForm.stories.tsx +++ b/packages/diracx-web-components/stories/LoginForm.stories.tsx @@ -68,7 +68,7 @@ const meta = { }, tags: ["autodocs"], argTypes: { - logoURL: { control: "text" }, + logoURL: { control: { disable: true } }, }, decorators: [ (Story) => { diff --git a/packages/diracx-web-components/stories/Sunburst.stories.tsx b/packages/diracx-web-components/stories/Sunburst.stories.tsx new file mode 100644 index 00000000..704890c3 --- /dev/null +++ b/packages/diracx-web-components/stories/Sunburst.stories.tsx @@ -0,0 +1,166 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { useState, useEffect } from "react"; +import { Sunburst } from "../src/components/shared/Sunburst/Sunburst"; + +// Mock data for the story +const mockTree = { + name: "", + children: [ + { + name: "Production", + value: 1500, + children: [ + { name: "Running", value: 800 }, + { name: "Completed", value: 500 }, + { name: "Failed", value: 200 }, + ], + }, + { + name: "Development", + value: 800, + children: [ + { name: "Testing", value: 400 }, + { name: "Debugging", value: 300 }, + { name: "Review", value: 100 }, + ], + }, + { + name: "Maintenance", + value: 600, + children: [ + { name: "Updates", value: 300 }, + { name: "Backups", value: 200 }, + { name: "Monitoring", value: 100 }, + ], + }, + ], +}; + +function customSizeToText(size: number): string { + return `${size} owners`; +} + +function customColorScales(name: string, _size: number, depth: number) { + // Custom color logic based on depth and name + const colors = { + 0: "#FF6B6B", // Root level + 1: "#4ECDC4", // First level + 2: "#45B7D1", // Second level + }; + + // Different colors for different categories + if (name.includes("Production")) return "#FF6B6B"; + if (name.includes("Development")) return "#4ECDC4"; + if (name.includes("Maintenance")) return "#45B7D1"; + if (name.includes("Running")) return "#2ECC71"; + if (name.includes("Failed")) return "#E74C3C"; + if (name.includes("Completed")) return "#F39C12"; + + return colors[depth as keyof typeof colors] || "#95A5A6"; +} + +const meta: Meta = { + title: "Shared/Sunburst", + component: Sunburst, + parameters: { + layout: "centered", + docs: { + description: { + component: + "A D3-based sunburst chart for hierarchical data visualization.", + }, + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + tags: ["autodocs"], +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + tree: mockTree, + error: null, + isLoading: false, + hasHiddenLevels: false, + sizeToText: undefined, + colorScales: undefined, + }, + argTypes: { + tree: { + control: "select", + options: ["Default", "Empty"], + mapping: { + Default: mockTree, + Empty: { name: "", children: [] }, + }, + }, + sizeToText: { + control: "select", + options: ["Default", "Custom"], + mapping: { + Default: undefined, + Custom: customSizeToText, + }, + }, + colorScales: { + control: "select", + options: ["Default", "Custom"], + mapping: { + Default: undefined, + Custom: customColorScales, + }, + }, + error: { + control: "select", + options: ["None", "Error", "Custom Error"], + mapping: { + None: null, + Error: new Error(), + "Custom Error": new Error("Custom error message"), + }, + }, + }, + + render: (args) => { + const [currentPath, setCurrentPath] = useState([]); + const [tree, setTree] = useState(args.tree); + + useEffect(() => { + if (currentPath.length === 0) { + setTree(args.tree); + return; + } + const newChildren = + mockTree.children.filter( + (child) => currentPath[currentPath.length - 1] === child.name, + )[0]?.children || []; + const newTree = { + name: "", + children: newChildren, + }; + + setTree(newTree); + }, [currentPath, args.tree]); + + return ( + + ); + }, +}; diff --git a/packages/diracx-web-components/stories/mocks/contexts.mock.tsx b/packages/diracx-web-components/stories/mocks/contexts.mock.tsx new file mode 100644 index 00000000..7febcab7 --- /dev/null +++ b/packages/diracx-web-components/stories/mocks/contexts.mock.tsx @@ -0,0 +1,42 @@ +import React, { createContext, useContext } from "react"; +import { Job, JobHistory } from "../../src/types"; + +interface JobMockContextType { + jobs: Job[] | null; + jobHistory: JobHistory[] | null; + error: Error | null; + isLoading: boolean; +} + +interface MockProviderProps { + children: React.ReactNode; + jobs: Job[] | null; + jobHistory: JobHistory[] | null; + error?: Error | null; + isLoading?: boolean; +} + +const JobMockContext = createContext(undefined); + +export const useJobMockContext = () => { + const context = useContext(JobMockContext); + if (!context) { + console.error("useMockContext must be used within a MockProvider"); + throw new Error("useMockContext must be used within a MockProvider"); + } + return context; +}; + +export const JobMockProvider: React.FC = ({ + children, + jobs, + jobHistory, + error = null, + isLoading = false, +}): JSX.Element => { + return ( + + {children} + + ); +}; diff --git a/packages/diracx-web-components/stories/mocks/jobDataService.mock.ts b/packages/diracx-web-components/stories/mocks/jobDataService.mock.tsx similarity index 82% rename from packages/diracx-web-components/stories/mocks/jobDataService.mock.ts rename to packages/diracx-web-components/stories/mocks/jobDataService.mock.tsx index 786c3394..452f6a39 100644 --- a/packages/diracx-web-components/stories/mocks/jobDataService.mock.ts +++ b/packages/diracx-web-components/stories/mocks/jobDataService.mock.tsx @@ -1,15 +1,13 @@ /* eslint-disable */ import { Job, JobHistory, SearchBody, JobSummary } from "../../src/types"; +import { useJobMockContext } from "./contexts.mock"; + // Mock data store for jobs let mockJobsResponse: { jobs: Job[] | null; - error: Error | null; - isLoading: boolean; } = { jobs: null, - error: null, - isLoading: false, }; // Mock data store for job history @@ -24,11 +22,7 @@ let mockJobHistoryResponse: { }; // Function to set mock jobs data -export function setJobsMock(data: { - jobs: Job[] | null; - error: Error | null; - isLoading: boolean; -}) { +export function setJobsMock(data: { jobs: Job[] | null }) { mockJobsResponse = data; } @@ -41,42 +35,37 @@ export function setJobHistoryMock(data: { mockJobHistoryResponse = data; } -// Mock implementation of `useJobs` -export const useJobs = ( +export function useJobs( _diracxUrl: string | null, _accessToken: string, _searchBody: any, _page: number, _rowsPerPage: number, -) => { - if (mockJobsResponse.error) { - return { - data: undefined, - error: mockJobsResponse.error, - isLoading: mockJobsResponse.isLoading, - isValidating: false, - }; +) { + const { jobs, isLoading, error } = useJobMockContext(); + + if (isLoading) { + return { data: undefined, error: null, isLoading: true }; + } + + if (error) { + return { data: undefined, error, isLoading: false }; + } + + if (!jobs || jobs.length === 0) { + return { data: [], error: null, isLoading: false }; } - // Create headers with content-range for pagination const headers = new Headers(); - headers.append( - "content-range", - `jobs 0-${mockJobsResponse.jobs?.length || 0}/${mockJobsResponse.jobs?.length || 0}`, - ); + headers.append("content-range", `jobs 0-${jobs.length}/${jobs.length}`); return { - data: mockJobsResponse.jobs - ? { - headers, - data: mockJobsResponse.jobs, - } - : undefined, + headers, + data: jobs, error: null, - isLoading: mockJobsResponse.isLoading, - isValidating: false, + isLoading: false, }; -}; +} // Mock implementation of `getJobHistory` export const getJobHistory = async ( @@ -177,6 +166,7 @@ export async function getJobSummary( VO: "VOA", UserPriority: 100, RescheduleCounter: 0, + count: 10, }, { Status: "Completed", @@ -191,6 +181,7 @@ export async function getJobSummary( VO: "VOB", UserPriority: 200, RescheduleCounter: 1, + count: 5, }, { Status: "Failed", @@ -205,6 +196,7 @@ export async function getJobSummary( VO: "VOC", UserPriority: 300, RescheduleCounter: 2, + count: 2, }, ], }); diff --git a/packages/diracx-web-components/test/ChartView.test.tsx b/packages/diracx-web-components/test/ChartView.test.tsx new file mode 100644 index 00000000..208c6ee9 --- /dev/null +++ b/packages/diracx-web-components/test/ChartView.test.tsx @@ -0,0 +1,27 @@ +import { render, screen } from "@testing-library/react"; +import { composeStories } from "@storybook/react"; +import * as stories from "../stories/ChartView.stories"; +import "@testing-library/jest-dom"; + +// Compose the stories to get actual Storybook behavior (decorators, args, etc) +const { Default } = composeStories(stories); + +describe("ChartView", () => { + it("renders the element", () => { + render(); + expect(screen.getByText("Select Columns")).toBeInTheDocument(); + expect(screen.getByText("Level 1")).toBeInTheDocument(); + }); + + it("renders custom title", () => { + render(); + expect(screen.getByText("Custom title")).toBeInTheDocument(); + }); + + it("renders with columns", () => { + render(); + + expect(screen.getByDisplayValue("Column 1")).toBeInTheDocument(); + expect(screen.getByDisplayValue("Column 2")).toBeInTheDocument(); + }); +}); diff --git a/packages/diracx-web-components/test/JobMonitor.test.tsx b/packages/diracx-web-components/test/JobMonitor.test.tsx index 3452bd5a..75af4751 100644 --- a/packages/diracx-web-components/test/JobMonitor.test.tsx +++ b/packages/diracx-web-components/test/JobMonitor.test.tsx @@ -11,7 +11,7 @@ import * as stories from "../stories/JobMonitor.stories"; import "@testing-library/jest-dom"; // Compose Storybook stories (includes all decorators/args) -const { Default, Loading, Empty, Error } = composeStories(stories); +const { Default, Loading, Empty, WithError } = composeStories(stories); describe("JobMonitor", () => { it("renders the job monitor component", async () => { @@ -34,13 +34,11 @@ describe("JobMonitor", () => { }); it("renders error state when data fetch fails", async () => { - const { getByText } = render(); + const { getByText } = render(); // Verify error message await waitFor(() => { - expect( - getByText("An error occurred while fetching data. Reload the page."), - ).toBeInTheDocument(); + expect(getByText("Custom error message here")).toBeInTheDocument(); }); }); diff --git a/packages/diracx-web-components/test/Sunburst.test.tsx b/packages/diracx-web-components/test/Sunburst.test.tsx new file mode 100644 index 00000000..c7aa9402 --- /dev/null +++ b/packages/diracx-web-components/test/Sunburst.test.tsx @@ -0,0 +1,194 @@ +import { render, screen } from "@testing-library/react"; +import { composeStories } from "@storybook/react"; +import { Sunburst } from "../src/components/shared/Sunburst/Sunburst"; +import { SunburstTree } from "../src/types"; +import * as stories from "../stories/Sunburst.stories"; // Importing all stories to use in tests +import "@testing-library/jest-dom"; + +// Sample tree data for testing +const mockTree: SunburstTree = { + name: "Root", + value: 2900, + children: [ + { + name: "Production", + value: 1500, + children: [ + { name: "Running", value: 800 }, + { name: "Completed", value: 500 }, + { name: "Failed", value: 200 }, + ], + }, + { + name: "Development", + value: 800, + children: [ + { name: "Testing", value: 400 }, + { name: "Debugging", value: 300 }, + { name: "Review", value: 100 }, + ], + }, + { + name: "Maintenance", + value: 600, + children: [ + { name: "Updates", value: 300 }, + { name: "Backups", value: 200 }, + { name: "Monitoring", value: 100 }, + ], + }, + ], +}; + +// Sample tree data with small segments that should be grouped into "Others" + +// Empty tree for testing +const emptyTree: SunburstTree = { + name: "Empty", + value: 0, + children: [], +}; + +// Custom size to text function for testing +const customSizeToText = (size: number, total?: number) => { + if (total) { + return `${size} of ${total} (${Math.round((size / total) * 100)}%)`; + } + return `${size} items`; +}; + +describe("Sunburst Component", () => { + const { Default } = composeStories(stories); + + describe("Rendering States", () => { + test("renders loading skeleton when isLoading is true", () => { + render(); + const loadingSkeleton = screen.getByTestId("loading-skeleton"); + expect(loadingSkeleton).toBeInTheDocument(); + }); + + test("renders error message when error is provided", () => { + const errorMessage = "Failed to load data"; + render( + , + ); + const errorAlert = screen.getByText(errorMessage); + expect(errorAlert).toBeInTheDocument(); + }); + + test("renders default error message when error object has no message", () => { + render( + , + ); + const defaultErrorMessage = screen.getByText( + "An error occurred while loading the data.", + ); + expect(defaultErrorMessage).toBeInTheDocument(); + }); + + test("renders the sunburst chart when data is provided and not loading or error", () => { + render(); + // Since D3 is mocked, we check that SVG is rendered + const svg = document.querySelector("svg"); + expect(svg).toBeInTheDocument(); + }); + + test("handles empty tree data", () => { + render(); + const svg = document.querySelector("svg"); + expect(svg).toBeInTheDocument(); + // We don't expect any errors to be thrown + }); + }); + + describe("Custom Rendering", () => { + test("uses custom size to text function when provided", () => { + render( + , + ); + + // Since D3 is mocked, we can't directly test the text content + // but we can verify the component rendered without errors + const svg = document.querySelector("svg"); + expect(svg).toBeInTheDocument(); + }); + }); + + describe("Performance", () => { + test("handles large datasets without crashing", () => { + // Create a large dataset + const largeTree: SunburstTree = { + name: "Root", + value: 0, + children: [], + }; + + // Add 5000 children + for (let i = 0; i < 5000; i++) { + largeTree.children!.push({ + name: `Node ${i}`, + value: i + 1, + }); + } + + // This should render without crashing + render(); + const svg = document.querySelector("svg"); + expect(svg).toBeInTheDocument(); + }); + }); + + describe("Component Lifecycle", () => { + test("component should update when current path changes", () => { + const setCurrentPathMock = jest.fn(); + const { rerender } = render( + , + ); + + // Rerender with different current path + rerender( + , + ); + + // In a real test environment, we would check if the visualization has updated + const svg = document.querySelector("svg"); + expect(svg).toBeInTheDocument(); + }); + }); + + // The previous tests cover the main functionalities of the Sunburst component. + // Here we just ensure that the story renders correctly. + describe("Storybook Integration", () => { + test("renders the Default story correctly", () => { + render(); + expect(screen.getByText("Top")).toBeInTheDocument(); + expect(screen.getByTestId("sunburst-chart")).toBeInTheDocument(); + }); + + test("renders while loading", () => { + render(); + expect(screen.getByTestId("loading-skeleton")).toBeInTheDocument(); + }); + }); +}); diff --git a/packages/diracx-web/.gitignore b/packages/diracx-web/.gitignore index 145d1eae..5642b75c 100644 --- a/packages/diracx-web/.gitignore +++ b/packages/diracx-web/.gitignore @@ -7,6 +7,7 @@ # testing /coverage +/cypress # next.js /.next/ diff --git a/packages/diracx-web/test/e2e/jobMonitor.cy.ts b/packages/diracx-web/test/e2e/jobMonitor.cy.ts index fba480c9..889cd132 100644 --- a/packages/diracx-web/test/e2e/jobMonitor.cy.ts +++ b/packages/diracx-web/test/e2e/jobMonitor.cy.ts @@ -200,6 +200,8 @@ describe("Job Monitor", () => { } }); + cy.wait(1000); // Wait for the table to update + let firstValue: number; let lastValue: number; @@ -213,7 +215,7 @@ describe("Job Monitor", () => { firstValue = parseInt(text.trim(), 10); }); - // Scroll and get the last visible row value (e.g. 6) + // Scroll and get the last visible row value cy.get('[data-testid="virtuoso-scroller"]') .wait(100) .scrollTo("bottom", { ensureScrollable: false }); @@ -256,9 +258,9 @@ describe("Job Monitor", () => { }); it("should delete jobs", () => { - cy.get("[data-index=1]").as("jobItem1"); - cy.get("[data-index=2]").as("jobItem2"); - cy.get("[data-index=3]").as("jobItem3"); + cy.get("[data-index=0]").as("jobItem1"); + cy.get("[data-index=1]").as("jobItem2"); + cy.get("[data-index=2]").as("jobItem3"); cy.get("@jobItem1").click({ force: true }); cy.get("@jobItem2").click({ force: true }); cy.get("@jobItem3").click({ force: true }); @@ -272,20 +274,42 @@ describe("Job Monitor", () => { cy.get("@jobItem3").find("td").eq(2).should("contain", "Deleted"); }); - // FIXME - // This test can't pass because the reschedule functionality is not completly working yet + // ### FIXME: The reschedule functionality is not working as expected ### + // The test below would be decommented once the reschedule functionality is fixed in diracx + // it("should reschedule jobs", () => { - // cy.get("[data-index=1]").click({ force: true }); - // cy.get("[data-index=2]").click({ force: true }); - // cy.get("[data-index=3]").click({ force: true }); + // cy.wait(1000); // Wait for the table to load + + // cy.get("[data-testid=search-field]").type("Reschedule Counter{enter}!={enter}3{enter}"); + + // cy.wait(1000); // Wait for the search to complete + + // // Create aliases for the job items + // cy.get("[data-index=0]").as("jobItem1"); + // cy.get("[data-index=1]").as("jobItem2"); + // cy.get("[data-index=2]").as("jobItem3"); + // // First, kill the jobs to ensure they can be rescheduled + // cy.get("@jobItem1").click({ force: true }); + // cy.get("@jobItem2").click({ force: true }); + // cy.get("@jobItem3").click({ force: true }); + + // cy.get('[data-testid="ClearIcon"] > path').click(); + + // // Then, select the jobs to reschedule + // cy.get("@jobItem1").click({ force: true }); + // cy.get("@jobItem2").click({ force: true }); + // cy.get("@jobItem3").click({ force: true }); + + // cy.get('[data-testid="ReplayIcon"] > path').click({ force: true }); + // cy.get('[aria-label="Reschedule"]').click({ force: true }); // cy.get('[data-testid="ReplayIcon"] > path').click({ force: true }); // cy.get('[aria-label="Reschedule"]').click({ force: true }); // // Make sure the job status is "Received" + // cy.get("[data-index=0]").find("td").eq(2).should("contain", "Received"); // cy.get("[data-index=1]").find("td").eq(2).should("contain", "Received"); // cy.get("[data-index=2]").find("td").eq(2).should("contain", "Received"); - // cy.get("[data-index=3]").find("td").eq(2).should("contain", "Received"); // }); /** Column interactions */ @@ -301,17 +325,23 @@ describe("Job Monitor", () => { } }); + cy.wait(1000); // Wait for the table to load + // Click on the visibility icon cy.get('[data-testid="VisibilityIcon"] > path').click(); cy.get('[data-testid="column-visibility-popover"]').should("be.visible"); // Hide the "Status" column and Show the "VO" column - cy.get('[data-testid="column-visibility-popover"]').within(() => { - cy.contains("VO").parent().find('input[type="checkbox"]').click(); - }); - cy.get('[data-testid="column-visibility-popover"]').within(() => { - cy.contains("Status").parent().find('input[type="checkbox"]').click(); - }); + cy.get('[data-testid="column-visibility-popover"]') + .contains("Status") + .parent() + .find('input[type="checkbox"]') + .click(); + cy.get('[data-testid="column-visibility-popover"]') + .contains("VO") + .parent() + .find('input[type="checkbox"]') + .click(); // Close the popover by clicking outside cy.get("body").click(0, 0); @@ -405,7 +435,7 @@ describe("Job Monitor", () => { cy.get("[data-testid=search-field]").type("ID{enter}={enter}1{enter}"); - cy.get('[role="group"]').find("button").should("have.length", 3); + cy.get('[role="group"]').find("button").should("have.length", 5); }); it("should handle filter editing", () => { @@ -422,13 +452,11 @@ describe("Job Monitor", () => { cy.get("[data-testid=search-field]").type("Name{enter}={enter}test{enter}"); - cy.get("[data-testid=search-field]").type("ID{enter}={enter}1{enter}"); - - cy.get('[role="group"]').find("button").should("have.length", 6); + cy.get('[role="group"]').find("button").should("have.length", 5); cy.get('[data-testid="DeleteIcon"]').click(); - cy.get('[role="group"]').should("not.exist"); + cy.get('[role="group"]').find("button").should("have.length", 2); }); it("should handle filter apply and persist", () => { @@ -447,7 +475,7 @@ describe("Job Monitor", () => { `ID{enter}={enter}${jobID}{enter}`, ); }); - cy.get('[role="group"]').find("button").should("have.length", 3); + cy.get('[role="group"]').find("button").should("have.length", 5); cy.get('[role="group"]').find("button").contains("ID").should("exist"); // Wait for the filter to apply @@ -459,20 +487,32 @@ describe("Job Monitor", () => { it("should handle filter apply and save filters in dashboard", () => { cy.get("table").should("be.visible"); - cy.get("[data-testid=search-field]").type("ID{enter}={enter}5{enter}"); + let jobID: string; + cy.get("table tbody tr") + .first() + .find("td") + .eq(1) + .invoke("text") + .then((text) => { + jobID = text.trim(); + + cy.get("[data-testid=search-field]").type( + `ID{enter}={enter}${jobID}{enter}`, + ); + }); // Wait for the filter to apply cy.wait(1000); - cy.get('[role="group"]').find("button").should("have.length", 3); + cy.get('[role="group"]').find("button").should("have.length", 5); cy.get(".MuiButtonBase-root").contains("Job Monitor 2").click(); - cy.get('[role="group"]').should("not.exist"); + cy.get('[role="group"]').find("button").should("have.length", 2); cy.get(".MuiButtonBase-root").contains("Job Monitor").click(); - cy.get('[role="group"]').find("button").should("have.length", 3); + cy.get('[role="group"]').find("button").should("have.length", 5); }); it("should control the in the last operator utilization", () => { @@ -484,8 +524,21 @@ describe("Job Monitor", () => { // Wait for the filter to apply cy.wait(1000); - cy.get('[role="group"]').find("button").should("have.length", 3); + cy.get('[role="group"]').find("button").should("have.length", 5); cy.get("table").should("be.visible"); }); + + /** Sunburst */ + + it("should render the sunburst chart", () => { + // Click on the sunburst button + cy.get('[role="group"]').last().click(); + + // Make sure the sunburst chart is visible + cy.get('[data-testid="sunburst-chart"]').should("be.visible"); + + // Make sure the column selector is visible + cy.get('[data-testid="column-selector"]').should("be.visible"); + }); }); diff --git a/packages/extensions/src/gubbins/components/OwnerMonitor/OwnerMonitor.tsx b/packages/extensions/src/gubbins/components/OwnerMonitor/OwnerMonitor.tsx index ef49e8e0..c7171b6e 100644 --- a/packages/extensions/src/gubbins/components/OwnerMonitor/OwnerMonitor.tsx +++ b/packages/extensions/src/gubbins/components/OwnerMonitor/OwnerMonitor.tsx @@ -142,7 +142,6 @@ export default function OwnerMonitor() { setSearchBody={() => {}} error={null} isLoading={isLoading} - isValidating={isLoading} toolbarComponents={<>} menuItems={[]} />