diff --git a/tools/debug-ui/.eslintrc.yml b/tools/debug-ui/.eslintrc.yml new file mode 100644 index 00000000000..f7bfb139242 --- /dev/null +++ b/tools/debug-ui/.eslintrc.yml @@ -0,0 +1,32 @@ +env: + browser: true + es2021: true +extends: + - eslint:recommended + - plugin:react/recommended + - plugin:react/jsx-runtime + - plugin:@typescript-eslint/recommended + - plugin:import/recommended + - plugin:import/typescript + - prettier +overrides: [] +parser: '@typescript-eslint/parser' +parserOptions: + ecmaVersion: latest + sourceType: module +plugins: + - react + - '@typescript-eslint' +rules: + import/order: + - error + "@typescript-eslint/no-non-null-assertion": + - off + "@typescript-eslint/no-unused-vars": + - error +settings: + import/resolver: + typescript: true + node: true + react: + version: detect \ No newline at end of file diff --git a/tools/debug-ui/.prettierrc.json b/tools/debug-ui/.prettierrc.json new file mode 100644 index 00000000000..908a5e75674 --- /dev/null +++ b/tools/debug-ui/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "printWidth": 100, + "tabWidth": 4, + "singleQuote": true, + "bracketSameLine": true +} \ No newline at end of file diff --git a/tools/debug-ui/README.md b/tools/debug-ui/README.md index a27068597d3..1965bef7b4e 100644 --- a/tools/debug-ui/README.md +++ b/tools/debug-ui/README.md @@ -39,3 +39,9 @@ same keys are only fetched once. It's also helpful to understand at a high level how the react-router library works; this is used to support deep-linking in the URL (e.g. `/127.0.0.1/cluster` leads to the cluster page), allowing the UI to be served as a single application. + +### Linting & Formatting +The project is configured to use ESLint (error-checking) and Prettier (consistent formatting). + +Run `npm run lint` to check for linting & formatting errors, and `npm run fix` to fix those that +can be automatically fixed. diff --git a/tools/debug-ui/package-lock.json b/tools/debug-ui/package-lock.json index c87963d9336..483fc313c0a 100644 --- a/tools/debug-ui/package-lock.json +++ b/tools/debug-ui/package-lock.json @@ -18,10 +18,20 @@ "react-router-dom": "^6.4.4", "react-scripts": "^5.0.1", "react-tooltip": "^5.4.0", - "react-xarrows": "^2.0.2", - "typescript": "^4.9.3" + "react-xarrows": "^2.0.2" }, "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.52.0", + "@typescript-eslint/parser": "^5.52.0", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-import-resolver-typescript": "^3.5.3", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^15.6.1", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.32.2", + "prettier": "2.8.4", + "typescript": "^4.9.5", "typescript-plugin-css-modules": "^4.1.1" } }, @@ -2271,14 +2281,14 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.4.0", - "globals": "^13.15.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -2314,9 +2324,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dependencies": { "type-fest": "^0.20.2" }, @@ -2368,9 +2378,9 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -3186,6 +3196,26 @@ "node": ">= 8" } }, + "node_modules/@pkgr/utils": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz", + "integrity": "sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "is-glob": "^4.0.3", + "open": "^8.4.0", + "picocolors": "^1.0.0", + "tiny-glob": "^0.2.9", + "tslib": "^2.4.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", @@ -3981,14 +4011,15 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz", - "integrity": "sha512-cOizjPlKEh0bXdFrBLTrI/J6B/QMlhwE9auOov53tgB+qMukH6/h8YAK/qw+QJGct/PTbdh2lytGyipxCcEtAw==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz", + "integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==", "dependencies": { - "@typescript-eslint/scope-manager": "5.45.1", - "@typescript-eslint/type-utils": "5.45.1", - "@typescript-eslint/utils": "5.45.1", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/type-utils": "5.52.0", + "@typescript-eslint/utils": "5.52.0", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "regexpp": "^3.2.0", @@ -4012,6 +4043,101 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz", + "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==", + "dependencies": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz", + "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz", + "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==", + "dependencies": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz", + "integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/typescript-estree": "5.52.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz", + "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==", + "dependencies": { + "@typescript-eslint/types": "5.52.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4028,6 +4154,26 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4066,13 +4212,13 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.1.tgz", - "integrity": "sha512-JQ3Ep8bEOXu16q0ztsatp/iQfDCtvap7sp/DKo7DWltUquj5AfCOpX2zSzJ8YkAVnrQNqQ5R62PBz2UtrfmCkA==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz", + "integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==", "dependencies": { - "@typescript-eslint/scope-manager": "5.45.1", - "@typescript-eslint/types": "5.45.1", - "@typescript-eslint/typescript-estree": "5.45.1", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/typescript-estree": "5.52.0", "debug": "^4.3.4" }, "engines": { @@ -4091,6 +4237,76 @@ } } }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz", + "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==", + "dependencies": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz", + "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz", + "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==", + "dependencies": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz", + "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==", + "dependencies": { + "@typescript-eslint/types": "5.52.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/parser/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4112,6 +4328,20 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/@typescript-eslint/parser/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "5.45.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.1.tgz", @@ -4129,12 +4359,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz", - "integrity": "sha512-aosxFa+0CoYgYEl3aptLe1svP910DJq68nwEJzyQcrtRhC4BN0tJAvZGAe+D0tzjJmFXe+h4leSsiZhwBa2vrA==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz", + "integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==", "dependencies": { - "@typescript-eslint/typescript-estree": "5.45.1", - "@typescript-eslint/utils": "5.45.1", + "@typescript-eslint/typescript-estree": "5.52.0", + "@typescript-eslint/utils": "5.52.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -4154,6 +4384,101 @@ } } }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz", + "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==", + "dependencies": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz", + "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz", + "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==", + "dependencies": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz", + "integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/typescript-estree": "5.52.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz", + "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==", + "dependencies": { + "@typescript-eslint/types": "5.52.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/type-utils/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4170,11 +4495,45 @@ } } }, + "node_modules/@typescript-eslint/type-utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/@typescript-eslint/type-utils/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/@typescript-eslint/type-utils/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/types": { "version": "5.45.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.1.tgz", @@ -5520,6 +5879,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/builtins/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -6974,12 +7357,12 @@ } }, "node_modules/eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", - "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", "dependencies": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -6998,7 +7381,7 @@ "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.15.0", + "globals": "^13.19.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", @@ -7028,6 +7411,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", + "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-config-react-app": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", @@ -7056,12 +7451,92 @@ } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", "dependencies": { "debug": "^3.2.7", - "resolve": "^1.20.0" + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz", + "integrity": "sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "enhanced-resolve": "^5.10.0", + "get-tsconfig": "^4.2.0", + "globby": "^13.1.2", + "is-core-module": "^2.10.0", + "is-glob": "^4.0.3", + "synckit": "^0.8.4" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" + } + }, + "node_modules/eslint-import-resolver-typescript/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-typescript/node_modules/globby": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-import-resolver-typescript/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/eslint-import-resolver-typescript/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint-module-utils": { @@ -7080,6 +7555,49 @@ } } }, + "node_modules/eslint-plugin-es": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/eslint-plugin-flowtype": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", @@ -7098,22 +7616,24 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", "has": "^1.0.3", - "is-core-module": "^2.8.1", + "is-core-module": "^2.11.0", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", "tsconfig-paths": "^3.14.1" }, "engines": { @@ -7123,14 +7643,6 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, "node_modules/eslint-plugin-import/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -7153,10 +7665,13 @@ "json5": "lib/cli.js" } }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } }, "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { "version": "3.14.1", @@ -7226,10 +7741,62 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-n": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.6.1.tgz", + "integrity": "sha512-R9xw9OtCRxxaxaszTQmQAlPgM+RdGjaL1akWuY/Fv9fRAi8Wj4CUKc6iYVG8QNRjRuo8/BqVYIpfqberJUEacA==", + "dev": true, + "dependencies": { + "builtins": "^5.0.1", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", + "ignore": "^5.1.1", + "is-core-module": "^2.11.0", + "minimatch": "^3.1.2", + "resolve": "^1.22.1", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, "node_modules/eslint-plugin-react": { - "version": "7.31.11", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz", - "integrity": "sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw==", + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", "dependencies": { "array-includes": "^3.1.6", "array.prototype.flatmap": "^1.3.1", @@ -7243,7 +7810,7 @@ "object.hasown": "^1.1.2", "object.values": "^1.1.6", "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", + "resolve": "^2.0.0-next.4", "semver": "^6.3.0", "string.prototype.matchall": "^4.0.8" }, @@ -7556,9 +8123,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dependencies": { "type-fest": "^0.20.2" }, @@ -8462,6 +9029,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-tsconfig": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.4.0.tgz", + "integrity": "sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -8540,6 +9116,12 @@ "node": ">=4" } }, + "node_modules/globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", + "dev": true + }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -8559,6 +9141,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -14235,6 +14823,21 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", + "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -16190,6 +16793,22 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/tailwindcss": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", @@ -16393,6 +17012,16 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, + "node_modules/tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "dev": true, + "dependencies": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -16490,9 +17119,9 @@ } }, "node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -16564,9 +17193,9 @@ } }, "node_modules/typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -19204,14 +19833,14 @@ "requires": {} }, "@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.4.0", - "globals": "^13.15.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -19233,9 +19862,9 @@ } }, "globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "requires": { "type-fest": "^0.20.2" } @@ -19274,9 +19903,9 @@ } }, "@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "requires": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -19884,6 +20513,20 @@ "fastq": "^1.6.0" } }, + "@pkgr/utils": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz", + "integrity": "sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "is-glob": "^4.0.3", + "open": "^8.4.0", + "picocolors": "^1.0.0", + "tiny-glob": "^0.2.9", + "tslib": "^2.4.0" + } + }, "@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", @@ -20480,14 +21123,15 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "@typescript-eslint/eslint-plugin": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz", - "integrity": "sha512-cOizjPlKEh0bXdFrBLTrI/J6B/QMlhwE9auOov53tgB+qMukH6/h8YAK/qw+QJGct/PTbdh2lytGyipxCcEtAw==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz", + "integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==", "requires": { - "@typescript-eslint/scope-manager": "5.45.1", - "@typescript-eslint/type-utils": "5.45.1", - "@typescript-eslint/utils": "5.45.1", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/type-utils": "5.52.0", + "@typescript-eslint/utils": "5.52.0", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "regexpp": "^3.2.0", @@ -20495,6 +21139,58 @@ "tsutils": "^3.21.0" }, "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz", + "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==", + "requires": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0" + } + }, + "@typescript-eslint/types": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz", + "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==" + }, + "@typescript-eslint/typescript-estree": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz", + "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==", + "requires": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz", + "integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==", + "requires": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/typescript-estree": "5.52.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz", + "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==", + "requires": { + "@typescript-eslint/types": "5.52.0", + "eslint-visitor-keys": "^3.3.0" + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -20503,6 +21199,20 @@ "ms": "2.1.2" } }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -20527,16 +21237,53 @@ } }, "@typescript-eslint/parser": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.1.tgz", - "integrity": "sha512-JQ3Ep8bEOXu16q0ztsatp/iQfDCtvap7sp/DKo7DWltUquj5AfCOpX2zSzJ8YkAVnrQNqQ5R62PBz2UtrfmCkA==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz", + "integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==", "requires": { - "@typescript-eslint/scope-manager": "5.45.1", - "@typescript-eslint/types": "5.45.1", - "@typescript-eslint/typescript-estree": "5.45.1", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/typescript-estree": "5.52.0", "debug": "^4.3.4" }, "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz", + "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==", + "requires": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0" + } + }, + "@typescript-eslint/types": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz", + "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==" + }, + "@typescript-eslint/typescript-estree": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz", + "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==", + "requires": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz", + "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==", + "requires": { + "@typescript-eslint/types": "5.52.0", + "eslint-visitor-keys": "^3.3.0" + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -20549,6 +21296,14 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } } } }, @@ -20562,16 +21317,68 @@ } }, "@typescript-eslint/type-utils": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz", - "integrity": "sha512-aosxFa+0CoYgYEl3aptLe1svP910DJq68nwEJzyQcrtRhC4BN0tJAvZGAe+D0tzjJmFXe+h4leSsiZhwBa2vrA==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz", + "integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==", "requires": { - "@typescript-eslint/typescript-estree": "5.45.1", - "@typescript-eslint/utils": "5.45.1", + "@typescript-eslint/typescript-estree": "5.52.0", + "@typescript-eslint/utils": "5.52.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz", + "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==", + "requires": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0" + } + }, + "@typescript-eslint/types": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz", + "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==" + }, + "@typescript-eslint/typescript-estree": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz", + "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==", + "requires": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz", + "integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==", + "requires": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/typescript-estree": "5.52.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz", + "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==", + "requires": { + "@typescript-eslint/types": "5.52.0", + "eslint-visitor-keys": "^3.3.0" + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -20580,10 +21387,32 @@ "ms": "2.1.2" } }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } } } }, @@ -21595,6 +22424,26 @@ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==" }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "requires": { + "semver": "^7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -22659,12 +23508,12 @@ } }, "eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", - "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", "requires": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -22683,7 +23532,7 @@ "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.15.0", + "globals": "^13.19.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", @@ -22761,9 +23610,9 @@ } }, "globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "requires": { "type-fest": "^0.20.2" } @@ -22801,6 +23650,13 @@ } } }, + "eslint-config-prettier": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", + "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", + "dev": true, + "requires": {} + }, "eslint-config-react-app": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", @@ -22823,12 +23679,64 @@ } }, "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", "requires": { "debug": "^3.2.7", - "resolve": "^1.20.0" + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + } + }, + "eslint-import-resolver-typescript": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz", + "integrity": "sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "enhanced-resolve": "^5.10.0", + "get-tsconfig": "^4.2.0", + "globby": "^13.1.2", + "is-core-module": "^2.10.0", + "is-glob": "^4.0.3", + "synckit": "^0.8.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globby": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + } } }, "eslint-module-utils": { @@ -22839,6 +23747,33 @@ "debug": "^3.2.7" } }, + "eslint-plugin-es": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, "eslint-plugin-flowtype": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", @@ -22849,33 +23784,27 @@ } }, "eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", "requires": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", "has": "^1.0.3", - "is-core-module": "^2.8.1", + "is-core-module": "^2.11.0", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", "tsconfig-paths": "^3.14.1" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -22892,10 +23821,10 @@ "minimist": "^1.2.0" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "tsconfig-paths": { "version": "3.14.1", @@ -22945,10 +23874,44 @@ } } }, + "eslint-plugin-n": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.6.1.tgz", + "integrity": "sha512-R9xw9OtCRxxaxaszTQmQAlPgM+RdGjaL1akWuY/Fv9fRAi8Wj4CUKc6iYVG8QNRjRuo8/BqVYIpfqberJUEacA==", + "dev": true, + "requires": { + "builtins": "^5.0.1", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", + "ignore": "^5.1.1", + "is-core-module": "^2.11.0", + "minimatch": "^3.1.2", + "resolve": "^1.22.1", + "semver": "^7.3.8" + }, + "dependencies": { + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "requires": {} + }, "eslint-plugin-react": { - "version": "7.31.11", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz", - "integrity": "sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw==", + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", "requires": { "array-includes": "^3.1.6", "array.prototype.flatmap": "^1.3.1", @@ -22962,7 +23925,7 @@ "object.hasown": "^1.1.2", "object.values": "^1.1.6", "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", + "resolve": "^2.0.0-next.4", "semver": "^6.3.0", "string.prototype.matchall": "^4.0.8" }, @@ -23716,6 +24679,12 @@ "get-intrinsic": "^1.1.1" } }, + "get-tsconfig": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.4.0.tgz", + "integrity": "sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==", + "dev": true + }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -23775,6 +24744,12 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, + "globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", + "dev": true + }, "globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -23788,6 +24763,12 @@ "slash": "^3.0.0" } }, + "globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -27840,6 +28821,12 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, + "prettier": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", + "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "dev": true + }, "pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -29267,6 +30254,16 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, + "synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + } + }, "tailwindcss": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", @@ -29409,6 +30406,16 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, + "tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "dev": true, + "requires": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -29483,9 +30490,9 @@ } }, "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "tsutils": { "version": "3.21.0", @@ -29538,9 +30545,9 @@ } }, "typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==" + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" }, "typescript-plugin-css-modules": { "version": "4.1.1", diff --git a/tools/debug-ui/package.json b/tools/debug-ui/package.json index 06ea28c7540..7c690349bfc 100644 --- a/tools/debug-ui/package.json +++ b/tools/debug-ui/package.json @@ -13,18 +13,14 @@ "react-router-dom": "^6.4.4", "react-scripts": "^5.0.1", "react-tooltip": "^5.4.0", - "react-xarrows": "^2.0.2", - "typescript": "^4.9.3" + "react-xarrows": "^2.0.2" }, "scripts": { - "start": "react-scripts start", + "start": "DISABLE_ESLINT_PLUGIN=true react-scripts start", "build": "react-scripts build", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": [ - "react-app" - ] + "eject": "react-scripts eject", + "lint": "eslint . && prettier --check src", + "fix": "eslint . --fix && prettier --write src" }, "browserslist": { "production": [ @@ -39,6 +35,17 @@ ] }, "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.52.0", + "@typescript-eslint/parser": "^5.52.0", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-import-resolver-typescript": "^3.5.3", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^15.6.1", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.32.2", + "prettier": "2.8.4", + "typescript": "^4.9.5", "typescript-plugin-css-modules": "^4.1.1" } } diff --git a/tools/debug-ui/src/App.scss b/tools/debug-ui/src/App.scss index 4eb2b53f285..14af5c9bc9a 100644 --- a/tools/debug-ui/src/App.scss +++ b/tools/debug-ui/src/App.scss @@ -23,4 +23,4 @@ color: white; } } -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/App.tsx b/tools/debug-ui/src/App.tsx index 4d66b19aace..0e20db55399 100644 --- a/tools/debug-ui/src/App.tsx +++ b/tools/debug-ui/src/App.tsx @@ -1,6 +1,6 @@ -import { Navigate, Route, Routes, useParams } from 'react-router'; -import { NavLink } from 'react-router-dom'; import './App.scss'; +import { NavLink } from 'react-router-dom'; +import { Navigate, Route, Routes, useParams } from 'react-router'; import { ClusterView } from './ClusterView'; import { EpochInfoView } from './EpochInfoView'; import { HeaderBar } from './HeaderBar'; @@ -8,41 +8,54 @@ import { LatestBlocksView } from './LatestBlocksView'; import { NetworkInfoView } from './NetworkInfoView'; function useNodeAddr(): string { - const params = useParams<{ addr: string }>(); - const addr = params.addr || '127.0.0.1'; - return addr.includes(':') ? addr : addr + ':3030'; + const params = useParams<{ addr: string }>(); + const addr = params.addr || '127.0.0.1'; + return addr.includes(':') ? addr : addr + ':3030'; } export const App = () => { - const addr = useNodeAddr(); + const addr = useNodeAddr(); - - return ( -
- -
- Latest Blocks - Network Info - Epoch Info - Chain & Chunk Info - Sync Info - Validator Info - Cluster View -
- - } /> - } /> - } /> - } /> - TODO
} /> - TODO} /> - TODO} /> - } /> - - - ); -} + return ( +
+ +
+ + Latest Blocks + + + Network Info + + + Epoch Info + + + Chain & Chunk Info + + + Sync Info + + + Validator Info + + + Cluster View + +
+ + } /> + } /> + } /> + } /> + TODO
} /> + TODO} /> + TODO} /> + } /> + + + ); +}; function navLinkClassName({ isActive }: { isActive: boolean }) { - return isActive ? 'nav-link active' : 'nav-link'; -} \ No newline at end of file + return isActive ? 'nav-link active' : 'nav-link'; +} diff --git a/tools/debug-ui/src/ClusterNodeView.scss b/tools/debug-ui/src/ClusterNodeView.scss index 9d67a2e39a6..a004c11a0b0 100644 --- a/tools/debug-ui/src/ClusterNodeView.scss +++ b/tools/debug-ui/src/ClusterNodeView.scss @@ -46,4 +46,4 @@ width: 200px; margin: 0 10px; } -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/ClusterNodeView.tsx b/tools/debug-ui/src/ClusterNodeView.tsx index f84a1195a37..194ae5df7b7 100644 --- a/tools/debug-ui/src/ClusterNodeView.tsx +++ b/tools/debug-ui/src/ClusterNodeView.tsx @@ -1,6 +1,13 @@ import { useEffect } from 'react'; import { useQuery } from 'react-query'; -import { fetchBasicStatus, fetchFullStatus, fetchSyncStatus, fetchTrackedShards, SyncStatusResponse, TrackedShardsResponse } from './api'; +import { + SyncStatusResponse, + TrackedShardsResponse, + fetchBasicStatus, + fetchFullStatus, + fetchSyncStatus, + fetchTrackedShards, +} from './api'; import './ClusterNodeView.scss'; interface Props { @@ -10,7 +17,12 @@ interface Props { newNodesDiscovered: (nodes: string[]) => void; } -export const ClusterNodeView = ({ addr, highestHeight, basicStatusChanged, newNodesDiscovered }: Props) => { +export const ClusterNodeView = ({ + addr, + highestHeight, + basicStatusChanged, + newNodesDiscovered, +}: Props) => { const status = useQuery(['status', addr], () => fetchBasicStatus(addr)); const fullStatus = useQuery(['fullStatus', addr], () => fetchFullStatus(addr)); const syncStatus = useQuery(['syncStatus', addr], () => fetchSyncStatus(addr)); @@ -18,9 +30,11 @@ export const ClusterNodeView = ({ addr, highestHeight, basicStatusChanged, newNo useEffect(() => { if (status.data) { - basicStatusChanged(addr, + basicStatusChanged( + addr, status.data.validator_account_id, - status.data.sync_info.latest_block_height); + status.data.sync_info.latest_block_height + ); } }, [addr, status.data, basicStatusChanged]); @@ -46,29 +60,38 @@ export const ClusterNodeView = ({ addr, highestHeight, basicStatusChanged, newNo }, [addr, fullStatus.data, newNodesDiscovered]); const anyError = status.error || fullStatus.error || syncStatus.error || trackedShards.error; - const anyLoading = status.isLoading || fullStatus.isLoading || syncStatus.isLoading || trackedShards.isLoading; + const anyLoading = + status.isLoading || fullStatus.isLoading || syncStatus.isLoading || trackedShards.isLoading; return ( -
-
- +
+ - {status.data &&
{status.data.sync_info.latest_block_height}
} - {status.data && status.data.validator_account_id && -
{status.data.validator_account_id}
} - {syncStatus.data &&
{syncStatusToText(syncStatus.data)}
} - {trackedShards.data && -
{trackedShardsToText(trackedShards.data)}
} - {!!anyError &&
{'' + anyError}
} - {anyLoading && !anyError &&
Loading...
} + {status.data && ( +
+ {status.data.sync_info.latest_block_height} +
+ )} + {status.data && status.data.validator_account_id && ( +
{status.data.validator_account_id}
+ )} + {syncStatus.data && ( +
{syncStatusToText(syncStatus.data)}
+ )} + {trackedShards.data &&
{trackedShardsToText(trackedShards.data)}
} + {!!anyError &&
{'' + anyError}
} + {anyLoading && !anyError &&
Loading...
}
); -} +}; function syncStatusToText(syncStatus: SyncStatusResponse): string { const status = syncStatus.status_response.SyncStatus; @@ -76,22 +99,22 @@ function syncStatusToText(syncStatus: SyncStatusResponse): string { return 'No sync status??'; } if (status === 'AwaitingPeers') { - return "Awaiting peers"; + return 'Awaiting peers'; } if (status === 'NoSync') { return ''; } if (status === 'StateSyncDone') { - return "State sync done"; + return 'State sync done'; } - if ("EpochSync" in status) { - return "Epoch sync"; + if ('EpochSync' in status) { + return 'Epoch sync'; } - if ("HeaderSync" in status) { - return "Header sync"; + if ('HeaderSync' in status) { + return 'Header sync'; } - if ("StateSync" in status) { - return "State sync"; + if ('StateSync' in status) { + return 'State sync'; } return `Body sync ${status.BodySync.start_height} -> ${status.BodySync.highest_height}`; } @@ -107,8 +130,9 @@ function booleanArrayToIndexList(array: boolean[]): number[] { } function trackedShardsToText(trackedShards: TrackedShardsResponse): string { - const { shards_tracked_this_epoch, shards_tracked_next_epoch } = trackedShards.status_response.TrackedShards; + const { shards_tracked_this_epoch, shards_tracked_next_epoch } = + trackedShards.status_response.TrackedShards; const thisShards = booleanArrayToIndexList(shards_tracked_this_epoch).join(', '); const nextShards = booleanArrayToIndexList(shards_tracked_next_epoch).join(', '); return `[${thisShards}] next: [${nextShards}]`; -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/ClusterView.scss b/tools/debug-ui/src/ClusterView.scss index 5a2502ed18d..13f42efeb07 100644 --- a/tools/debug-ui/src/ClusterView.scss +++ b/tools/debug-ui/src/ClusterView.scss @@ -5,4 +5,4 @@ font-weight: bold; margin-bottom: 10px; } -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/ClusterView.tsx b/tools/debug-ui/src/ClusterView.tsx index 62090994c31..2a292ef935e 100644 --- a/tools/debug-ui/src/ClusterView.tsx +++ b/tools/debug-ui/src/ClusterView.tsx @@ -26,7 +26,9 @@ export class DiscoveryNodes { sorted(): string[] { return Array.from(this.nodes).sort((a, b) => { - return sortingKeyForNode(a, this.addrToName).localeCompare(sortingKeyForNode(b, this.addrToName)); + return sortingKeyForNode(a, this.addrToName).localeCompare( + sortingKeyForNode(b, this.addrToName) + ); }); } } @@ -58,17 +60,19 @@ export const ClusterView = ({ initialAddr }: Props) => { }, []); return ( -
+
Discovered {sortedNodes.length} nodes in cluster
{sortedNodes.map((addr) => { - return ; + return ( + + ); })}
); -}; \ No newline at end of file +}; diff --git a/tools/debug-ui/src/ConnectionStorageView.tsx b/tools/debug-ui/src/ConnectionStorageView.tsx index 47355265d21..4ca9f875d17 100644 --- a/tools/debug-ui/src/ConnectionStorageView.tsx +++ b/tools/debug-ui/src/ConnectionStorageView.tsx @@ -1,39 +1,54 @@ -import { useQuery } from "react-query"; -import { toHumanTime } from "./utils"; -import { fetchRecentOutboundConnections } from "./api"; -import "./ConnectionStorageView.scss"; +import { useQuery } from 'react-query'; +import { toHumanTime } from './utils'; +import { fetchRecentOutboundConnections } from './api'; +import './ConnectionStorageView.scss'; type ConnectionStorageViewProps = { - addr: string, + addr: string; }; export const ConnectionStorageView = ({ addr }: ConnectionStorageViewProps) => { - const { data: connectionStore, error, isLoading } = - useQuery(['connectionStore', addr], () => fetchRecentOutboundConnections(addr)); + const { + data: connectionStore, + error, + isLoading, + } = useQuery(['connectionStore', addr], () => fetchRecentOutboundConnections(addr)); if (isLoading) { return
Loading...
; } else if (error) { - return
- {(error as Error).stack} -
; + return
{(error as Error).stack}
; } - return - - - - - - - - {connectionStore!.status_response.RecentOutboundConnections.recent_outbound_connections.map((conn) => { - return - - - - - - })} - -
Peer IDPeer addressTime establishedTime connected until
{conn.peer_id}{conn.addr}{toHumanTime(Math.floor(Date.now() / 1000) - conn.time_established)}{toHumanTime(Math.floor(Date.now() / 1000) - conn.time_connected_until)}
; + return ( + + + + + + + + + {connectionStore!.status_response.RecentOutboundConnections.recent_outbound_connections.map( + (conn) => { + return ( + + + + + + + ); + } + )} + +
Peer IDPeer addressTime establishedTime connected until
{conn.peer_id}{conn.addr} + {toHumanTime( + Math.floor(Date.now() / 1000) - conn.time_established + )} + + {toHumanTime( + Math.floor(Date.now() / 1000) - conn.time_connected_until + )} +
+ ); }; diff --git a/tools/debug-ui/src/CurrentPeersView.scss b/tools/debug-ui/src/CurrentPeersView.scss index 2a1e9a0ba46..07a72cf3cd6 100644 --- a/tools/debug-ui/src/CurrentPeersView.scss +++ b/tools/debug-ui/src/CurrentPeersView.scss @@ -27,4 +27,4 @@ background-color: red; color: white; } -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/CurrentPeersView.tsx b/tools/debug-ui/src/CurrentPeersView.tsx index ad4bcc1d503..0d81eb725dc 100644 --- a/tools/debug-ui/src/CurrentPeersView.tsx +++ b/tools/debug-ui/src/CurrentPeersView.tsx @@ -1,19 +1,19 @@ -import { MouseEvent, ReactElement, useCallback, useMemo, useState } from "react"; -import { useQuery } from "react-query"; -import { fetchEpochInfo, fetchFullStatus, PeerInfoView } from "./api"; -import { formatDurationInMillis, formatTraffic, addDebugPortLink } from "./utils"; -import "./CurrentPeersView.scss"; +import { MouseEvent, useCallback, useMemo, useState } from 'react'; +import { useQuery } from 'react-query'; +import { PeerInfoView, fetchEpochInfo, fetchFullStatus } from './api'; +import { addDebugPortLink, formatDurationInMillis, formatTraffic } from './utils'; +import './CurrentPeersView.scss'; type NetworkInfoViewProps = { - addr: string, + addr: string; }; type PeerInfo = { - peer: PeerInfoView, - validator: string[], - routedValidator: string[], - statusClassName: string, -} + peer: PeerInfoView; + validator: string[]; + routedValidator: string[]; + statusClassName: string; +}; function peerClass(current_height: number, peer_height: number): string { if (peer_height > current_height + 5) { @@ -36,204 +36,267 @@ function peerClass(current_height: number, peer_height: number): string { } function getIntersection(setA: Set, setB: Set): Set { - const intersection = new Set( - [...setA].filter(element => setB.has(element)) - ); + const intersection = new Set([...setA].filter((element) => setB.has(element))); return intersection; } function getDifference(setA: Set, setB: Set): Set { - return new Set( - [...setA].filter(element => !setB.has(element)) - ); + return new Set([...setA].filter((element) => !setB.has(element))); } export const CurrentPeersView = ({ addr }: NetworkInfoViewProps) => { - const { data: fullStatus, error: fullStatusError, isLoading: fullStatusLoading } = - useQuery(['fullStatus', addr], () => fetchFullStatus(addr)); - const { data: epochInfo, error: epochInfoError, isLoading: epochInfoLoading } = - useQuery(['epochInfo', addr], () => fetchEpochInfo(addr)); + const { + data: fullStatus, + error: fullStatusError, + isLoading: fullStatusLoading, + } = useQuery(['fullStatus', addr], () => fetchFullStatus(addr)); + const { + data: epochInfo, + error: epochInfoError, + isLoading: epochInfoLoading, + } = useQuery(['epochInfo', addr], () => fetchEpochInfo(addr)); - const { blockProducers, chunkProducers, knownSet, reachableSet, numPeersByStatus, peers } = useMemo(() => { - if (fullStatus && epochInfo) { - const epochId = fullStatus?.sync_info.epoch_id; - const networkInfo = fullStatus.detailed_debug_status!.network_info; - const knownSet = new Set( - networkInfo.known_producers - .map((p) => p.account_id)); - const reachableSet = new Set( - networkInfo.known_producers - .filter((p) => (p.next_hops?.length ?? 0) > 0) - .map((p) => p.account_id)); - let blockProducers = new Set(); - let chunkProducers = new Set(); - for (const oneEpoch of epochInfo.status_response.EpochInfo) { - if (oneEpoch.epoch_id === epochId) { - blockProducers = new Set( - oneEpoch.block_producers.map(bp => bp.account_id)); - chunkProducers = new Set(oneEpoch.chunk_only_producers); - break; + const { blockProducers, chunkProducers, knownSet, reachableSet, numPeersByStatus, peers } = + useMemo(() => { + if (fullStatus && epochInfo) { + const epochId = fullStatus?.sync_info.epoch_id; + const networkInfo = fullStatus.detailed_debug_status!.network_info; + const knownSet = new Set(networkInfo.known_producers.map((p) => p.account_id)); + const reachableSet = new Set( + networkInfo.known_producers + .filter((p) => (p.next_hops?.length ?? 0) > 0) + .map((p) => p.account_id) + ); + let blockProducers = new Set(); + let chunkProducers = new Set(); + for (const oneEpoch of epochInfo.status_response.EpochInfo) { + if (oneEpoch.epoch_id === epochId) { + blockProducers = new Set( + oneEpoch.block_producers.map((bp) => bp.account_id) + ); + chunkProducers = new Set(oneEpoch.chunk_only_producers); + break; + } } - } - const currentHeight = fullStatus.sync_info.latest_block_height; - const numPeersByStatus = new Map(); - const peers = [] as PeerInfo[]; - for (const peer of networkInfo.connected_peers) { - const validator = []; - const routedValidator = []; - for (const element of networkInfo.known_producers) { - if (blockProducers.has(element.account_id) || chunkProducers.has(element.account_id)) { - if (element.peer_id === peer.peer_id) { - // This means that the peer that we're connected to is a validator. - validator.push(element.account_id); - } else if (element.next_hops?.includes(peer.peer_id)) { - // This means that the peer that we're connected to is on the shortest path - // to this validator. - routedValidator.push(element.account_id); + const currentHeight = fullStatus.sync_info.latest_block_height; + const numPeersByStatus = new Map(); + const peers = [] as PeerInfo[]; + for (const peer of networkInfo.connected_peers) { + const validator = []; + const routedValidator = []; + for (const element of networkInfo.known_producers) { + if ( + blockProducers.has(element.account_id) || + chunkProducers.has(element.account_id) + ) { + if (element.peer_id === peer.peer_id) { + // This means that the peer that we're connected to is a validator. + validator.push(element.account_id); + } else if (element.next_hops?.includes(peer.peer_id)) { + // This means that the peer that we're connected to is on the shortest path + // to this validator. + routedValidator.push(element.account_id); + } } } + const statusClassName = peerClass(currentHeight, peer.height || 0); + numPeersByStatus.set( + statusClassName, + (numPeersByStatus.get(statusClassName) || 0) + 1 + ); + peers.push({ + peer, + validator, + routedValidator, + statusClassName, + }); } - const statusClassName = peerClass(currentHeight, peer.height || 0); - numPeersByStatus.set(statusClassName, (numPeersByStatus.get(statusClassName) || 0) + 1); - peers.push({ - peer, - validator, - routedValidator, - statusClassName, - }); + return { + blockProducers, + chunkProducers, + knownSet, + reachableSet, + numPeersByStatus, + peers, + }; } return { - blockProducers, - chunkProducers, - knownSet, - reachableSet, - numPeersByStatus, - peers, - } - } - return { - blockProducers: new Set(), - chunkProducers: new Set(), - knownSet: new Set(), - reachableSet: new Set(), - numPeersByStatus: new Map(), - peers: [], - }; - }, [fullStatus, epochInfo]); + blockProducers: new Set(), + chunkProducers: new Set(), + knownSet: new Set(), + reachableSet: new Set(), + numPeersByStatus: new Map(), + peers: [], + }; + }, [fullStatus, epochInfo]); if (fullStatusLoading || epochInfoLoading) { return
Loading...
; } if (fullStatusError || epochInfoError) { - return
-
- {((fullStatusError || epochInfoError) as Error).stack} + return ( +
+
{((fullStatusError || epochInfoError) as Error).stack}
-
; + ); } if (!fullStatus || !epochInfo) { - return
-
No Data
-
; + return ( +
+
No Data
+
+ ); } const detailedDebugStatus = fullStatus.detailed_debug_status!; - return
-

PeerId: {fullStatus.node_public_key}

-

Current Sync Status: {detailedDebugStatus.sync_status}

-

- Number of peers: {detailedDebugStatus.network_info.num_connected_peers}{' '} - / {detailedDebugStatus.network_info.peer_max_count} -

+ return ( +
+

+ PeerId: {fullStatus.node_public_key} +

+

+ Current Sync Status: {detailedDebugStatus.sync_status} +

+

+ Number of peers: {detailedDebugStatus.network_info.num_connected_peers} /{' '} + {detailedDebugStatus.network_info.peer_max_count} +

-

- Block producers: {blockProducers.size}{' '} -

    -
  • Unknown: {[...getDifference(blockProducers, knownSet)].join(', ') || '(none)'}
  • -
  • Known but not reachable: { - [...getDifference(getIntersection(blockProducers, knownSet), reachableSet)].join(', ') || '(none)'}
  • -
-

+

+ Block producers: {blockProducers.size}{' '} +

    +
  • + Unknown:{' '} + {[...getDifference(blockProducers, knownSet)].join(', ') || '(none)'} +
  • +
  • + Known but not reachable:{' '} + {[ + ...getDifference( + getIntersection(blockProducers, knownSet), + reachableSet + ), + ].join(', ') || '(none)'} +
  • +
+

-

- Chunk producers: {chunkProducers.size}{' '} -

    -
  • Unknown: {[...getDifference(chunkProducers, knownSet)].join(', ') || '(none)'}
  • -
  • Known but not reachable: { - [...getDifference(getIntersection(chunkProducers, knownSet), reachableSet)].join(', ') || '(none)'}
  • -
-

+

+ Chunk producers: {chunkProducers.size}{' '} +

    +
  • + Unknown:{' '} + {[...getDifference(chunkProducers, knownSet)].join(', ') || '(none)'} +
  • +
  • + Known but not reachable:{' '} + {[ + ...getDifference( + getIntersection(chunkProducers, knownSet), + reachableSet + ), + ].join(', ') || '(none)'} +
  • +
+

-

- Unknown means that we didn't receive 'announce' information about this - validator (so we don't know on which peer it is). This usually means that the validator - didn't connect to the network during current epoch.
- Unreachable means, that we know the peer_id of this validator, but we - cannot find it in our routing table. This usually means that validator did connect to - the network in the past, but now it is gone for at least 1 hour. -

+

+ Unknown means that we didn't receive 'announce' + information about this validator (so we don't know on which peer it is). This + usually means that the validator didn't connect to the network during current + epoch.
+ Unreachable means, that we know the peer_id of this validator, but + we cannot find it in our routing table. This usually means that validator did + connect to the network in the past, but now it is gone for at least 1 hour. +

- - - {[ - ['peer_ahead_alot', 'Peer ahead a lot'], - ['peer_ahead', 'Peer ahead'], - ['peer_in_sync', 'Peer in sync'], - ['peer_behind_a_little', 'Peer behind a little'], - ['peer_behind', 'Peer behind'], - ['peer_far_behind', 'Peer far behind'] - ].map(([className, description]) => - )} - -
{description} {numPeersByStatus.get(className) || 0}
- - - +
- - - - - - - - - - - - - - - - - {peers.map(({ peer, validator, routedValidator, statusClassName }) => { - return - - - {/* strips prefix 'ed25519:' */} - - - - - - - - - - + ))} + +
AddressValidator?Account IDLast pingHeightLast Block HashTracked ShardsArchivalConnection typeNonceFirst connectionTraffic (last minute)Route to validators
{addDebugPortLink(peer.addr)}{validator.join(', ')}{peer.peer_id.substring(8, 14)}... 60 * 1000 ? 'peer_far_behind' : '' - }>{formatDurationInMillis(peer.last_time_received_message_millis)}{peer.height}{peer.block_hash}{JSON.stringify(peer.tracked_shards)}{JSON.stringify(peer.archival)}{peer.is_outbound_peer ? 'OUT' : 'IN'} - {peer.nonce}
- {peer.nonce > 1660000000 ? formatDurationInMillis(Date.now() - peer.nonce * 1000) : "old style nonce"} + {[ + ['peer_ahead_alot', 'Peer ahead a lot'], + ['peer_ahead', 'Peer ahead'], + ['peer_in_sync', 'Peer in sync'], + ['peer_behind_a_little', 'Peer behind a little'], + ['peer_behind', 'Peer behind'], + ['peer_far_behind', 'Peer far behind'], + ].map(([className, description], i) => ( +
+ {description} {numPeersByStatus.get(className) || 0} {formatDurationInMillis(peer.connection_established_time_millis)}{formatTraffic(peer.received_bytes_per_sec, peer.sent_bytes_per_sec)}
+ + + + + + + + + + + + + + + + + - })} - -
AddressValidator?Account IDLast pingHeightLast Block HashTracked ShardsArchivalConnection typeNonceFirst connectionTraffic (last minute)Route to validators
-
; + + + {peers.map(({ peer, validator, routedValidator, statusClassName }) => { + return ( + + {addDebugPortLink(peer.addr)} + {validator.join(', ')} + {peer.peer_id.substring(8, 14)}...{' '} + {/* strips prefix 'ed25519:' */} + 60 * 1000 + ? 'peer_far_behind' + : '' + }> + {formatDurationInMillis(peer.last_time_received_message_millis)} + + {peer.height} + {peer.block_hash} + {JSON.stringify(peer.tracked_shards)} + {JSON.stringify(peer.archival)} + {peer.is_outbound_peer ? 'OUT' : 'IN'} + + {peer.nonce} +
+ {peer.nonce > 1660000000 + ? formatDurationInMillis(Date.now() - peer.nonce * 1000) + : 'old style nonce'} + + + {formatDurationInMillis( + peer.connection_established_time_millis + )} + + + {formatTraffic( + peer.received_bytes_per_sec, + peer.sent_bytes_per_sec + )} + + + + + + ); + })} + + +
+ ); }; const CollapsableValidatorList = ({ validators }: { validators: string[] }) => { @@ -247,10 +310,22 @@ const CollapsableValidatorList = ({ validators }: { validators: string[] }) => { return <>{validators.join(', ')}; } if (showAll) { - return <>{validators.join(', ')} Show fewer; + return ( + <> + {validators.join(', ')}{' '} + + Show fewer + + + ); } - return <> - {validators.slice(0, MAX_TO_SHOW).join(', ')}, ...
- Show all {validators.length} - ; -} + return ( + <> + {validators.slice(0, MAX_TO_SHOW).join(', ')}, ... +
+ + Show all {validators.length} + + + ); +}; diff --git a/tools/debug-ui/src/EpochInfoView.scss b/tools/debug-ui/src/EpochInfoView.scss index b324bde5357..b0642f79182 100644 --- a/tools/debug-ui/src/EpochInfoView.scss +++ b/tools/debug-ui/src/EpochInfoView.scss @@ -29,4 +29,4 @@ .content { padding: 10px; } -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/EpochInfoView.tsx b/tools/debug-ui/src/EpochInfoView.tsx index 0cf90ff0440..2e60aac6360 100644 --- a/tools/debug-ui/src/EpochInfoView.tsx +++ b/tools/debug-ui/src/EpochInfoView.tsx @@ -1,31 +1,38 @@ -import React from "react"; -import { Navigate, Route, Routes } from "react-router"; -import { NavLink } from "react-router-dom"; import './EpochInfoView.scss'; -import { EpochShardsView } from "./EpochShardsView"; -import { EpochValidatorsView } from "./EpochValidatorsView"; -import { RecentEpochsView } from "./RecentEpochsView"; +import { NavLink } from 'react-router-dom'; +import { Navigate, Route, Routes } from 'react-router'; +import { EpochShardsView } from './EpochShardsView'; +import { EpochValidatorsView } from './EpochValidatorsView'; +import { RecentEpochsView } from './RecentEpochsView'; type EpochInfoViewProps = { - addr: string, + addr: string; }; export const EpochInfoView = ({ addr }: EpochInfoViewProps) => { - return
-
- Recent Epochs - Validators - Shard Sizes + return ( +
+
+ + Recent Epochs + + + Validators + + + Shard Sizes + +
+
+ + } /> + } /> + } /> + } /> + +
-
- - } /> - } /> - } /> - } /> - -
-
; + ); }; function navLinkClassName({ isActive }: { isActive: boolean }) { diff --git a/tools/debug-ui/src/EpochShardsView.scss b/tools/debug-ui/src/EpochShardsView.scss index a745bd78960..2b97f9dcab8 100644 --- a/tools/debug-ui/src/EpochShardsView.scss +++ b/tools/debug-ui/src/EpochShardsView.scss @@ -67,4 +67,4 @@ thead tr:nth-child(2) th:nth-child(2) { background-color: #d6ffd0; } -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/EpochShardsView.tsx b/tools/debug-ui/src/EpochShardsView.tsx index 2d539865f6c..544d4030815 100644 --- a/tools/debug-ui/src/EpochShardsView.tsx +++ b/tools/debug-ui/src/EpochShardsView.tsx @@ -1,8 +1,8 @@ -import { useQuery } from "react-query"; -import { fetchEpochInfo } from "./api"; -import "./EpochShardsView.scss"; +import { useQuery } from 'react-query'; +import { fetchEpochInfo } from './api'; +import './EpochShardsView.scss'; -function humanFileSize(bytes: number, si: boolean = false, dp: number = 1): string { +function humanFileSize(bytes: number, si = false, dp = 1): string { const thresh = si ? 1000 : 1024; if (Math.abs(bytes) < thresh) { @@ -24,20 +24,21 @@ function humanFileSize(bytes: number, si: boolean = false, dp: number = 1): stri } type EpochShardsViewProps = { - addr: string, + addr: string; }; export const EpochShardsView = ({ addr }: EpochShardsViewProps) => { - const { data: epochData, error: epochError, isLoading: epochIsLoading } = - useQuery(['epochInfo', addr], () => fetchEpochInfo(addr)); + const { + data: epochData, + error: epochError, + isLoading: epochIsLoading, + } = useQuery(['epochInfo', addr], () => fetchEpochInfo(addr)); if (epochIsLoading) { return
Loading...
; } if (epochError) { - return
- {(epochError as Error).stack} -
; + return
{(epochError as Error).stack}
; } const epochs = epochData!.status_response.EpochInfo; @@ -49,47 +50,58 @@ export const EpochShardsView = ({ addr }: EpochShardsViewProps) => { maxShardSize = Math.max(maxShardSize, size); } } - return - - - - - - - - - {epochs.slice(1).map(epoch => { - return - })} - - - - {[...Array(numShards).keys()].map(i => { - return - - {epochs.slice(1).map(epoch => { - if (epoch.shards_size_and_parts.length <= i) { - return <>; - } - const [size, parts, requested] = epoch.shards_size_and_parts[i]; - return ; + return ( +
Current EpochPast Epochs
{epoch.epoch_id.substring(0, 6)}...
Shard {i} -
- {drawShardSizeBar(size, maxShardSize)} -
{parts} parts
-
-
+ + + + + + + + + {epochs.slice(1).map((epoch) => { + return ; })} - ; - })} - -
Current EpochPast Epochs
{epoch.epoch_id.substring(0, 6)}...
; + + + + {[...Array(numShards).keys()].map((i) => { + return ( + + Shard {i} + {epochs.slice(1).map((epoch) => { + if (epoch.shards_size_and_parts.length <= i) { + return <>; + } + const [size, parts, requested] = epoch.shards_size_and_parts[i]; + return ( + +
+ {drawShardSizeBar(size, maxShardSize)} +
{parts} parts
+
+ + ); + })} + + ); + })} + + + ); }; function drawShardSizeBar(size: number, maxSize: number): JSX.Element { - const width = size / maxSize * 100 + 5; + const width = (size / maxSize) * 100 + 5; const text = humanFileSize(size); - return
-
-
{text}
-
; + return ( +
+
+
{text}
+
+ ); } diff --git a/tools/debug-ui/src/EpochValidatorsView.scss b/tools/debug-ui/src/EpochValidatorsView.scss index 45cace084cb..0c08bcf7b9d 100644 --- a/tools/debug-ui/src/EpochValidatorsView.scss +++ b/tools/debug-ui/src/EpochValidatorsView.scss @@ -96,9 +96,7 @@ } } - thead tr:nth-child(2) th { - &:nth-child(6), &:nth-child(7), &:nth-child(8), @@ -109,7 +107,6 @@ } tbody tr:last-child td { - &:nth-child(6), &:nth-child(7), &:nth-child(8), @@ -118,4 +115,4 @@ border-bottom: $current-border; } } -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/EpochValidatorsView.tsx b/tools/debug-ui/src/EpochValidatorsView.tsx index e2cb7a0766a..6790d866e7d 100644 --- a/tools/debug-ui/src/EpochValidatorsView.tsx +++ b/tools/debug-ui/src/EpochValidatorsView.tsx @@ -1,41 +1,41 @@ -import { useId } from "react"; -import { useQuery } from "react-query"; -import { Tooltip } from "react-tooltip"; -import { fetchEpochInfo, ValidatorKickoutReason } from "./api"; -import "./EpochValidatorsView.scss"; +import { useId } from 'react'; +import { useQuery } from 'react-query'; +import { Tooltip } from 'react-tooltip'; +import { ValidatorKickoutReason, fetchEpochInfo } from './api'; +import './EpochValidatorsView.scss'; interface ProducedAndExpected { - produced: number, - expected: number, + produced: number; + expected: number; } type ValidatorRole = 'BlockProducer' | 'ChunkOnlyProducer' | 'None'; interface CurrentValidatorInfo { - stake: number, - shards: number[], - blocks: ProducedAndExpected, - chunks: ProducedAndExpected, + stake: number; + shards: number[]; + blocks: ProducedAndExpected; + chunks: ProducedAndExpected; } interface NextValidatorInfo { - stake: number, - shards: number[], + stake: number; + shards: number[]; } interface ValidatorInfo { - accountId: string, - current: CurrentValidatorInfo | null, - next: NextValidatorInfo | null, - proposalStake: number | null, - kickoutReason: ValidatorKickoutReason | null, - roles: ValidatorRole[], + accountId: string; + current: CurrentValidatorInfo | null; + next: NextValidatorInfo | null; + proposalStake: number | null; + kickoutReason: ValidatorKickoutReason | null; + roles: ValidatorRole[]; } class Validators { validators: Map = new Map(); - constructor(private numEpochs: number) { } + constructor(private numEpochs: number) {} validator(accountId: string): ValidatorInfo { if (this.validators.has(accountId)) { @@ -88,22 +88,26 @@ class Validators { } type EpochValidatorViewProps = { - addr: string, + addr: string; }; export const EpochValidatorsView = ({ addr }: EpochValidatorViewProps) => { - const { data: epochData, error: epochError, isLoading: epochIsLoading } = - useQuery(['epochInfo', addr], () => fetchEpochInfo(addr)); + const { + data: epochData, + error: epochError, + isLoading: epochIsLoading, + } = useQuery(['epochInfo', addr], () => fetchEpochInfo(addr)); if (epochIsLoading) { return
Loading...
; } if (epochError) { - return
- {(epochError as Error).stack} -
; + return
{(epochError as Error).stack}
; } - let maxStake = 0, totalStake = 0, maxExpectedBlocks = 0, maxExpectedChunks = 0; + let maxStake = 0, + totalStake = 0, + maxExpectedBlocks = 0, + maxExpectedChunks = 0; const epochs = epochData!.status_response.EpochInfo; const validators = new Validators(epochs.length); const currentValidatorInfo = epochData!.status_response.EpochInfo[1].validator_info; @@ -151,61 +155,91 @@ export const EpochValidatorsView = ({ addr }: EpochValidatorViewProps) => { } }); - return - - - - - - - - - + return ( +
Next EpochCurrent EpochPast Epochs
Validator
+ + + + + + + + + - - - - + + + + - - - - - + + + + + - - {epochs.slice(2).map(epoch => { - return ; - })} - - - - {validators.sorted().map(validator => { - return - - - - - - - - - - - - - - {validator.roles.slice(2).map((role, i) => { - return + + {epochs.slice(2).map((epoch) => { + return ( + + ); })} - ; - })} - -
Next EpochCurrent EpochPast Epochs
ValidatorRoleShardsStakeProposalRoleShardsStakeProposalRoleShardsStakeBlocksChunksRoleShardsStakeBlocksChunksKickout{epoch.epoch_id.substring(0, 4)}...
{validator.accountId}{renderRole(validator.roles[0])}{validator.next?.shards?.join(',') ?? ''}{drawStakeBar(validator.next?.stake ?? null, maxStake, totalStake)}{drawStakeBar(validator.proposalStake, maxStake, totalStake)}{renderRole(validator.roles[1])}{validator.current?.shards?.join(',') ?? ''}{drawStakeBar(validator.current?.stake ?? null, maxStake, totalStake)}{drawProducedAndExpectedBar(validator.current?.blocks ?? null, maxExpectedBlocks)}{drawProducedAndExpectedBar(validator.current?.chunks ?? null, maxExpectedChunks)}{renderRole(role)}Kickout + {epoch.epoch_id.substring(0, 4)}... +
+ + + + {validators.sorted().map((validator) => { + return ( + + {validator.accountId} + {renderRole(validator.roles[0])} + {validator.next?.shards?.join(',') ?? ''} + + {drawStakeBar(validator.next?.stake ?? null, maxStake, totalStake)} + + {drawStakeBar(validator.proposalStake, maxStake, totalStake)} + {renderRole(validator.roles[1])} + {validator.current?.shards?.join(',') ?? ''} + + {drawStakeBar( + validator.current?.stake ?? null, + maxStake, + totalStake + )} + + + {drawProducedAndExpectedBar( + validator.current?.blocks ?? null, + maxExpectedBlocks + )} + + + {drawProducedAndExpectedBar( + validator.current?.chunks ?? null, + maxExpectedChunks + )} + + + + + + {validator.roles.slice(2).map((role, i) => { + return {renderRole(role)}; + })} + + ); + })} + + + ); }; -function drawProducedAndExpectedBar(producedAndExpected: ProducedAndExpected | null, maxExpected: number): JSX.Element { +function drawProducedAndExpectedBar( + producedAndExpected: ProducedAndExpected | null, + maxExpected: number +): JSX.Element { if (producedAndExpected === null) { return <>; } @@ -213,9 +247,9 @@ function drawProducedAndExpectedBar(producedAndExpected: ProducedAndExpected | n if (expected == 0) { return
0
; } - const expectedWidth = expected / maxExpected * 100 + 10; - let producedWidth = expectedWidth * produced / expected; - let missedWidth = expectedWidth * (expected - produced) / expected; + const expectedWidth = (expected / maxExpected) * 100 + 10; + let producedWidth = (expectedWidth * produced) / expected; + let missedWidth = (expectedWidth * (expected - produced)) / expected; if (produced !== expected) { if (producedWidth < 5) { producedWidth = 5; @@ -226,34 +260,45 @@ function drawProducedAndExpectedBar(producedAndExpected: ProducedAndExpected | n producedWidth = expectedWidth - missedWidth; } } - return
-
{produced}
-
- {produced !== expected && <> -
-
{expected - produced}
- } -
+ return ( +
+
{produced}
+
+ {produced !== expected && ( + <> +
+
{expected - produced}
+ + )} +
+ ); } function drawStakeBar(stake: number | null, maxStake: number, totalStake: number): JSX.Element { if (stake === null) { return <>; } - const width = stake / maxStake * 100 + 5; + const width = (stake / maxStake) * 100 + 5; const stakeText = Math.floor(stake / 1e24).toLocaleString('en-US'); - const stakePercentage = (100 * stake / totalStake).toFixed(2) + '%'; - return
-
-
{stakeText} ({stakePercentage})
-
; + const stakePercentage = ((100 * stake) / totalStake).toFixed(2) + '%'; + return ( +
+
+
+ {stakeText} ({stakePercentage}) +
+
+ ); } function renderRole(role: ValidatorRole): JSX.Element { switch (role) { - case 'BlockProducer': return BP; - case 'ChunkOnlyProducer': return CP; - default: return <>; + case 'BlockProducer': + return BP; + case 'ChunkOnlyProducer': + return CP; + default: + return <>; } } @@ -286,8 +331,12 @@ const KickoutReason = ({ reason }: { reason: ValidatorKickoutReason | null }) => kickoutSummary = 'Other'; kickoutReason = JSON.stringify(reason); } - return <> - {kickoutSummary} - - -}; \ No newline at end of file + return ( + <> + + {kickoutSummary} + + + + ); +}; diff --git a/tools/debug-ui/src/HeaderBar.scss b/tools/debug-ui/src/HeaderBar.scss index 524719a6b43..02e5321ec83 100644 --- a/tools/debug-ui/src/HeaderBar.scss +++ b/tools/debug-ui/src/HeaderBar.scss @@ -34,4 +34,4 @@ margin-right: 20px; margin-left: 4px; } -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/HeaderBar.tsx b/tools/debug-ui/src/HeaderBar.tsx index 9fe5f697ac4..afe46b0eb48 100644 --- a/tools/debug-ui/src/HeaderBar.tsx +++ b/tools/debug-ui/src/HeaderBar.tsx @@ -1,62 +1,81 @@ -import { useQuery } from "react-query"; -import { fetchBasicStatus } from "./api"; +import { useQuery } from 'react-query'; +import { fetchBasicStatus } from './api'; import './HeaderBar.scss'; type Props = { addr: string; -} +}; export const HeaderBar = ({ addr }: Props) => { - const { data: nodeStatus, error, isLoading } = useQuery(['status', addr], () => fetchBasicStatus(addr)); + const { + data: nodeStatus, + error, + isLoading, + } = useQuery(['status', addr], () => fetchBasicStatus(addr)); if (isLoading) { - return
-
Loading...
-
; + return ( +
+
Loading...
+
+ ); } if (error) { - return
-
{'' + error}
-
; + return ( +
+
{'' + error}
+
+ ); } if (!nodeStatus) { - return
-
No node status
-
; + return ( +
+
No node status
+
+ ); } const version = nodeStatus.version; - return
-
Chain
-
{nodeStatus.chain_id}
- {nodeStatus.validator_account_id &&
Validator
} - {nodeStatus.validator_account_id &&
{nodeStatus.validator_account_id}
} -
Protocol
-
{nodeStatus.protocol_version}
-
Build
-
- {version.version === 'trunk' && 'Nightly build '} - {version.version === version.build && 'Release '} - {version.version === 'trunk' || version.version === version.build - ? {version.build} - : `Release ${version.version} (build ${version.build})`} + return ( +
+
Chain
+
{nodeStatus.chain_id}
+ {nodeStatus.validator_account_id &&
Validator
} + {nodeStatus.validator_account_id && ( +
{nodeStatus.validator_account_id}
+ )} +
Protocol
+
{nodeStatus.protocol_version}
+
Build
+
+ {version.version === 'trunk' && 'Nightly build '} + {version.version === version.build && 'Release '} + {version.version === 'trunk' || version.version === version.build ? ( + + {version.build} + + ) : ( + `Release ${version.version} (build ${version.build})` + )} +
+
rustc
+
{version.rustc_version}
+
Uptime
+
{formatDurationInSec(nodeStatus.uptime_sec)}
+
Address
+
{addr}
-
rustc
-
{version.rustc_version}
-
Uptime
-
{formatDurationInSec(nodeStatus.uptime_sec)}
-
Address
-
{addr}
-
-} + ); +}; function formatDurationInSec(totalSeconds: number): string { - let seconds = totalSeconds % 60; - let total_minutes = (totalSeconds - seconds) / 60; - let minutes = total_minutes % 60; - let total_hours = (total_minutes - minutes) / 60; - let hours = total_hours % 24; - let days = (total_hours - hours) / 24; + const seconds = totalSeconds % 60; + const total_minutes = (totalSeconds - seconds) / 60; + const minutes = total_minutes % 60; + const total_hours = (total_minutes - minutes) / 60; + const hours = total_hours % 24; + const days = (total_hours - hours) / 24; return `${days}d ${addZeros(hours)}:${addZeros(minutes)}:${addZeros(seconds)}`; } @@ -66,4 +85,4 @@ function addZeros(x: number): string { } else { return '0' + x; } -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/LatestBlocksView.scss b/tools/debug-ui/src/LatestBlocksView.scss index 4fb3724a708..0681355b033 100644 --- a/tools/debug-ui/src/LatestBlocksView.scss +++ b/tools/debug-ui/src/LatestBlocksView.scss @@ -123,4 +123,4 @@ .header-head-label { background-color: rgb(235, 215, 0); } -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/LatestBlocksView.tsx b/tools/debug-ui/src/LatestBlocksView.tsx index f9bd6bfd20e..aeea9bfe5ee 100644 --- a/tools/debug-ui/src/LatestBlocksView.tsx +++ b/tools/debug-ui/src/LatestBlocksView.tsx @@ -1,7 +1,7 @@ -import React, { ReactElement, useCallback, useMemo, useState } from "react"; -import { useQuery } from "react-query"; -import Xarrow, { useXarrow, Xwrapper } from "react-xarrows"; -import { DebugBlockStatus, fetchBlockStatus, fetchFullStatus, MissedHeightInfo } from "./api"; +import { Fragment, ReactElement, useCallback, useMemo, useState } from 'react'; +import { useQuery } from 'react-query'; +import Xarrow, { Xwrapper, useXarrow } from 'react-xarrows'; +import { DebugBlockStatus, MissedHeightInfo, fetchBlockStatus, fetchFullStatus } from './api'; import './LatestBlocksView.scss'; function ellipsify(str: string, maxLen: number): string { @@ -20,29 +20,31 @@ type HashElementProps = { // Makes an element that when clicked, expands or ellipsifies the hash and creator. const HashElement = ({ hashValue, creator, expandAll, knownProducers }: HashElementProps) => { - let [expanded, setExpanded] = useState(false); - let updateXarrow = useXarrow(); - return { - setExpanded((value) => !value); - // xarrows need to be updated whenever graph dot positions may change. - updateXarrow(); - }}> - {expanded || expandAll - ? `${hashValue} ${creator}` - : `${ellipsify(hashValue, 8)} ${ellipsify(creator, 13)}`} - ; -} + const [expanded, setExpanded] = useState(false); + const updateXarrow = useXarrow(); + return ( + { + setExpanded((value) => !value); + // xarrows need to be updated whenever graph dot positions may change. + updateXarrow(); + }}> + {expanded || expandAll + ? `${hashValue} ${creator}` + : `${ellipsify(hashValue, 8)} ${ellipsify(creator, 13)}`} + + ); +}; type BlockTableRowBlock = { - block: DebugBlockStatus, - parentIndex: number | null, // the index of the parent block, or null if parent not included in the data - graphColumn: number | null, // the column to display the graph node in - blockDelay: number | null, // number of seconds since parent's block timestamp, or null if parent not included in the data - chunkSkipped: boolean[], // for each chunk, whether the chunk is the same as that chunk of parent block - isHead: boolean, - isHeaderHead: boolean, + block: DebugBlockStatus; + parentIndex: number | null; // the index of the parent block, or null if parent not included in the data + graphColumn: number | null; // the column to display the graph node in + blockDelay: number | null; // number of seconds since parent's block timestamp, or null if parent not included in the data + chunkSkipped: boolean[]; // for each chunk, whether the chunk is the same as that chunk of parent block + isHead: boolean; + isHeaderHead: boolean; }; type BlockTableRow = BlockTableRowBlock | { missedHeight: MissedHeightInfo }; @@ -51,9 +53,10 @@ function sortBlocksAndDetermineBlockGraphLayout( blocks: DebugBlockStatus[], missedHeights: MissedHeightInfo[], head: string, - headerHead: string): BlockTableRow[] { + headerHead: string +): BlockTableRow[] { const rows: BlockTableRow[] = []; - for (let block of blocks) { + for (const block of blocks) { rows.push({ block, parentIndex: null, @@ -64,14 +67,14 @@ function sortBlocksAndDetermineBlockGraphLayout( isHeaderHead: headerHead === block.block_hash, }); } - for (let missedHeight of missedHeights) { + for (const missedHeight of missedHeights) { rows.push({ missedHeight }); } function sortingKey(row: BlockTableRow) { if ('block' in row) { // some lousy tie-breaking for same-height rows. - return row.block.block_height + (row.block.block_timestamp / 1e12 % 1); + return row.block.block_height + ((row.block.block_timestamp / 1e12) % 1); } else { return row.missedHeight.block_height; } @@ -88,7 +91,7 @@ function sortBlocksAndDetermineBlockGraphLayout( let highestNodeOnFirstColumn = rows.length; for (let i = rows.length - 1; i >= 0; i--) { - let row = rows[i]; + const row = rows[i]; if ('missedHeight' in row) { continue; } @@ -99,9 +102,7 @@ function sortBlocksAndDetermineBlockGraphLayout( row.parentIndex = rowIndexByHash.get(block.prev_block_hash)!; const parentBlock = (rows[row.parentIndex] as BlockTableRowBlock).block; row.blockDelay = (block.block_timestamp - parentBlock.block_timestamp) / 1e9; - for (let j = 0; - j < Math.min(block.chunks.length, parentBlock.chunks.length); - j++) { + for (let j = 0; j < Math.min(block.chunks.length, parentBlock.chunks.length); j++) { row.chunkSkipped[j] = block.chunks[j].chunk_hash === parentBlock.chunks[j].chunk_hash; } @@ -114,9 +115,11 @@ function sortBlocksAndDetermineBlockGraphLayout( // // Not the best layout for a graph, but it's sufficient since we rarely have forks. let column = 0; - if (row.parentIndex !== null && + if ( + row.parentIndex !== null && (rows[row.parentIndex] as BlockTableRowBlock).graphColumn === 0 && - row.parentIndex > highestNodeOnFirstColumn) { + row.parentIndex > highestNodeOnFirstColumn + ) { column = 1; } else { highestNodeOnFirstColumn = i; @@ -127,33 +130,38 @@ function sortBlocksAndDetermineBlockGraphLayout( } type BlocksTableProps = { - rows: BlockTableRow[], - knownProducers: Set, - expandAll: boolean, - hideMissingHeights: boolean, -} + rows: BlockTableRow[]; + knownProducers: Set; + expandAll: boolean; + hideMissingHeights: boolean; +}; const BlocksTable = ({ rows, knownProducers, expandAll, hideMissingHeights }: BlocksTableProps) => { - let numGraphColumns = 1; // either 1 or 2; determines the width of leftmost td + let numGraphColumns = 1; // either 1 or 2; determines the width of leftmost td let numShards = 0; - for (let row of rows) { + for (const row of rows) { if ('block' in row) { numGraphColumns = Math.max(numGraphColumns, (row.graphColumn || 0) + 1); - for (let chunk of row.block.chunks) { + for (const chunk of row.block.chunks) { numShards = Math.max(numShards, chunk.shard_id + 1); } } } - const header = - Chain - Height - {'Hash & creator'} - Processing Time (ms) - Block Delay (s) - Gas price ratio - {[...Array(numShards).keys()].map(i => - Shard {i} (hash/gas(Tgas)/time(ms)))} - ; + const header = ( + + Chain + Height + {'Hash & creator'} + Processing Time (ms) + Block Delay (s) + Gas price ratio + {[...Array(numShards).keys()].map((i) => ( + + Shard {i} (hash/gas(Tgas)/time(ms)) + + ))} + + ); // One xarrow element per arrow (from block to block). const graphArrows = [] as ReactElement[]; @@ -164,38 +172,48 @@ const BlocksTable = ({ rows, knownProducers, expandAll, hideMissingHeights }: Bl const row = rows[i]; if ('missedHeight' in row) { if (!hideMissingHeights) { - tableRows.push( - - {row.missedHeight.block_height} - {row.missedHeight.block_producer} missed block - ); + tableRows.push( + + + {row.missedHeight.block_height} + + {row.missedHeight.block_producer} missed block + + + ); } continue; } - let block = row.block; + const block = row.block; const chunkCells = [] as ReactElement[]; block.chunks.forEach((chunk, shardId) => { - chunkCells.push( - - - - {(chunk.gas_used / (1024 * 1024 * 1024 * 1024)).toFixed(1)} - {chunk.processing_time_ms} - ); + chunkCells.push( + + + + + {(chunk.gas_used / (1024 * 1024 * 1024 * 1024)).toFixed(1)} + {chunk.processing_time_ms} + + ); }); tableRows.push( - + -
-
+
{block.block_height} @@ -207,52 +225,64 @@ const BlocksTable = ({ rows, knownProducers, expandAll, hideMissingHeights }: Bl hashValue={block.block_hash} creator={block.block_producer || ''} expandAll={expandAll} - knownProducers={knownProducers} /> + knownProducers={knownProducers} + /> {block.processing_time_ms} {row.blockDelay ?? ''} {block.gas_price_ratio} {block.full_block_missing && header only} {chunkCells} - ); + + ); if (row.parentIndex != null) { - graphArrows.push(); + graphArrows.push( + + ); } } - return
- {graphArrows} - - - {header} - {tableRows} - -
-
-} + return ( +
+ {graphArrows} + + + {header} + {tableRows} + +
+
+ ); +}; type LatestBlockViewProps = { - addr: string, -} + addr: string; +}; export const LatestBlocksView = ({ addr }: LatestBlockViewProps) => { - const [height, setHeight] = React.useState(null); - const [heightInInput, setHeightInInput] = React.useState(''); - const [expandAll, setExpandAll] = React.useState(false); - const [hideMissingHeights, setHideMissingHeights] = React.useState(false); - const [showMissingChunksStats, setShowMissingChunksStats] = React.useState(false); + const [height, setHeight] = useState(null); + const [heightInInput, setHeightInInput] = useState(''); + const [expandAll, setExpandAll] = useState(false); + const [hideMissingHeights, setHideMissingHeights] = useState(false); + const [showMissingChunksStats, setShowMissingChunksStats] = useState(false); const updateXarrow = useXarrow(); - const { data: status } = - useQuery(['fullStatus', addr], async () => await fetchFullStatus(addr)); - const { data: blockData, error, isLoading } = - useQuery(['latestBlocks', addr, height], async () => await fetchBlockStatus(addr, height)); + const { data: status } = useQuery( + ['fullStatus', addr], + async () => await fetchFullStatus(addr) + ); + const { + data: blockData, + error, + isLoading, + } = useQuery(['latestBlocks', addr, height], async () => await fetchBlockStatus(addr, height)); const { rows, knownProducerSet } = useMemo(() => { if (status && blockData) { @@ -266,14 +296,15 @@ export const LatestBlocksView = ({ addr }: LatestBlockViewProps) => { data.blocks, data.missed_heights, data.head, - data.header_head); + data.header_head + ); return { rows, knownProducerSet }; } return { rows: [], knownProducerSet: new Set() }; }, [status, blockData]); // Compute missing blocks and chunks statistics (whenever rows changes). - const { numCanonicalBlocks, canonicalHeightCount, numChunksSkipped } = React.useMemo(() => { + const { numCanonicalBlocks, canonicalHeightCount, numChunksSkipped } = useMemo(() => { let firstCanonicalHeight = 0; let lastCanonicalHeight = 0; let numCanonicalBlocks = 0; @@ -312,65 +343,86 @@ export const LatestBlocksView = ({ addr }: LatestBlockViewProps) => { setHeight(height); }, [heightInInput]); - return -
-
- - {height == null ? 'Displaying most recent blocks' : `Displaying blocks from height ${height}`} - - setHeightInInput(e.target.value)} /> - - -
-
Skipped chunks have grey background.
-
- Red text means that we don't know this producer - (it's not present in our announce account list). -
- {!!error &&
{(error as Error).stack}
} -
- Missing blocks: {canonicalHeightCount - numCanonicalBlocks} { } - Produced: {numCanonicalBlocks} { } - Missing Rate: {((canonicalHeightCount - numCanonicalBlocks) / canonicalHeightCount * 100).toFixed(2)}% + return ( + +
+
+ + {height == null + ? 'Displaying most recent blocks' + : `Displaying blocks from height ${height}`} + + setHeightInInput(e.target.value)} + /> + + +
+
Skipped chunks have grey background.
+
+ Red text means that we don't know this producer (it's not present in + our announce account list). +
+ {!!error &&
{(error as Error).stack}
} +
+ Missing blocks: {canonicalHeightCount - numCanonicalBlocks} {} + Produced: {numCanonicalBlocks} {} + Missing Rate:{' '} + {( + ((canonicalHeightCount - numCanonicalBlocks) / canonicalHeightCount) * + 100 + ).toFixed(2)} + % +
+ + + + {showMissingChunksStats && ( +
+ {numChunksSkipped.map((numSkipped, shardId) => ( +
+ Shard {shardId}: Missing chunks: {numSkipped} {} + Produced: {numCanonicalBlocks - numSkipped} {} + Missing Rate: {((numSkipped / numCanonicalBlocks) * 100).toFixed(2)} + % +
+ ))} +
+ )} + {isLoading ? ( +
Loading...
+ ) : ( + + )}
- - - - {showMissingChunksStats &&
- {numChunksSkipped.map((numSkipped, shardId) => -
- Shard {shardId}: Missing chunks: {numSkipped} { } - Produced: {numCanonicalBlocks - numSkipped} { } - Missing Rate: {(numSkipped / numCanonicalBlocks * 100).toFixed(2)}% -
)} -
} - {isLoading ? -
Loading...
: - - } -
- ; + + ); }; diff --git a/tools/debug-ui/src/NetworkInfoView.scss b/tools/debug-ui/src/NetworkInfoView.scss index c012eaa6a4d..26727fc1242 100644 --- a/tools/debug-ui/src/NetworkInfoView.scss +++ b/tools/debug-ui/src/NetworkInfoView.scss @@ -26,4 +26,4 @@ padding: 8px; background-color: lightgrey; } -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/NetworkInfoView.tsx b/tools/debug-ui/src/NetworkInfoView.tsx index 5e78a4f6b68..7b0ce8ec740 100644 --- a/tools/debug-ui/src/NetworkInfoView.tsx +++ b/tools/debug-ui/src/NetworkInfoView.tsx @@ -1,32 +1,40 @@ -import React from "react"; -import { Navigate, NavLink, Route, Routes } from "react-router-dom"; -import { CurrentPeersView } from "./CurrentPeersView"; import './NetworkInfoView.scss'; -import { PeerStorageView } from "./PeerStorageView"; -import { ConnectionStorageView } from "./ConnectionStorageView"; -import { Tier1View } from "./Tier1View"; +import { NavLink, Navigate, Route, Routes } from 'react-router-dom'; +import { CurrentPeersView } from './CurrentPeersView'; +import { PeerStorageView } from './PeerStorageView'; +import { ConnectionStorageView } from './ConnectionStorageView'; +import { Tier1View } from './Tier1View'; type NetworkInfoViewProps = { - addr: string, + addr: string; }; export const NetworkInfoView = ({ addr }: NetworkInfoViewProps) => { - return
-
- Current Peers - Detailed Peer Storage - Connection Storage - TIER1 - + return ( +
+
+ + Current Peers + + + Detailed Peer Storage + + + Connection Storage + + + TIER1 + +
+ + } /> + } /> + } /> + } /> + } /> +
- - } /> - } /> - } /> - } /> - } /> - -
; + ); }; function navLinkClassName({ isActive }: { isActive: boolean }) { diff --git a/tools/debug-ui/src/PeerStorageView.scss b/tools/debug-ui/src/PeerStorageView.scss index 81cf0ed43ba..c0b78af5766 100644 --- a/tools/debug-ui/src/PeerStorageView.scss +++ b/tools/debug-ui/src/PeerStorageView.scss @@ -1,3 +1,3 @@ .peer-storage-table { margin: 10px; -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/PeerStorageView.tsx b/tools/debug-ui/src/PeerStorageView.tsx index 24b3a3bb6d6..00bfd44ab90 100644 --- a/tools/debug-ui/src/PeerStorageView.tsx +++ b/tools/debug-ui/src/PeerStorageView.tsx @@ -1,48 +1,65 @@ -import { useQuery } from "react-query"; -import { toHumanTime } from "./utils"; -import { fetchPeerStore } from "./api"; -import "./PeerStorageView.scss"; +import { useQuery } from 'react-query'; +import { toHumanTime } from './utils'; +import { fetchPeerStore } from './api'; +import './PeerStorageView.scss'; type PeerStorageViewProps = { - addr: string, + addr: string; }; export const PeerStorageView = ({ addr }: PeerStorageViewProps) => { - const { data: peerStore, error, isLoading } = - useQuery(['peerStore', addr], () => fetchPeerStore(addr)); + const { + data: peerStore, + error, + isLoading, + } = useQuery(['peerStore', addr], () => fetchPeerStore(addr)); if (isLoading) { return
Loading...
; } else if (error) { - return
- {(error as Error).stack} -
; + return
{(error as Error).stack}
; } - return - - - - - - - - - - {peerStore!.status_response.PeerStore.peer_states.map((peer) => { - return - - - - - {peer.last_attempt ? <> - - - : <> - - - } - - })} - -
Peer IDPeer addressFirst seenLast seenLast connection attemptStatus
{peer.peer_id}{peer.addr}{toHumanTime(Math.floor(Date.now() / 1000) - peer.first_seen)}{toHumanTime(Math.floor(Date.now() / 1000) - peer.last_seen)}{toHumanTime(Math.floor(Date.now() / 1000) - peer.last_attempt[0])}{peer.status}
Last attempt: {peer.last_attempt[1]}
{peer.status}
; + return ( + + + + + + + + + + + {peerStore!.status_response.PeerStore.peer_states.map((peer) => { + return ( + + + + + + {peer.last_attempt ? ( + <> + + + + ) : ( + <> + + + + )} + + ); + })} + +
Peer IDPeer addressFirst seenLast seenLast connection attemptStatus
{peer.peer_id}{peer.addr}{toHumanTime(Math.floor(Date.now() / 1000) - peer.first_seen)}{toHumanTime(Math.floor(Date.now() / 1000) - peer.last_seen)} + {toHumanTime( + Math.floor(Date.now() / 1000) - peer.last_attempt[0] + )} + + {peer.status} +
+ Last attempt: {peer.last_attempt[1]} +
{peer.status}
+ ); }; diff --git a/tools/debug-ui/src/RecentEpochsView.scss b/tools/debug-ui/src/RecentEpochsView.scss index ee8487449db..47ee7a50bfd 100644 --- a/tools/debug-ui/src/RecentEpochsView.scss +++ b/tools/debug-ui/src/RecentEpochsView.scss @@ -24,4 +24,4 @@ tr.next-epoch { color: gray; } -} \ No newline at end of file +} diff --git a/tools/debug-ui/src/RecentEpochsView.tsx b/tools/debug-ui/src/RecentEpochsView.tsx index 23301726cb0..b70fa21eea6 100644 --- a/tools/debug-ui/src/RecentEpochsView.tsx +++ b/tools/debug-ui/src/RecentEpochsView.tsx @@ -1,78 +1,93 @@ -import { useQuery } from "react-query"; -import { fetchEpochInfo, fetchFullStatus } from "./api"; -import { formatDurationInMillis } from "./utils"; -import "./RecentEpochsView.scss"; +import { useQuery } from 'react-query'; +import { fetchEpochInfo, fetchFullStatus } from './api'; +import { formatDurationInMillis } from './utils'; +import './RecentEpochsView.scss'; type RecentEpochsViewProps = { - addr: string, + addr: string; }; export const RecentEpochsView = ({ addr }: RecentEpochsViewProps) => { - const { data: epochData, error: epochError, isLoading: epochIsLoading } = - useQuery(['epochInfo', addr], () => fetchEpochInfo(addr)); - const { data: statusData, error: statusError, isLoading: statusIsLoading } = - useQuery(['fullStatus', addr], () => fetchFullStatus(addr)); + const { + data: epochData, + error: epochError, + isLoading: epochIsLoading, + } = useQuery(['epochInfo', addr], () => fetchEpochInfo(addr)); + const { + data: statusData, + error: statusError, + isLoading: statusIsLoading, + } = useQuery(['fullStatus', addr], () => fetchFullStatus(addr)); if (epochIsLoading || statusIsLoading) { return
Loading...
; } if (epochError || statusError) { - return
- {((epochError || statusError) as Error).stack} -
; + return
{((epochError || statusError) as Error).stack}
; } const epochInfos = epochData!.status_response.EpochInfo; const status = statusData!; - return - - - - - - - - - - - - - - {epochInfos.map((epochInfo, index) => { - let firstBlockColumn = ""; - let epochStartColumn = ""; - if (epochInfo.first_block === null) { + return ( +
Epoch IDStart HeightProtocol VersionFirst BlockEpoch StartBlock ProducersChunk-only Producers
+ + + + + + + + + + + + + + {epochInfos.map((epochInfo, index) => { + let firstBlockColumn = ''; + let epochStartColumn = ''; + if (epochInfo.first_block === null) { + if (index == 0) { + const blocksRemaining = + epochInfo.height - status.sync_info.latest_block_height; + const millisecondsRemaining = + blocksRemaining * + status.detailed_debug_status!.block_production_delay_millis; + firstBlockColumn = `Next epoch - in ${blocksRemaining} blocks`; + epochStartColumn = `in ${formatDurationInMillis( + millisecondsRemaining + )}`; + } + } else { + firstBlockColumn = epochInfo.first_block[0]; + epochStartColumn = `${formatDurationInMillis( + Date.now() - Date.parse(epochInfo.first_block[1]) + )} ago`; + } + let rowClassName = ''; + let firstColumnText = ''; if (index == 0) { - const blocksRemaining = (epochInfo.height - status.sync_info.latest_block_height); - const millisecondsRemaining = blocksRemaining * status.detailed_debug_status!.block_production_delay_millis; - firstBlockColumn = `Next epoch - in ${blocksRemaining} blocks`; - epochStartColumn = `in ${formatDurationInMillis(millisecondsRemaining)}`; + rowClassName = 'next-epoch'; + firstColumnText = 'Next ⮕'; + } else if (index == 1) { + rowClassName = 'current-epoch'; + firstColumnText = 'Current ⮕'; } - } else { - firstBlockColumn = epochInfo.first_block[0]; - epochStartColumn = `${formatDurationInMillis(Date.now() - Date.parse(epochInfo.first_block[1]))} ago`; - } - let rowClassName = ""; - let firstColumnText = ""; - if (index == 0) { - rowClassName = "next-epoch"; - firstColumnText = "Next ⮕"; - } else if (index == 1) { - rowClassName = "current-epoch"; - firstColumnText = "Current ⮕"; - } - return - - - - - - - - - ; - })} - -
Epoch IDStart HeightProtocol VersionFirst BlockEpoch StartBlock ProducersChunk-only Producers
{firstColumnText}{epochInfo.epoch_id}{epochInfo.height}{epochInfo.protocol_version}{firstBlockColumn}{epochStartColumn}{epochInfo.block_producers.length}{epochInfo.chunk_only_producers.length}
; + return ( + + {firstColumnText} + {epochInfo.epoch_id} + {epochInfo.height} + {epochInfo.protocol_version} + {firstBlockColumn} + {epochStartColumn} + {epochInfo.block_producers.length} + {epochInfo.chunk_only_producers.length} + + ); + })} + + + ); }; diff --git a/tools/debug-ui/src/Tier1View.tsx b/tools/debug-ui/src/Tier1View.tsx index ec36dcaa923..96b8a70ede2 100644 --- a/tools/debug-ui/src/Tier1View.tsx +++ b/tools/debug-ui/src/Tier1View.tsx @@ -1,51 +1,49 @@ -import { MouseEvent, ReactElement, useCallback, useMemo, useState } from "react"; -import { useQuery } from "react-query"; -import { fetchEpochInfo, fetchFullStatus, PeerInfoView, PeerAddr } from "./api"; -import { formatDurationInMillis, formatTraffic, addDebugPortLink } from "./utils"; -import "./Tier1View.scss"; +import { MouseEvent, useCallback, useState } from 'react'; +import { useQuery } from 'react-query'; +import { PeerAddr, fetchFullStatus } from './api'; +import { addDebugPortLink, formatDurationInMillis, formatTraffic } from './utils'; +import './Tier1View.scss'; type Tier1ViewProps = { - addr: string, + addr: string; }; -type PeerInfo = { - peer: PeerInfoView, - validator: string[], - routedValidator: string[], - statusClassName: string, -} - export const Tier1View = ({ addr }: Tier1ViewProps) => { - const { data: fullStatus, error: fullStatusError, isLoading: fullStatusLoading } = - useQuery(['fullStatus', addr], () => fetchFullStatus(addr)); + const { + data: fullStatus, + error: fullStatusError, + isLoading: fullStatusLoading, + } = useQuery(['fullStatus', addr], () => fetchFullStatus(addr)); if (fullStatusLoading) { return
Loading...
; } if (fullStatusError) { - return
-
- {(fullStatusError as Error).stack} + return ( +
+
{(fullStatusError as Error).stack}
-
; + ); } if (!fullStatus) { - return
-
No Data
-
; + return ( +
+
No Data
+
+ ); } const networkInfo = fullStatus.detailed_debug_status!.network_info; const rendered = new Set(); - const rows = new Array(); + const rows = []; // tier1_connections contains TIER1 nodes we are currently connected to for (const peer of networkInfo.tier1_connections!) { - let accountKey = ""; - let proxies:PeerAddr[] = []; + let accountKey = ''; + let proxies: PeerAddr[] = []; - networkInfo.tier1_accounts_data!.forEach(account => { + networkInfo.tier1_accounts_data!.forEach((account) => { if (account.peer_id == peer.peer_id) { accountKey = account.account_key; proxies = account.proxies; @@ -54,65 +52,73 @@ export const Tier1View = ({ addr }: Tier1ViewProps) => { rendered.add(accountKey); - let accountId = ""; - networkInfo.known_producers.forEach(producer => { + let accountId = ''; + networkInfo.known_producers.forEach((producer) => { if (producer.peer_id == peer.peer_id) { accountId = producer.account_id; } }); let lastPing = formatDurationInMillis(peer.last_time_received_message_millis); - let lastPingClass = ""; + let lastPingClass = ''; if (fullStatus.node_public_key == accountKey) { - lastPing = "n/a" + lastPing = 'n/a'; } else if (peer.last_time_received_message_millis > 60 * 1000) { - lastPingClass = "peer_far_behind"; + lastPingClass = 'peer_far_behind'; } - rows.push( - {addDebugPortLink(peer.addr)} - {accountKey.substring(8, 14)}... - {accountId} - - {peer.peer_id.substring(8, 14)}... - {lastPing} - {JSON.stringify(peer.tracked_shards)} - {JSON.stringify(peer.archival)} - {peer.is_outbound_peer ? 'OUT' : 'IN'} - {formatDurationInMillis(peer.connection_established_time_millis)} - {formatTraffic(peer.received_bytes_per_sec, peer.sent_bytes_per_sec)} - ); + rows.push( + + {addDebugPortLink(peer.addr)} + {accountKey.substring(8, 14)}... + {accountId} + + + + {peer.peer_id.substring(8, 14)}... + {lastPing} + {JSON.stringify(peer.tracked_shards)} + {JSON.stringify(peer.archival)} + {peer.is_outbound_peer ? 'OUT' : 'IN'} + {formatDurationInMillis(peer.connection_established_time_millis)} + {formatTraffic(peer.received_bytes_per_sec, peer.sent_bytes_per_sec)} + + ); } // tier1_accounts_data contains data about TIER1 nodes we would like to connect to for (const account of networkInfo.tier1_accounts_data!) { - let accountKey = account.account_key; + const accountKey = account.account_key; if (rendered.has(accountKey)) { continue; } rendered.add(accountKey); - let accountId = ""; - networkInfo.known_producers.forEach(producer => { + let accountId = ''; + networkInfo.known_producers.forEach((producer) => { if (producer.peer_id == account.peer_id) { accountId = producer.account_id; } }); - rows.push( - - {accountKey.substring(8, 14)}... - {accountId} - - {account.peer_id.substring(8, 14)}... - - - - - - - ); + rows.push( + + + {accountKey.substring(8, 14)}... + {accountId} + + + + {account.peer_id.substring(8, 14)}... + + + + + + + + ); } // tier1_accounts_keys contains accounts whose data we would like to collect @@ -122,43 +128,45 @@ export const Tier1View = ({ addr }: Tier1ViewProps) => { } rendered.add(accountKey); - rows.push( - - {accountKey.substring(8, 14)}... - - - - - - - - - - ); + rows.push( + + + {accountKey.substring(8, 14)}... + + + + + + + + + + + ); } - return
- - - - - - - - - - - - - - - - - - {rows} - -
AddressAccountKeyAccount IDProxiesPeerIdLast pingTracked ShardsArchivalConnection typeFirst connectionTraffic (last minute)
-
; + return ( +
+ + + + + + + + + + + + + + + + + {rows} +
AddressAccountKeyAccount IDProxiesPeerIdLast pingTracked ShardsArchivalConnection typeFirst connectionTraffic (last minute)
+
+ ); }; const CollapsibleProxyList = ({ proxies }: { proxies: PeerAddr[] }) => { @@ -169,18 +177,30 @@ const CollapsibleProxyList = ({ proxies }: { proxies: PeerAddr[] }) => { }, []); const MAX_TO_SHOW = 2; - const formatted = proxies.map(proxy => { - return proxy.peer_id.substring(8, 14) + "...@" + proxy.addr; + const formatted = proxies.map((proxy) => { + return proxy.peer_id.substring(8, 14) + '...@' + proxy.addr; }); if (formatted.length <= MAX_TO_SHOW) { return <>{formatted.join(', ')}; } if (showAll) { - return <>{formatted.join(', ')} Show fewer; + return ( + <> + {formatted.join(', ')}{' '} + + Show fewer + + + ); } - return <> - {formatted.slice(0, MAX_TO_SHOW).join(', ')}, ...
- Show all {formatted.length} - ; -} + return ( + <> + {formatted.slice(0, MAX_TO_SHOW).join(', ')}, ... +
+ + Show all {formatted.length} + + + ); +}; diff --git a/tools/debug-ui/src/api.tsx b/tools/debug-ui/src/api.tsx index 57c1a62bde1..7ce87c9a131 100644 --- a/tools/debug-ui/src/api.tsx +++ b/tools/debug-ui/src/api.tsx @@ -1,4 +1,3 @@ - export interface StatusResponse { chain_id: string; latest_protocol_version: number; @@ -40,257 +39,258 @@ export interface BuildInfo { } export interface DetailedDebugStatus { - network_info: NetworkInfoView, - sync_status: string, - catchup_status: CatchupStatusView[], - current_head_status: BlockStatusView, - current_header_head_status: BlockStatusView, - block_production_delay_millis: number, + network_info: NetworkInfoView; + sync_status: string; + catchup_status: CatchupStatusView[]; + current_head_status: BlockStatusView; + current_header_head_status: BlockStatusView; + block_production_delay_millis: number; } export interface NetworkInfoView { - peer_max_count: number, - num_connected_peers: number, - connected_peers: PeerInfoView[], - known_producers: KnownProducerView[], - tier1_accounts_keys?: string[], - tier1_accounts_data?: AccountData[], - tier1_connections?: PeerInfoView[], + peer_max_count: number; + num_connected_peers: number; + connected_peers: PeerInfoView[]; + known_producers: KnownProducerView[]; + tier1_accounts_keys?: string[]; + tier1_accounts_data?: AccountData[]; + tier1_connections?: PeerInfoView[]; } export interface CatchupStatusView { - sync_block_hash: string, - sync_block_height: number, - shard_sync_status: { [shard_id: number]: string }, - blocks_to_catchup: BlockStatusView[], + sync_block_hash: string; + sync_block_height: number; + shard_sync_status: { [shard_id: number]: string }; + blocks_to_catchup: BlockStatusView[]; } export interface BlockStatusView { - height: number, - hash: string, + height: number; + hash: string; } export interface PeerInfoView { - addr: string, - account_id: string | null, - height: number | null, - block_hash: string | null, - is_highest_block_invalid: boolean, - tracked_shards: number[], - archival: boolean, - peer_id: string, - received_bytes_per_sec: number, - sent_bytes_per_sec: number, - last_time_peer_requested_millis: number, - last_time_received_message_millis: number, - connection_established_time_millis: number, - is_outbound_peer: boolean, - nonce: number, + addr: string; + account_id: string | null; + height: number | null; + block_hash: string | null; + is_highest_block_invalid: boolean; + tracked_shards: number[]; + archival: boolean; + peer_id: string; + received_bytes_per_sec: number; + sent_bytes_per_sec: number; + last_time_peer_requested_millis: number; + last_time_received_message_millis: number; + connection_established_time_millis: number; + is_outbound_peer: boolean; + nonce: number; } export interface KnownProducerView { - account_id: string, - peer_id: string, - next_hops: string[] | null, + account_id: string; + peer_id: string; + next_hops: string[] | null; } export interface PeerAddr { - addr: string, - peer_id: string, + addr: string; + peer_id: string; } export interface AccountData { - peer_id: string, - proxies: PeerAddr[], - account_key: string, - version: number, - timestamp: string, + peer_id: string; + proxies: PeerAddr[]; + account_key: string; + version: number; + timestamp: string; } export type SyncStatusView = - 'AwaitingPeers' | - 'NoSync' | { - EpochSync: { epoch_ord: number } - } | { - HeaderSync: { - start_height: number, - current_height: number, - highest_height: number, - } - } | { - StateSync: [ - string, - { [shard_id: number]: ShardSyncDownloadView }, - ] - } | - 'StateSyncDone' | { - BodySync: { - start_height: number, - current_height: number, - highest_height: number, - } - }; + | 'AwaitingPeers' + | 'NoSync' + | { + EpochSync: { epoch_ord: number }; + } + | { + HeaderSync: { + start_height: number; + current_height: number; + highest_height: number; + }; + } + | { + StateSync: [string, { [shard_id: number]: ShardSyncDownloadView }]; + } + | 'StateSyncDone' + | { + BodySync: { + start_height: number; + current_height: number; + highest_height: number; + }; + }; export interface ShardSyncDownloadView { - downloads: { error: boolean, done: boolean }[]; + downloads: { error: boolean; done: boolean }[]; status: string; } export interface DebugBlockStatusData { - blocks: DebugBlockStatus[], - missed_heights: MissedHeightInfo[], - head: string, - header_head: string, + blocks: DebugBlockStatus[]; + missed_heights: MissedHeightInfo[]; + head: string; + header_head: string; } export interface DebugBlockStatus { - block_hash: string, - prev_block_hash: string, - block_height: number, - block_timestamp: number, - block_producer: string | null, - full_block_missing: boolean, - is_on_canonical_chain: boolean, - chunks: DebugChunkStatus[], - processing_time_ms?: number, - gas_price_ratio: number, + block_hash: string; + prev_block_hash: string; + block_height: number; + block_timestamp: number; + block_producer: string | null; + full_block_missing: boolean; + is_on_canonical_chain: boolean; + chunks: DebugChunkStatus[]; + processing_time_ms?: number; + gas_price_ratio: number; } export interface MissedHeightInfo { - block_height: number, - block_producer: string | null, + block_height: number; + block_producer: string | null; } export interface DebugChunkStatus { - shard_id: number, - chunk_hash: string, - chunk_producer: string | null, - gas_used: number, - processing_time_ms?: number, + shard_id: number; + chunk_hash: string; + chunk_producer: string | null; + gas_used: number; + processing_time_ms?: number; } export interface EpochInfoView { - epoch_id: string, - height: number, - first_block: null | [string, string], - block_producers: ValidatorInfo[], - chunk_only_producers: string[], - validator_info: EpochValidatorInfo, - protocol_version: number, - shards_size_and_parts: [number, number, boolean][], + epoch_id: string; + height: number; + first_block: null | [string, string]; + block_producers: ValidatorInfo[]; + chunk_only_producers: string[]; + validator_info: EpochValidatorInfo; + protocol_version: number; + shards_size_and_parts: [number, number, boolean][]; } export interface EpochValidatorInfo { - current_validators: CurrentEpochValidatorInfo[], - next_validators: NextEpochValidatorInfo[], - current_fishermen: ValidatorStakeView[], - next_fishermen: ValidatorStakeView[], - current_proposals: ValidatorStakeView[], - prev_epoch_kickout: ValidatorKickoutView[], - epoch_start_height: number, - epoch_height: number, + current_validators: CurrentEpochValidatorInfo[]; + next_validators: NextEpochValidatorInfo[]; + current_fishermen: ValidatorStakeView[]; + next_fishermen: ValidatorStakeView[]; + current_proposals: ValidatorStakeView[]; + prev_epoch_kickout: ValidatorKickoutView[]; + epoch_start_height: number; + epoch_height: number; } export interface CurrentEpochValidatorInfo { - account_id: string, - public_key: string, - is_slashed: boolean, - stake: string, - shards: number[], - num_produced_blocks: number, - num_expected_blocks: number, - num_produced_chunks: number, - num_expected_chunks: number, + account_id: string; + public_key: string; + is_slashed: boolean; + stake: string; + shards: number[]; + num_produced_blocks: number; + num_expected_blocks: number; + num_produced_chunks: number; + num_expected_chunks: number; } export interface NextEpochValidatorInfo { - account_id: string, - public_key: string, - stake: string, - shards: number[], + account_id: string; + public_key: string; + stake: string; + shards: number[]; } export interface ValidatorStakeView { - account_id: string, - public_key: string, - stake: string, - validator_stake_struct_version: 'V1', + account_id: string; + public_key: string; + stake: string; + validator_stake_struct_version: 'V1'; } export interface ValidatorKickoutView { - account_id: string, - reason: ValidatorKickoutReason, + account_id: string; + reason: ValidatorKickoutReason; } export type ValidatorKickoutReason = - 'Slashed' | - { NotEnoughBlocks: { produced: number, expected: number } } | - { NotEnoughChunks: { produced: number, expected: number } } | - 'Unstaked' | - { NotEnoughStake: { stake: string, threshold: string } } | - 'DidNotGetASeat'; + | 'Slashed' + | { NotEnoughBlocks: { produced: number; expected: number } } + | { NotEnoughChunks: { produced: number; expected: number } } + | 'Unstaked' + | { NotEnoughStake: { stake: string; threshold: string } } + | 'DidNotGetASeat'; export interface PeerStoreView { - peer_states: KnownPeerStateView[], + peer_states: KnownPeerStateView[]; } export interface KnownPeerStateView { - peer_id: string, - status: string, - addr: string, - first_seen: number, - last_seen: number, - last_attempt: [number, string] | null, + peer_id: string; + status: string; + addr: string; + first_seen: number; + last_seen: number; + last_attempt: [number, string] | null; } export interface SyncStatusResponse { status_response: { - SyncStatus: SyncStatusView, + SyncStatus: SyncStatusView; }; } export interface TrackedShardsResponse { status_response: { TrackedShards: { - shards_tracked_this_epoch: boolean[], - shards_tracked_next_epoch: boolean[], - }, + shards_tracked_this_epoch: boolean[]; + shards_tracked_next_epoch: boolean[]; + }; }; } export interface BlockStatusResponse { status_response: { - BlockStatus: DebugBlockStatusData, - } + BlockStatus: DebugBlockStatusData; + }; } export interface EpochInfoResponse { status_response: { - EpochInfo: EpochInfoView[], - } + EpochInfo: EpochInfoView[]; + }; } export interface PeerStoreResponse { status_response: { - PeerStore: PeerStoreView, - } + PeerStore: PeerStoreView; + }; } export interface ConnectionInfoView { - peer_id: string, - addr: string, - time_established: number, - time_connected_until: number, + peer_id: string; + addr: string; + time_established: number; + time_connected_until: number; } export interface RecentOutboundConnectionsView { - recent_outbound_connections: ConnectionInfoView[], + recent_outbound_connections: ConnectionInfoView[]; } export interface RecentOutboundConnectionsResponse { status_response: { - RecentOutboundConnections: RecentOutboundConnectionsView - } + RecentOutboundConnections: RecentOutboundConnectionsView; + }; } export async function fetchBasicStatus(addr: string): Promise { @@ -313,7 +313,10 @@ export async function fetchTrackedShards(addr: string): Promise { +export async function fetchBlockStatus( + addr: string, + height: number | null +): Promise { const trailing = height ? `/${height}` : ''; const response = await fetch(`http://${addr}/debug/api/block_status${trailing}`); return await response.json(); @@ -329,7 +332,9 @@ export async function fetchPeerStore(addr: string): Promise { return await response.json(); } -export async function fetchRecentOutboundConnections(addr: string): Promise { +export async function fetchRecentOutboundConnections( + addr: string +): Promise { const response = await fetch(`http://${addr}/debug/api/recent_outbound_connections`); return await response.json(); } diff --git a/tools/debug-ui/src/index.css b/tools/debug-ui/src/index.css index 3e3b6a19055..d74d8add29d 100644 --- a/tools/debug-ui/src/index.css +++ b/tools/debug-ui/src/index.css @@ -1,13 +1,11 @@ body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', + 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} \ No newline at end of file + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; +} diff --git a/tools/debug-ui/src/index.tsx b/tools/debug-ui/src/index.tsx index a15b6627654..b65b0bbf313 100644 --- a/tools/debug-ui/src/index.tsx +++ b/tools/debug-ui/src/index.tsx @@ -1,31 +1,32 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; +import * as React from 'react'; +import * as ReactDOM from 'react-dom/client'; import './index.css'; -import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom'; +import { Navigate, RouterProvider, createBrowserRouter } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from 'react-query'; import { App } from './App'; import 'react-tooltip/dist/react-tooltip.css'; -const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement -); +const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); const queryClient = new QueryClient(); -const router = createBrowserRouter([{ - // Root path redirects to the current host - this is useful if we serve the app from the node - // itself. - path: '/', - element: -}, { - path: '/:addr/*', - element: -}]); +const router = createBrowserRouter([ + { + // Root path redirects to the current host - this is useful if we serve the app from the node + // itself. + path: '/', + element: , + }, + { + path: '/:addr/*', + element: , + }, +]); root.render( - - - - - + + + + + ); diff --git a/tools/debug-ui/src/utils.tsx b/tools/debug-ui/src/utils.tsx index e39ace2b4b0..c7193e98757 100644 --- a/tools/debug-ui/src/utils.tsx +++ b/tools/debug-ui/src/utils.tsx @@ -1,24 +1,24 @@ -import { ReactElement } from "react"; +import { ReactElement } from 'react'; export function formatDurationInMillis(millis: number): string { if (millis == null) { return '(null)'; } - let total_seconds = Math.floor(millis / 1000); - let hours = Math.floor(total_seconds / 3600) - let minutes = Math.floor((total_seconds - (hours * 3600)) / 60) - let seconds = total_seconds - (hours * 3600) - (minutes * 60) + const total_seconds = Math.floor(millis / 1000); + const hours = Math.floor(total_seconds / 3600); + const minutes = Math.floor((total_seconds - hours * 3600) / 60); + const seconds = total_seconds - hours * 3600 - minutes * 60; if (hours > 0) { if (minutes > 0) { - return `${hours}h ${minutes}m ${seconds}s` + return `${hours}h ${minutes}m ${seconds}s`; } else { - return `${hours}h ${seconds}s` + return `${hours}h ${seconds}s`; } } if (minutes > 0) { - return `${minutes}m ${seconds}s` + return `${minutes}m ${seconds}s`; } - return `${seconds}s` + return `${seconds}s`; } function formatBytesPerSecond(bytes_per_second: number): string { @@ -28,30 +28,29 @@ function formatBytesPerSecond(bytes_per_second: number): string { if (bytes_per_second < 3000) { return `${bytes_per_second} bps`; } - let kilobytes_per_second = bytes_per_second / 1024; + const kilobytes_per_second = bytes_per_second / 1024; if (kilobytes_per_second < 3000) { return `${kilobytes_per_second.toFixed(1)} Kbps`; } - let megabytes_per_second = kilobytes_per_second / 1024; + const megabytes_per_second = kilobytes_per_second / 1024; return `${megabytes_per_second.toFixed(1)} Mbps`; } export function formatTraffic(bytes_received: number, bytes_sent: number): ReactElement { - return
-
{"⬇ " + formatBytesPerSecond(bytes_received)}
-
{"⬆ " + formatBytesPerSecond(bytes_sent)}
-
; + return ( +
+
{'⬇ ' + formatBytesPerSecond(bytes_received)}
+
{'⬆ ' + formatBytesPerSecond(bytes_sent)}
+
+ ); } export function addDebugPortLink(peer_addr: string): ReactElement { - return - {peer_addr} - ; + return {peer_addr}; } export function toHumanTime(seconds: number): string { - let result = ""; + let result = ''; if (seconds >= 60) { let minutes = Math.floor(seconds / 60); seconds = seconds % 60; @@ -59,14 +58,14 @@ export function toHumanTime(seconds: number): string { let hours = Math.floor(minutes / 60); minutes = minutes % 60; if (hours > 24) { - let days = Math.floor(hours / 24); + const days = Math.floor(hours / 24); hours = hours % 24; - result += days + " days "; + result += days + ' days '; } - result += hours + " h "; + result += hours + ' h '; } - result += minutes + " m "; + result += minutes + ' m '; } - result += seconds + " s" + result += seconds + ' s'; return result; } diff --git a/tools/debug-ui/tsconfig.json b/tools/debug-ui/tsconfig.json index 5086fb4cdc6..86556e57fd0 100644 --- a/tools/debug-ui/tsconfig.json +++ b/tools/debug-ui/tsconfig.json @@ -18,7 +18,7 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "preserve", + "jsx": "preserve" }, "include": [ "src"