diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c435d6ea5..9419d40c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')" strategy: matrix: - node: ["18", "20"] + node: ["20", "21"] flavor: ["dev", "prod"] fail-fast: false runs-on: "ubuntu-latest" @@ -29,13 +29,13 @@ jobs: - name: Read rust-toolchain file from repository id: gettoolchain - run: echo "::set-output name=toolchain::$(cat rust-toolchain)" + run: echo "::set-output name=toolchain::$(cat ./rust/gbt/rust-toolchain)" working-directory: ${{ matrix.node }}/${{ matrix.flavor }} - name: Install ${{ steps.gettoolchain.outputs.toolchain }} Rust toolchain # Latest version available on this commit is 1.71.1 # Commit date is Aug 3, 2023 - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 with: toolchain: ${{ steps.gettoolchain.outputs.toolchain }} @@ -115,6 +115,10 @@ jobs: - name: Sync-assets run: npm run sync-assets-dev + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MEMPOOL_CDN: 1 + VERBOSE: 1 working-directory: assets/frontend - name: Zip mining-pool assets @@ -156,7 +160,7 @@ jobs: if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')" strategy: matrix: - node: ["18", "20"] + node: ["20", "21"] flavor: ["dev", "prod"] fail-fast: false runs-on: "ubuntu-latest" @@ -237,6 +241,8 @@ jobs: working-directory: ${{ matrix.node }}/${{ matrix.flavor }}/frontend env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MEMPOOL_CDN: 1 + VERBOSE: 1 e2e: if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')" @@ -329,4 +335,32 @@ jobs: CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }} - \ No newline at end of file + + validate_docker_json: + if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')" + runs-on: "ubuntu-latest" + name: Validate generated backend Docker JSON + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + path: docker + + - name: Install jq + run: sudo apt-get install jq -y + + - name: Create new start script to run on CI + run: | + sed '$d' start.sh > start_ci.sh + working-directory: docker/docker/backend + + - name: Run the script to generate the sample JSON + run: | + sh start_ci.sh + working-directory: docker/docker/backend + + - name: Validate JSON syntax + run: | + cat mempool-config.json | jq + working-directory: docker/docker/backend diff --git a/.github/workflows/get_backend_block_height.yml b/.github/workflows/get_backend_block_height.yml new file mode 100644 index 000000000..52f3b038c --- /dev/null +++ b/.github/workflows/get_backend_block_height.yml @@ -0,0 +1,19 @@ +name: 'Check if servers are in sync' + +on: [workflow_dispatch] + +jobs: + print-backend-sha: + runs-on: 'ubuntu-latest' + name: Get block height + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + path: repo + + - name: Run script + working-directory: repo + run: | + chmod +x ./scripts/get_block_tip_height.sh + sh ./scripts/get_block_tip_height.sh diff --git a/.gitignore b/.gitignore index 4f19f2522..381f2187c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ backend/mempool-config.json frontend/src/resources/config.template.js frontend/src/resources/config.js target +docker/backend/start_ci.sh \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 59562297c..000000000 --- a/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[workspace] -members = [ - "./backend/rust-gbt", -] - -[profile.release] -lto = true -codegen-units = 1 diff --git a/backend/.gitignore b/backend/.gitignore index b4393c2f0..23380b731 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -7,6 +7,12 @@ mempool-config.json pools.json icons.json +# docker +Dockerfile +GeoIP +start.sh +wait-for-it.sh + # compiled output /dist /tmp @@ -48,3 +54,6 @@ Thumbs.db # package folder (npm run package output) /package + +# Rust GBT folder (We build externally first) +/rust-gbt diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index 3cb79b909..c93774fba 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -1,5 +1,6 @@ { "MEMPOOL": { + "OFFICIAL": false, "NETWORK": "mainnet", "BACKEND": "electrum", "ENABLED": true, @@ -16,6 +17,7 @@ "MEMPOOL_BLOCKS_AMOUNT": 8, "INDEXING_BLOCKS_AMOUNT": 11000, "BLOCKS_SUMMARIES_INDEXING": false, + "GOGGLES_INDEXING": false, "USE_SECOND_NODE_FOR_MINFEE": false, "EXTERNAL_ASSETS": [], "EXTERNAL_MAX_RETRY": 1, @@ -26,9 +28,8 @@ "POOLS_JSON_URL": "https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json", "POOLS_JSON_TREE_URL": "https://api.github.com/repos/mempool/mining-pools/git/trees/master", "AUDIT": false, - "ADVANCED_GBT_AUDIT": false, - "ADVANCED_GBT_MEMPOOL": false, "RUST_GBT": false, + "LIMIT_GBT": false, "CPFP_INDEXING": false, "DISK_CACHE_BLOCK_INTERVAL": 6, "MAX_PUSH_TX_SIZE_WEIGHT": 4000000, @@ -96,10 +97,6 @@ "GEOLITE2_ASN": "/usr/local/share/GeoIP/GeoLite2-ASN.mmdb", "GEOIP2_ISP": "/usr/local/share/GeoIP/GeoIP2-ISP.mmdb" }, - "BISQ": { - "ENABLED": false, - "DATA_PATH": "/bisq/statsnode-data/btc_mainnet/db" - }, "LIGHTNING": { "ENABLED": false, "BACKEND": "lnd", @@ -130,9 +127,7 @@ "MEMPOOL_API": "https://mempool.space/api/v1", "MEMPOOL_ONION": "http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/api/v1", "LIQUID_API": "https://liquid.network/api/v1", - "LIQUID_ONION": "http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1", - "BISQ_URL": "https://bisq.markets/api", - "BISQ_ONION": "http://bisqmktse2cabavbr2xjq7xw3h6g5ottemo5rolfcwt6aly6tp5fdryd.onion/api" + "LIQUID_ONION": "http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1" }, "REDIS": { "ENABLED": false, @@ -151,7 +146,11 @@ ] }, "MEMPOOL_SERVICES": { - "API": "https://mempool.space/api", + "API": "https://mempool.space/api/v1/services", "ACCELERATIONS": false + }, + "FIAT_PRICE": { + "ENABLED": true, + "API_KEY": "your-api-key-from-freecurrencyapi.com" } } diff --git a/backend/npm_package_rm_build_deps.sh b/backend/npm_package_rm_build_deps.sh index 6b260d84d..692ba24eb 100755 --- a/backend/npm_package_rm_build_deps.sh +++ b/backend/npm_package_rm_build_deps.sh @@ -6,7 +6,4 @@ cd package/node_modules rm -r \ typescript \ @typescript-eslint \ - @napi-rs \ - ./rust-gbt/src \ - ./rust-gbt/Cargo.toml \ - ./rust-gbt/build.rs + @napi-rs diff --git a/backend/package-lock.json b/backend/package-lock.json index b3b659459..41bc0577b 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -7,17 +7,18 @@ "": { "name": "mempool-backend", "version": "3.0.0-dev", + "hasInstallScript": true, "license": "GNU Affero General Public License v3.0", "dependencies": { - "@babel/core": "^7.23.2", + "@babel/core": "^7.24.0", "@mempool/electrum-client": "1.1.9", "@types/node": "^18.15.3", "axios": "~1.6.1", "bitcoinjs-lib": "~6.1.3", "crypto-js": "~4.2.0", - "express": "~4.18.2", + "express": "~4.19.2", "maxmind": "~4.3.11", - "mysql2": "~3.7.0", + "mysql2": "~3.9.1", "redis": "^4.6.6", "rust-gbt": "file:./rust-gbt", "socks-proxy-agent": "~7.0.0", @@ -26,7 +27,7 @@ }, "devDependencies": { "@babel/code-frame": "^7.18.6", - "@babel/core": "^7.23.2", + "@babel/core": "^7.24.0", "@types/compression": "^1.7.2", "@types/crypto-js": "^4.1.1", "@types/express": "^4.17.17", @@ -42,6 +43,13 @@ "ts-node": "^10.9.1" } }, + "../rust/gbt": { + "version": "3.0.1", + "extraneous": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -65,12 +73,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { @@ -78,30 +86,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", - "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", + "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.0", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.0", + "@babel/parser": "^7.24.0", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -123,12 +131,12 @@ "dev": true }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -152,14 +160,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -214,9 +222,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", @@ -266,9 +274,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -284,32 +292,32 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", - "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz", + "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==", "dev": true, "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", @@ -321,9 +329,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -510,34 +518,34 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", + "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -545,12 +553,12 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, @@ -1499,21 +1507,6 @@ "node": ">=6" } }, - "node_modules/@napi-rs/cli": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.16.1.tgz", - "integrity": "sha512-L0Gr5iEQIDEbvWdDr1HUaBOxBSHL1VZhWSk1oryawoT8qJIY+KGfLFelU+Qma64ivCPbxYpkfPoKYVG3rcoGIA==", - "bin": { - "napi": "scripts/index.js" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, "node_modules/@noble/hashes": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", @@ -2536,12 +2529,12 @@ } }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -2549,7 +2542,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -2594,9 +2587,9 @@ } }, "node_modules/browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "dev": true, "funding": [ { @@ -2613,9 +2606,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", "update-browserslist-db": "^1.0.13" }, "bin": { @@ -2678,12 +2671,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2708,9 +2707,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001547", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz", - "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==", + "version": "1.0.30001591", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz", + "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==", "dev": true, "funding": [ { @@ -2867,9 +2866,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -2941,6 +2940,22 @@ "node": ">=0.10.0" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3031,9 +3046,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.551", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.551.tgz", - "integrity": "sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==", + "version": "1.4.686", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.686.tgz", + "integrity": "sha512-3avY1B+vUzNxEgkBDpKOP8WarvUAEwpRaiCL0He5OKWEFxzaOFiq4WoZEZe7qh0ReS7DiWoHMnYoQCKxNZNzSg==", "dev": true }, "node_modules/emittery": { @@ -3071,6 +3086,25 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -3466,16 +3500,16 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -3673,9 +3707,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -3741,9 +3775,12 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/generate-function": { "version": "2.3.1", @@ -3780,13 +3817,18 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3874,6 +3916,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -3890,6 +3943,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -3906,6 +3960,28 @@ "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -3917,6 +3993,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -4027,9 +4114,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", + "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==" }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -6110,9 +6197,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mysql2": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.7.0.tgz", - "integrity": "sha512-c45jA3Jc1X8yJKzrWu1GpplBKGwv/wIV6ITZTlCSY7npF2YfJR+6nMP5e+NTQhUeJPSyOQAbGDCGEHbAl8HN9w==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.1.tgz", + "integrity": "sha512-3njoWAAhGBYy0tWBabqUQcLtczZUxrmmtc2vszQUekg3kTJyZ5/IeLC3Fo04u6y6Iy5Sba7pIIa2P/gs8D3ZeQ==", "dependencies": { "denque": "^2.1.0", "generate-function": "^2.3.1", @@ -6192,9 +6279,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, "node_modules/normalize-path": { @@ -6219,9 +6306,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6641,9 +6728,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -6883,6 +6970,22 @@ "node": ">= 0.8.0" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -6910,13 +7013,17 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7666,15 +7773,7 @@ } }, "rust-gbt": { - "name": "gbt", - "version": "3.0.0-dev", - "hasInstallScript": true, - "dependencies": { - "@napi-rs/cli": "^2.16.1" - }, - "engines": { - "node": ">= 12" - } + "version": "0.0.1" } }, "dependencies": { @@ -7695,37 +7794,37 @@ } }, "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "requires": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" } }, "@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true }, "@babel/core": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", - "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", + "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", "dev": true, "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.0", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.0", + "@babel/parser": "^7.24.0", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -7742,12 +7841,12 @@ } }, "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "requires": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -7767,14 +7866,14 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" } @@ -7814,9 +7913,9 @@ } }, "@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.22.20", @@ -7851,9 +7950,9 @@ } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true }, "@babel/helper-validator-identifier": { @@ -7863,26 +7962,26 @@ "dev": true }, "@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true }, "@babel/helpers": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", - "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz", + "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==", "dev": true, "requires": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0" } }, "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.22.20", @@ -7891,9 +7990,9 @@ } }, "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -8023,41 +8122,41 @@ } }, "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" } }, "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", + "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } @@ -8774,11 +8873,6 @@ "resolved": "https://registry.npmjs.org/@mempool/electrum-client/-/electrum-client-1.1.9.tgz", "integrity": "sha512-mlvPiCzUlaETpYW3i6V87A24jjMYgsebaXtUo3WQyyLnYUuxs0KiXQ2mnKh3h15j8Xg/hfxeGIi+5OC9u0nftQ==" }, - "@napi-rs/cli": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.16.1.tgz", - "integrity": "sha512-L0Gr5iEQIDEbvWdDr1HUaBOxBSHL1VZhWSk1oryawoT8qJIY+KGfLFelU+Qma64ivCPbxYpkfPoKYVG3rcoGIA==" - }, "@noble/hashes": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", @@ -9580,12 +9674,12 @@ } }, "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "requires": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -9593,7 +9687,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -9633,14 +9727,14 @@ } }, "browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", "update-browserslist-db": "^1.0.13" } }, @@ -9691,12 +9785,15 @@ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" } }, "callsites": { @@ -9712,9 +9809,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001547", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz", - "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==", + "version": "1.0.30001591", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz", + "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==", "dev": true }, "chalk": { @@ -9823,9 +9920,9 @@ "dev": true }, "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" }, "cookie-signature": { "version": "1.0.6", @@ -9880,6 +9977,16 @@ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -9942,9 +10049,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { - "version": "1.4.551", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.551.tgz", - "integrity": "sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==", + "version": "1.4.686", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.686.tgz", + "integrity": "sha512-3avY1B+vUzNxEgkBDpKOP8WarvUAEwpRaiCL0He5OKWEFxzaOFiq4WoZEZe7qh0ReS7DiWoHMnYoQCKxNZNzSg==", "dev": true }, "emittery": { @@ -9973,6 +10080,19 @@ "is-arrayish": "^0.2.1" } }, + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "requires": { + "get-intrinsic": "^1.2.4" + } + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -10254,16 +10374,16 @@ } }, "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -10440,9 +10560,9 @@ "dev": true }, "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" }, "form-data": { "version": "4.0.0", @@ -10478,9 +10598,9 @@ "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "generate-function": { "version": "2.3.1", @@ -10508,13 +10628,15 @@ "dev": true }, "get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "get-package-type": { @@ -10572,6 +10694,14 @@ "slash": "^3.0.0" } }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, "graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -10588,6 +10718,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -10598,11 +10729,32 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, + "has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "requires": { + "es-define-property": "^1.0.0" + } + }, + "has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -10683,9 +10835,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", + "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==" }, "ipaddr.js": { "version": "1.9.1", @@ -12230,9 +12382,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "mysql2": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.7.0.tgz", - "integrity": "sha512-c45jA3Jc1X8yJKzrWu1GpplBKGwv/wIV6ITZTlCSY7npF2YfJR+6nMP5e+NTQhUeJPSyOQAbGDCGEHbAl8HN9w==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.1.tgz", + "integrity": "sha512-3njoWAAhGBYy0tWBabqUQcLtczZUxrmmtc2vszQUekg3kTJyZ5/IeLC3Fo04u6y6Iy5Sba7pIIa2P/gs8D3ZeQ==", "requires": { "denque": "^2.1.0", "generate-function": "^2.3.1", @@ -12298,9 +12450,9 @@ "dev": true }, "node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, "normalize-path": { @@ -12319,9 +12471,9 @@ } }, "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" }, "on-finished": { "version": "2.4.1", @@ -12601,9 +12753,9 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "requires": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -12701,10 +12853,7 @@ } }, "rust-gbt": { - "version": "file:rust-gbt", - "requires": { - "@napi-rs/cli": "^2.16.1" - } + "version": "file:rust-gbt" }, "safe-buffer": { "version": "5.2.1", @@ -12780,6 +12929,19 @@ "send": "0.18.0" } }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -12801,13 +12963,14 @@ "dev": true }, "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" } }, "signal-exit": { diff --git a/backend/package.json b/backend/package.json index cd1255392..767571f42 100644 --- a/backend/package.json +++ b/backend/package.json @@ -22,10 +22,12 @@ "main": "index.ts", "scripts": { "tsc": "./node_modules/typescript/bin/tsc -p tsconfig.build.json", - "build": "npm run rust-build && npm run tsc && npm run create-resources", + "build": "npm run tsc && npm run create-resources", + "clean": "rm -rf ./dist/ ./node_modules/ ./package/ ./rust-gbt/", "create-resources": "cp ./src/tasks/price-feeds/mtgox-weekly.json ./dist/tasks && node dist/api/fetch-version.js", "package": "./npm_package.sh", "package-rm-build-deps": "./npm_package_rm_build_deps.sh", + "preinstall": "cd ../rust/gbt && npm run build-release && npm run to-backend", "start": "node --max-old-space-size=2048 dist/index.js", "start-production": "node --max-old-space-size=16384 dist/index.js", "reindex-updated-pools": "npm run start-production --update-pools", @@ -34,20 +36,18 @@ "test:ci": "CI=true ./node_modules/.bin/jest --coverage", "lint": "./node_modules/.bin/eslint . --ext .ts", "lint:fix": "./node_modules/.bin/eslint . --ext .ts --fix", - "prettier": "./node_modules/.bin/prettier --write \"src/**/*.{js,ts}\"", - "rust-clean": "cd rust-gbt && rm -f *.node index.d.ts index.js && rm -rf target && cd ../", - "rust-build": "npm run rust-clean && cd rust-gbt && npm run build-release" + "prettier": "./node_modules/.bin/prettier --write \"src/**/*.{js,ts}\"" }, "dependencies": { - "@babel/core": "^7.23.2", + "@babel/core": "^7.24.0", "@mempool/electrum-client": "1.1.9", "@types/node": "^18.15.3", "axios": "~1.6.1", "bitcoinjs-lib": "~6.1.3", "crypto-js": "~4.2.0", - "express": "~4.18.2", + "express": "~4.19.2", "maxmind": "~4.3.11", - "mysql2": "~3.7.0", + "mysql2": "~3.9.1", "rust-gbt": "file:./rust-gbt", "redis": "^4.6.6", "socks-proxy-agent": "~7.0.0", @@ -56,7 +56,7 @@ }, "devDependencies": { "@babel/code-frame": "^7.18.6", - "@babel/core": "^7.23.2", + "@babel/core": "^7.24.0", "@types/compression": "^1.7.2", "@types/crypto-js": "^4.1.1", "@types/express": "^4.17.17", diff --git a/backend/rust-gbt/index.d.ts b/backend/rust-gbt/index.d.ts deleted file mode 100644 index d1cb85b92..000000000 --- a/backend/rust-gbt/index.d.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ - -/* auto-generated by NAPI-RS */ - -export interface ThreadTransaction { - uid: number - order: number - fee: number - weight: number - sigops: number - effectiveFeePerVsize: number - inputs: Array -} -export interface ThreadAcceleration { - uid: number - delta: number -} -export class GbtGenerator { - constructor() - /** - * # Errors - * - * Rejects if the thread panics or if the Mutex is poisoned. - */ - make(mempool: Array, accelerations: Array, maxUid: number): Promise - /** - * # Errors - * - * Rejects if the thread panics or if the Mutex is poisoned. - */ - update(newTxs: Array, removeTxs: Array, accelerations: Array, maxUid: number): Promise -} -/** - * The result from calling the gbt function. - * - * This tuple contains the following: - * blocks: A 2D Vector of transaction IDs (u32), the inner Vecs each represent a block. - * block_weights: A Vector of total weights per block. - * clusters: A 2D Vector of transaction IDs representing clusters of dependent mempool transactions - * rates: A Vector of tuples containing transaction IDs (u32) and effective fee per vsize (f64) - */ -export class GbtResult { - blocks: Array> - blockWeights: Array - clusters: Array> - rates: Array> - overflow: Array - constructor(blocks: Array>, blockWeights: Array, clusters: Array>, rates: Array>, overflow: Array) -} diff --git a/backend/rust-gbt/index.js b/backend/rust-gbt/index.js deleted file mode 100644 index 8680501d1..000000000 --- a/backend/rust-gbt/index.js +++ /dev/null @@ -1,258 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/* prettier-ignore */ - -/* auto-generated by NAPI-RS */ - -const { existsSync, readFileSync } = require('fs') -const { join } = require('path') - -const { platform, arch } = process - -let nativeBinding = null -let localFileExisted = false -let loadError = null - -function isMusl() { - // For Node 10 - if (!process.report || typeof process.report.getReport !== 'function') { - try { - const lddPath = require('child_process').execSync('which ldd').toString().trim() - return readFileSync(lddPath, 'utf8').includes('musl') - } catch (e) { - return true - } - } else { - const { glibcVersionRuntime } = process.report.getReport().header - return !glibcVersionRuntime - } -} - -switch (platform) { - case 'android': - switch (arch) { - case 'arm64': - localFileExisted = existsSync(join(__dirname, 'gbt.android-arm64.node')) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.android-arm64.node') - } else { - nativeBinding = require('gbt-android-arm64') - } - } catch (e) { - loadError = e - } - break - case 'arm': - localFileExisted = existsSync(join(__dirname, 'gbt.android-arm-eabi.node')) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.android-arm-eabi.node') - } else { - nativeBinding = require('gbt-android-arm-eabi') - } - } catch (e) { - loadError = e - } - break - default: - throw new Error(`Unsupported architecture on Android ${arch}`) - } - break - case 'win32': - switch (arch) { - case 'x64': - localFileExisted = existsSync( - join(__dirname, 'gbt.win32-x64-msvc.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.win32-x64-msvc.node') - } else { - nativeBinding = require('gbt-win32-x64-msvc') - } - } catch (e) { - loadError = e - } - break - case 'ia32': - localFileExisted = existsSync( - join(__dirname, 'gbt.win32-ia32-msvc.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.win32-ia32-msvc.node') - } else { - nativeBinding = require('gbt-win32-ia32-msvc') - } - } catch (e) { - loadError = e - } - break - case 'arm64': - localFileExisted = existsSync( - join(__dirname, 'gbt.win32-arm64-msvc.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.win32-arm64-msvc.node') - } else { - nativeBinding = require('gbt-win32-arm64-msvc') - } - } catch (e) { - loadError = e - } - break - default: - throw new Error(`Unsupported architecture on Windows: ${arch}`) - } - break - case 'darwin': - localFileExisted = existsSync(join(__dirname, 'gbt.darwin-universal.node')) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.darwin-universal.node') - } else { - nativeBinding = require('gbt-darwin-universal') - } - break - } catch {} - switch (arch) { - case 'x64': - localFileExisted = existsSync(join(__dirname, 'gbt.darwin-x64.node')) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.darwin-x64.node') - } else { - nativeBinding = require('gbt-darwin-x64') - } - } catch (e) { - loadError = e - } - break - case 'arm64': - localFileExisted = existsSync( - join(__dirname, 'gbt.darwin-arm64.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.darwin-arm64.node') - } else { - nativeBinding = require('gbt-darwin-arm64') - } - } catch (e) { - loadError = e - } - break - default: - throw new Error(`Unsupported architecture on macOS: ${arch}`) - } - break - case 'freebsd': - if (arch !== 'x64') { - throw new Error(`Unsupported architecture on FreeBSD: ${arch}`) - } - localFileExisted = existsSync(join(__dirname, 'gbt.freebsd-x64.node')) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.freebsd-x64.node') - } else { - nativeBinding = require('gbt-freebsd-x64') - } - } catch (e) { - loadError = e - } - break - case 'linux': - switch (arch) { - case 'x64': - if (isMusl()) { - localFileExisted = existsSync( - join(__dirname, 'gbt.linux-x64-musl.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.linux-x64-musl.node') - } else { - nativeBinding = require('gbt-linux-x64-musl') - } - } catch (e) { - loadError = e - } - } else { - localFileExisted = existsSync( - join(__dirname, 'gbt.linux-x64-gnu.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.linux-x64-gnu.node') - } else { - nativeBinding = require('gbt-linux-x64-gnu') - } - } catch (e) { - loadError = e - } - } - break - case 'arm64': - if (isMusl()) { - localFileExisted = existsSync( - join(__dirname, 'gbt.linux-arm64-musl.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.linux-arm64-musl.node') - } else { - nativeBinding = require('gbt-linux-arm64-musl') - } - } catch (e) { - loadError = e - } - } else { - localFileExisted = existsSync( - join(__dirname, 'gbt.linux-arm64-gnu.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.linux-arm64-gnu.node') - } else { - nativeBinding = require('gbt-linux-arm64-gnu') - } - } catch (e) { - loadError = e - } - } - break - case 'arm': - localFileExisted = existsSync( - join(__dirname, 'gbt.linux-arm-gnueabihf.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./gbt.linux-arm-gnueabihf.node') - } else { - nativeBinding = require('gbt-linux-arm-gnueabihf') - } - } catch (e) { - loadError = e - } - break - default: - throw new Error(`Unsupported architecture on Linux: ${arch}`) - } - break - default: - throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`) -} - -if (!nativeBinding) { - if (loadError) { - throw loadError - } - throw new Error(`Failed to load native binding`) -} - -const { GbtGenerator, GbtResult } = nativeBinding - -module.exports.GbtGenerator = GbtGenerator -module.exports.GbtResult = GbtResult diff --git a/backend/rust-gbt/package-lock.json b/backend/rust-gbt/package-lock.json deleted file mode 100644 index b7949b9ab..000000000 --- a/backend/rust-gbt/package-lock.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "gbt", - "version": "3.0.0-dev", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "gbt", - "version": "3.0.0-dev", - "hasInstallScript": true, - "dependencies": { - "@napi-rs/cli": "^2.16.1" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@napi-rs/cli": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.16.1.tgz", - "integrity": "sha512-L0Gr5iEQIDEbvWdDr1HUaBOxBSHL1VZhWSk1oryawoT8qJIY+KGfLFelU+Qma64ivCPbxYpkfPoKYVG3rcoGIA==", - "bin": { - "napi": "scripts/index.js" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - } - } -} diff --git a/backend/rust-gbt/package.json b/backend/rust-gbt/package.json deleted file mode 100644 index c95f36b06..000000000 --- a/backend/rust-gbt/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "gbt", - "version": "3.0.0-dev", - "description": "An inefficient re-implementation of the getBlockTemplate algorithm in Rust", - "main": "index.js", - "types": "index.d.ts", - "scripts": { - "artifacts": "napi artifacts", - "build": "napi build --platform", - "build-debug": "npm run build", - "build-release": "npm run build -- --release --strip", - "install": "npm run build-release", - "prepublishOnly": "napi prepublish -t npm", - "test": "cargo test" - }, - "author": "mononaut", - "napi": { - "name": "gbt", - "triples": { - "defaults": false, - "additional": [ - "x86_64-unknown-linux-gnu", - "x86_64-unknown-freebsd" - ] - } - }, - "dependencies": { - "@napi-rs/cli": "^2.16.1" - }, - "engines": { - "node": ">= 12" - } -} diff --git a/backend/src/__fixtures__/mempool-config.template.json b/backend/src/__fixtures__/mempool-config.template.json index 9445fc25d..1c973c45b 100644 --- a/backend/src/__fixtures__/mempool-config.template.json +++ b/backend/src/__fixtures__/mempool-config.template.json @@ -1,9 +1,11 @@ { "MEMPOOL": { "ENABLED": true, + "OFFICIAL": false, "NETWORK": "__MEMPOOL_NETWORK__", "BACKEND": "__MEMPOOL_BACKEND__", "BLOCKS_SUMMARIES_INDEXING": true, + "GOGGLES_INDEXING": false, "HTTP_PORT": 1, "SPAWN_CLUSTER_PROCS": 2, "API_URL_PREFIX": "__MEMPOOL_API_URL_PREFIX__", @@ -26,9 +28,8 @@ "POOLS_JSON_TREE_URL": "__MEMPOOL_POOLS_JSON_TREE_URL__", "POOLS_JSON_URL": "__MEMPOOL_POOLS_JSON_URL__", "AUDIT": true, - "ADVANCED_GBT_AUDIT": true, - "ADVANCED_GBT_MEMPOOL": true, "RUST_GBT": false, + "LIMIT_GBT": false, "CPFP_INDEXING": true, "MAX_BLOCKS_BULK_QUERY": 999, "DISK_CACHE_BLOCK_INTERVAL": 999, @@ -78,7 +79,8 @@ "USERNAME": "__DATABASE_USERNAME__", "PASSWORD": "__DATABASE_PASSWORD__", "PID_DIR": "__DATABASE_PID_FILE__", - "TIMEOUT": 3000 + "TIMEOUT": 3000, + "POOL_SIZE": 100 }, "SYSLOG": { "ENABLED": false, @@ -91,10 +93,6 @@ "ENABLED": false, "TX_PER_SECOND_SAMPLE_PERIOD": 20 }, - "BISQ": { - "ENABLED": true, - "DATA_PATH": "__BISQ_DATA_PATH__" - }, "SOCKS5PROXY": { "ENABLED": true, "USE_ONION": true, @@ -107,9 +105,7 @@ "MEMPOOL_API": "__EXTERNAL_DATA_SERVER_MEMPOOL_API__", "MEMPOOL_ONION": "__EXTERNAL_DATA_SERVER_MEMPOOL_ONION__", "LIQUID_API": "__EXTERNAL_DATA_SERVER_LIQUID_API__", - "LIQUID_ONION": "__EXTERNAL_DATA_SERVER_LIQUID_ONION__", - "BISQ_URL": "__EXTERNAL_DATA_SERVER_BISQ_URL__", - "BISQ_ONION": "__EXTERNAL_DATA_SERVER_BISQ_ONION__" + "LIQUID_ONION": "__EXTERNAL_DATA_SERVER_LIQUID_ONION__" }, "LIGHTNING": { "ENABLED": true, @@ -144,5 +140,9 @@ "ENABLED": false, "UNIX_SOCKET_PATH": "/tmp/redis.sock", "BATCH_QUERY_BASE_SIZE": 5000 + }, + "FIAT_PRICE": { + "ENABLED": true, + "API_KEY": "__MEMPOOL_CURRENCY_API_KEY__" } } diff --git a/backend/src/__tests__/api/difficulty-adjustment.test.ts b/backend/src/__tests__/api/difficulty-adjustment.test.ts index c3e8e1a88..a365d3429 100644 --- a/backend/src/__tests__/api/difficulty-adjustment.test.ts +++ b/backend/src/__tests__/api/difficulty-adjustment.test.ts @@ -11,9 +11,35 @@ describe('Mempool Difficulty Adjustment', () => { }; const vectors = [ - [ // Vector 1 + [ // Vector 1 (normal adjustment) + [ // Inputs + dt('2024-02-02T15:42:06.000Z'), // Last DA time (in seconds) + dt('2024-02-08T14:43:05.000Z'), // timestamp of 504 blocks ago (in seconds) + dt('2024-02-11T22:43:01.000Z'), // Current time (now) (in seconds) + 830027, // Current block height + 7.333505241141637, // Previous retarget % (Passed through) + 'mainnet', // Network (if testnet, next value is non-zero) + 0, // Latest block timestamp in seconds (only used if difficulty already locked in) + ], + { // Expected Result + progressPercent: 71.97420634920636, + difficultyChange: 8.512745140778843, + estimatedRetargetDate: 1708004001715, + remainingBlocks: 565, + remainingTime: 312620715, + previousRetarget: 7.333505241141637, + previousTime: 1706888526, + nextRetargetHeight: 830592, + timeAvg: 553311, + adjustedTimeAvg: 553311, + timeOffset: 0, + expectedBlocks: 1338.0916666666667, + }, + ], + [ // Vector 2 (within quarter-epoch overlap) [ // Inputs dt('2022-08-18T11:07:00.000Z'), // Last DA time (in seconds) + dt('2022-08-16T03:16:54.000Z'), // timestamp of 504 blocks ago (in seconds) dt('2022-08-19T14:03:53.000Z'), // Current time (now) (in seconds) 750134, // Current block height 0.6280047707459726, // Previous retarget % (Passed through) @@ -22,21 +48,23 @@ describe('Mempool Difficulty Adjustment', () => { ], { // Expected Result progressPercent: 9.027777777777777, - difficultyChange: 13.180707740199772, - estimatedRetargetDate: 1661895424692, + difficultyChange: 1.0420538959004633, + estimatedRetargetDate: 1662009048328, remainingBlocks: 1834, - remainingTime: 977591692, + remainingTime: 1091215328, previousRetarget: 0.6280047707459726, previousTime: 1660820820, nextRetargetHeight: 751968, timeAvg: 533038, + adjustedTimeAvg: 594992, timeOffset: 0, expectedBlocks: 161.68833333333333, }, ], - [ // Vector 2 (testnet) + [ // Vector 3 (testnet) [ // Inputs dt('2022-08-18T11:07:00.000Z'), // Last DA time (in seconds) + dt('2022-08-16T03:16:54.000Z'), // timestamp of 504 blocks ago (in seconds) dt('2022-08-19T14:03:53.000Z'), // Current time (now) (in seconds) 750134, // Current block height 0.6280047707459726, // Previous retarget % (Passed through) @@ -45,22 +73,24 @@ describe('Mempool Difficulty Adjustment', () => { ], { // Expected Result is same other than timeOffset progressPercent: 9.027777777777777, - difficultyChange: 13.180707740199772, - estimatedRetargetDate: 1661895424692, + difficultyChange: 1.0420538959004633, + estimatedRetargetDate: 1662009048328, remainingBlocks: 1834, - remainingTime: 977591692, + remainingTime: 1091215328, previousTime: 1660820820, previousRetarget: 0.6280047707459726, nextRetargetHeight: 751968, timeAvg: 533038, + adjustedTimeAvg: 594992, timeOffset: -667000, // 11 min 7 seconds since last block (testnet only) // If we add time avg to abs(timeOffset) it makes exactly 1200000 ms, or 20 minutes expectedBlocks: 161.68833333333333, }, ], - [ // Vector 3 (mainnet lock-in (epoch ending 788255)) + [ // Vector 4 (mainnet lock-in (epoch ending 788255)) [ // Inputs dt('2023-04-20T09:57:33.000Z'), // Last DA time (in seconds) + dt('2022-08-16T03:16:54.000Z'), // timestamp of 504 blocks ago (in seconds) dt('2023-05-04T14:54:09.000Z'), // Current time (now) (in seconds) 788255, // Current block height 1.7220298879531821, // Previous retarget % (Passed through) @@ -77,16 +107,17 @@ describe('Mempool Difficulty Adjustment', () => { previousTime: 1681984653, nextRetargetHeight: 788256, timeAvg: 609129, + adjustedTimeAvg: 609129, timeOffset: 0, expectedBlocks: 2045.66, }, ], - ] as [[number, number, number, number, string, number], DifficultyAdjustment][]; + ] as [[number, number, number, number, number, string, number], DifficultyAdjustment][]; for (const vector of vectors) { const result = calcDifficultyAdjustment(...vector[0]); // previousRetarget is passed through untouched - expect(result.previousRetarget).toStrictEqual(vector[0][3]); + expect(result.previousRetarget).toStrictEqual(vector[0][4]); expect(result).toStrictEqual(vector[1]); } }); diff --git a/backend/src/__tests__/config.test.ts b/backend/src/__tests__/config.test.ts index 97c218370..a7f447941 100644 --- a/backend/src/__tests__/config.test.ts +++ b/backend/src/__tests__/config.test.ts @@ -14,9 +14,11 @@ describe('Mempool Backend Config', () => { expect(config.MEMPOOL).toStrictEqual({ ENABLED: true, + OFFICIAL: false, NETWORK: 'mainnet', BACKEND: 'none', BLOCKS_SUMMARIES_INDEXING: false, + GOGGLES_INDEXING: false, HTTP_PORT: 8999, SPAWN_CLUSTER_PROCS: 0, API_URL_PREFIX: '/api/v1/', @@ -39,9 +41,8 @@ describe('Mempool Backend Config', () => { POOLS_JSON_TREE_URL: 'https://api.github.com/repos/mempool/mining-pools/git/trees/master', POOLS_JSON_URL: 'https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json', AUDIT: false, - ADVANCED_GBT_AUDIT: false, - ADVANCED_GBT_MEMPOOL: false, RUST_GBT: false, + LIMIT_GBT: false, CPFP_INDEXING: false, MAX_BLOCKS_BULK_QUERY: 0, DISK_CACHE_BLOCK_INTERVAL: 6, @@ -92,7 +93,8 @@ describe('Mempool Backend Config', () => { USERNAME: 'mempool', PASSWORD: 'mempool', TIMEOUT: 180000, - PID_DIR: '' + PID_DIR: '', + POOL_SIZE: 100, }); expect(config.SYSLOG).toStrictEqual({ @@ -105,8 +107,6 @@ describe('Mempool Backend Config', () => { expect(config.STATISTICS).toStrictEqual({ ENABLED: true, TX_PER_SECOND_SAMPLE_PERIOD: 150 }); - expect(config.BISQ).toStrictEqual({ ENABLED: false, DATA_PATH: '/bisq/statsnode-data/btc_mainnet/db' }); - expect(config.SOCKS5PROXY).toStrictEqual({ ENABLED: false, USE_ONION: true, @@ -120,9 +120,7 @@ describe('Mempool Backend Config', () => { MEMPOOL_API: 'https://mempool.space/api/v1', MEMPOOL_ONION: 'http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/api/v1', LIQUID_API: 'https://liquid.network/api/v1', - LIQUID_ONION: 'http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1', - BISQ_URL: 'https://bisq.markets/api', - BISQ_ONION: 'http://bisqmktse2cabavbr2xjq7xw3h6g5ottemo5rolfcwt6aly6tp5fdryd.onion/api' + LIQUID_ONION: 'http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1' }); expect(config.MAXMIND).toStrictEqual({ @@ -149,6 +147,11 @@ describe('Mempool Backend Config', () => { UNIX_SOCKET_PATH: '', BATCH_QUERY_BASE_SIZE: 5000, }); + + expect(config.FIAT_PRICE).toStrictEqual({ + ENABLED: true, + API_KEY: '', + }); }); }); @@ -175,8 +178,6 @@ describe('Mempool Backend Config', () => { expect(config.STATISTICS).toStrictEqual(fixture.STATISTICS); - expect(config.BISQ).toStrictEqual(fixture.BISQ); - expect(config.SOCKS5PROXY).toStrictEqual(fixture.SOCKS5PROXY); expect(config.EXTERNAL_DATA_SERVER).toStrictEqual(fixture.EXTERNAL_DATA_SERVER); diff --git a/backend/src/api/acceleration.ts b/backend/src/api/acceleration.ts new file mode 100644 index 000000000..412d65231 --- /dev/null +++ b/backend/src/api/acceleration.ts @@ -0,0 +1,738 @@ +import logger from '../logger'; +import { MempoolTransactionExtended } from '../mempool.interfaces'; +import { IEsploraApi } from './bitcoin/esplora-api.interface'; + +const BLOCK_WEIGHT_UNITS = 4_000_000; +const BLOCK_SIGOPS = 80_000; +const MAX_RELATIVE_GRAPH_SIZE = 200; +const BID_BOOST_WINDOW = 40_000; +const BID_BOOST_MIN_OFFSET = 10_000; +const BID_BOOST_MAX_OFFSET = 400_000; + +type Acceleration = { + txid: string; + max_bid: number; +}; + +interface TxSummary { + txid: string; // txid of the current transaction + effectiveVsize: number; // Total vsize of the dependency tree + effectiveFee: number; // Total fee of the dependency tree in sats + ancestorCount: number; // Number of ancestors +} + +export interface AccelerationInfo { + txSummary: TxSummary; + targetFeeRate: number; // target fee rate (recommended next block fee, or median fee for mined block) + nextBlockFee: number; // fee in sats required to be in the next block (using recommended next block fee, or median fee for mined block) + cost: number; // additional cost to accelerate ((cost + txSummary.effectiveFee) / txSummary.effectiveVsize) >= targetFeeRate +} + +interface GraphTx { + txid: string; + vsize: number; + weight: number; + fees: { + base: number; // in sats + }; + depends: string[]; + spentby: string[]; +} + +interface MempoolTx extends GraphTx { + ancestorcount: number; + ancestorsize: number; + fees: { // in sats + base: number; + ancestor: number; + }; + + ancestors: Map, + ancestorRate: number; + individualRate: number; + score: number; +} + +class AccelerationCosts { + /** + * Takes a list of accelerations and verbose block data + * Returns the "fair" boost rate to charge accelerations + * + * @param accelerationsx + * @param verboseBlock + */ + public calculateBoostRate(accelerations: Acceleration[], blockTxs: IEsploraApi.Transaction[]): number { + // Run GBT ourselves to calculate accurate effective fee rates + // the list of transactions comes from a mined block, so we already know everything fits within consensus limits + const template = makeBlockTemplate(blockTxs, accelerations, 1, Infinity, Infinity); + + // initialize working maps for fast tx lookups + const accMap = {}; + const txMap = {}; + for (const acceleration of accelerations) { + accMap[acceleration.txid] = acceleration; + } + for (const tx of template) { + txMap[tx.txid] = tx; + } + + // Identify and exclude accelerated and otherwise prioritized transactions + const excludeMap = {}; + let totalWeight = 0; + let minAcceleratedPackage = Infinity; + let lastEffectiveRate = 0; + // Iterate over the mined template from bottom to top. + // Transactions should appear in ascending order of mining priority. + for (const blockTx of [...blockTxs].reverse()) { + const txid = blockTx.txid; + const tx = txMap[txid]; + totalWeight += tx.weight; + const isAccelerated = accMap[txid] != null; + // If a cluster has a in-band effective fee rate than the previous cluster, + // it must have been prioritized out-of-band (in order to have a higher mining priority) + // so exclude from the analysis. + const isPrioritized = tx.effectiveFeePerVsize < lastEffectiveRate; + if (isPrioritized || isAccelerated) { + let packageWeight = 0; + // exclude this whole CPFP cluster + for (const clusterTxid of tx.cluster) { + packageWeight += txMap[clusterTxid].weight; + if (!excludeMap[clusterTxid]) { + excludeMap[clusterTxid] = true; + } + } + // keep track of the smallest accelerated CPFP cluster for later + if (isAccelerated) { + minAcceleratedPackage = Math.min(minAcceleratedPackage, packageWeight); + } + } + if (!isPrioritized) { + if (!isAccelerated) { + lastEffectiveRate = tx.effectiveFeePerVsize; + } + } + } + + // The Bid Boost Rate is calculated by disregarding the bottom X weight units of the block, + // where X is the larger of BID_BOOST_MIN_OFFSET or the smallest accelerated package weight (the "offset"), + // then taking the average fee rate of the following BID_BOOST_WINDOW weight units + // (ignoring accelerated transactions and their ancestors). + // + // Transactions within the offset might pay less than the fair rate due to bin-packing effects + // But the average rate paid by the next chunk of non-accelerated transactions provides a good + // upper bound on the "next best rate" of alternatives to including the accelerated transactions + // (since, if there were any better options, they would have been included instead) + const spareWeight = BLOCK_WEIGHT_UNITS - totalWeight; + const windowOffset = Math.min(Math.max(minAcceleratedPackage, BID_BOOST_MIN_OFFSET, spareWeight), BID_BOOST_MAX_OFFSET); + const leftBound = windowOffset; + const rightBound = windowOffset + BID_BOOST_WINDOW; + let totalFeeInWindow = 0; + let totalWeightInWindow = Math.max(0, spareWeight - leftBound); + let txIndex = blockTxs.length - 1; + for (let offset = spareWeight; offset < BLOCK_WEIGHT_UNITS && txIndex >= 0; txIndex--) { + const txid = blockTxs[txIndex].txid; + const tx = txMap[txid]; + if (excludeMap[txid]) { + // skip prioritized transactions and their ancestors + continue; + } + + const left = offset; + const right = offset + tx.weight; + offset += tx.weight; + if (right < leftBound) { + // not within window yet + continue; + } + if (left > rightBound) { + // past window + break; + } + // count fees for weight units within the window + const overlapLeft = Math.max(leftBound, left); + const overlapRight = Math.min(rightBound, right); + const overlapUnits = overlapRight - overlapLeft; + totalFeeInWindow += (tx.effectiveFeePerVsize * (overlapUnits / 4)); + totalWeightInWindow += overlapUnits; + } + + if (totalWeightInWindow < BID_BOOST_WINDOW) { + // not enough un-prioritized transactions to calculate a fair rate + // just charge everyone their max bids + return Infinity; + } + // Divide the total fee by the size of the BID_BOOST_WINDOW in vbytes + const averageRate = totalFeeInWindow / (BID_BOOST_WINDOW / 4); + return averageRate; + } + + + /** + * Takes an accelerated mined txid and a target rate + * Returns the total vsize, fees and acceleration cost (in sats) of the tx and all same-block ancestors + * + * @param txid + * @param medianFeeRate + */ + public getAccelerationInfo(tx: MempoolTransactionExtended, targetFeeRate: number, transactions: MempoolTransactionExtended[]): AccelerationInfo { + // Get same-block transaction ancestors + const allRelatives = this.getSameBlockRelatives(tx, transactions); + const relativesMap = this.initializeRelatives(allRelatives); + const rootTx = relativesMap.get(tx.txid) as MempoolTx; + + // Calculate cost to boost + return this.calculateAccelerationAncestors(rootTx, relativesMap, targetFeeRate); + } + + /** + * Takes a raw transaction, and builds a graph of same-block relatives, + * and returns as a MempoolTx + * + * @param tx + */ + private getSameBlockRelatives(tx: MempoolTransactionExtended, transactions: MempoolTransactionExtended[]): Map { + const blockTxs = new Map(); // map of txs in this block + const spendMap = new Map(); // map of outpoints to spending txids + for (const tx of transactions) { + blockTxs.set(tx.txid, tx); + for (const vin of tx.vin) { + spendMap.set(`${vin.txid}:${vin.vout}`, tx.txid); + } + } + + const relatives: Map = new Map(); + const stack: string[] = [tx.txid]; + + // build set of same-block ancestors + while (stack.length > 0) { + const nextTxid = stack.pop(); + const nextTx = nextTxid ? blockTxs.get(nextTxid) : null; + if (!nextTx || relatives.has(nextTx.txid)) { + continue; + } + + const mempoolTx = this.convertToGraphTx(nextTx); + + mempoolTx.fees.base = nextTx.fee || 0; + mempoolTx.depends = nextTx.vin.map(vin => vin.txid).filter(inTxid => inTxid && blockTxs.has(inTxid)) as string[]; + mempoolTx.spentby = nextTx.vout.map((vout, index) => spendMap.get(`${nextTx.txid}:${index}`)).filter(outTxid => outTxid && blockTxs.has(outTxid)) as string[]; + + for (const txid of [...mempoolTx.depends, ...mempoolTx.spentby]) { + if (txid) { + stack.push(txid); + } + } + + relatives.set(mempoolTx.txid, mempoolTx); + } + + return relatives; + } + + /** + * Takes a raw transaction and converts it to MempoolTx format + * fee and ancestor data is initialized with dummy/null values + * + * @param tx + */ + private convertToGraphTx(tx: MempoolTransactionExtended): GraphTx { + return { + txid: tx.txid, + vsize: Math.ceil(tx.weight / 4), + weight: tx.weight, + fees: { + base: 0, // dummy + }, + depends: [], // dummy + spentby: [], //dummy + }; + } + + private convertGraphToMempoolTx(tx: GraphTx): MempoolTx { + return { + ...tx, + fees: { + base: tx.fees.base, + ancestor: tx.fees.base, + }, + ancestorcount: 1, + ancestorsize: Math.ceil(tx.weight / 4), + ancestors: new Map(), + ancestorRate: 0, + individualRate: 0, + score: 0, + }; + } + + /** + * Given a root transaction, a list of in-mempool ancestors, and a target fee rate, + * Calculate the minimum set of transactions to fee-bump, their total vsize + fees + * + * @param tx + * @param ancestors + */ + private calculateAccelerationAncestors(tx: MempoolTx, relatives: Map, targetFeeRate: number): AccelerationInfo { + // add root tx to the ancestor map + relatives.set(tx.txid, tx); + + // Check for high-sigop transactions (not supported) + relatives.forEach(entry => { + if (entry.vsize > Math.ceil(entry.weight / 4)) { + throw new Error(`high_sigop_tx`); + } + }); + + // Initialize individual & ancestor fee rates + relatives.forEach(entry => this.setAncestorScores(entry)); + + // Sort by descending ancestor score + let sortedRelatives = Array.from(relatives.values()).sort(this.mempoolComparator); + + let includedInCluster: Map | null = null; + + // While highest score >= targetFeeRate + let maxIterations = MAX_RELATIVE_GRAPH_SIZE; + while (sortedRelatives.length && sortedRelatives[0].score && sortedRelatives[0].score >= targetFeeRate && maxIterations > 0) { + maxIterations--; + // Grab the highest scoring entry + const best = sortedRelatives.shift(); + if (best) { + const cluster = new Map(best.ancestors?.entries() || []); + if (best.ancestors.has(tx.txid)) { + includedInCluster = cluster; + } + cluster.set(best.txid, best); + // Remove this cluster (it already pays over the target rate, so doesn't need to be boosted) + // and update scores, ancestor totals and dependencies for the survivors + this.removeAncestors(cluster, relatives); + + // re-sort + sortedRelatives = Array.from(relatives.values()).sort(this.mempoolComparator); + } + } + + // sanity check for infinite loops / too many ancestors (should never happen) + if (maxIterations <= 0) { + logger.warn(`acceleration dependency calculation failed: calculateAccelerationAncestors loop exceeded ${MAX_RELATIVE_GRAPH_SIZE} iterations, unable to proceed`); + throw new Error('invalid_tx_dependencies'); + } + + let totalFee = tx.fees.ancestor; + + // transaction is already CPFP-d above the target rate by some descendant + if (includedInCluster) { + let clusterSize = 0; + let clusterFee = 0; + includedInCluster.forEach(entry => { + clusterSize += entry.vsize; + clusterFee += entry.fees.base; + }); + const clusterRate = clusterFee / clusterSize; + totalFee = Math.ceil(tx.ancestorsize * clusterRate); + } + + // Whatever remains in the accelerated tx's dependencies needs to be boosted to the targetFeeRate + // Cost = (totalVsize * targetFeeRate) - totalFee + return { + txSummary: { + txid: tx.txid, + effectiveVsize: tx.ancestorsize, + effectiveFee: totalFee, + ancestorCount: tx.ancestorcount, + }, + cost: Math.max(0, Math.ceil(tx.ancestorsize * targetFeeRate) - totalFee), + targetFeeRate, + nextBlockFee: Math.ceil(tx.ancestorsize * targetFeeRate), + }; + } + + /** + * Recursively traverses an in-mempool dependency graph, and sets a Map of in-mempool ancestors + * for each transaction. + * + * @param tx + * @param all + */ + private setAncestors(tx: MempoolTx, all: Map, visited: Map>, depth: number = 0): Map { + // sanity check for infinite recursion / too many ancestors (should never happen) + if (depth >= 100) { + logger.warn('acceleration dependency calculation failed: setAncestors reached depth of 100, unable to proceed', `Accelerator`); + throw new Error('invalid_tx_dependencies'); + } + + // initialize the ancestor map for this tx + tx.ancestors = new Map(); + tx.depends.forEach(parentId => { + const parent = all.get(parentId); + if (parent) { + // add the parent + tx.ancestors?.set(parentId, parent); + // check for a cached copy of this parent's ancestors + let ancestors = visited.get(parent.txid); + if (!ancestors) { + // recursively fetch the parent's ancestors + ancestors = this.setAncestors(parent, all, visited, depth + 1); + } + // and add to this tx's map + ancestors.forEach((ancestor, ancestorId) => { + tx.ancestors?.set(ancestorId, ancestor); + }); + } + }); + visited.set(tx.txid, tx.ancestors); + + return tx.ancestors; + } + + /** + * Efficiently sets a Map of in-mempool ancestors for each member of an expanded relative graph + * by running setAncestors on each leaf, and caching intermediate results. + * then initializes ancestor data for each transaction + * + * @param all + */ + private initializeRelatives(all: Map): Map { + const mempoolTxs = new Map(); + all.forEach(entry => { + mempoolTxs.set(entry.txid, this.convertGraphToMempoolTx(entry)); + }); + const visited: Map> = new Map(); + const leaves: MempoolTx[] = Array.from(mempoolTxs.values()).filter(entry => entry.spentby.length === 0); + for (const leaf of leaves) { + this.setAncestors(leaf, mempoolTxs, visited); + } + mempoolTxs.forEach(entry => { + entry.ancestors?.forEach(ancestor => { + entry.ancestorcount++; + entry.ancestorsize += ancestor.vsize; + entry.fees.ancestor += ancestor.fees.base; + }); + this.setAncestorScores(entry); + }); + return mempoolTxs; + } + + /** + * Remove a cluster of transactions from an in-mempool dependency graph + * and update the survivors' scores and ancestors + * + * @param cluster + * @param ancestors + */ + private removeAncestors(cluster: Map, all: Map): void { + // remove + cluster.forEach(tx => { + all.delete(tx.txid); + }); + + // update survivors + all.forEach(tx => { + cluster.forEach(remove => { + if (tx.ancestors?.has(remove.txid)) { + // remove as dependency + tx.ancestors.delete(remove.txid); + tx.depends = tx.depends.filter(parent => parent !== remove.txid); + // update ancestor sizes and fees + tx.ancestorsize -= remove.vsize; + tx.fees.ancestor -= remove.fees.base; + } + }); + // recalculate fee rates + this.setAncestorScores(tx); + }); + } + + /** + * Take a mempool transaction, and set the fee rates and ancestor score + * + * @param tx + */ + private setAncestorScores(tx: MempoolTx): void { + tx.individualRate = tx.fees.base / tx.vsize; + tx.ancestorRate = tx.fees.ancestor / tx.ancestorsize; + tx.score = Math.min(tx.individualRate, tx.ancestorRate); + } + + // Sort by descending score + private mempoolComparator(a, b): number { + return b.score - a.score; + } +} + +export default new AccelerationCosts; + +interface TemplateTransaction { + txid: string; + order: number; + weight: number; + adjustedVsize: number; // sigop-adjusted vsize, rounded up to the nearest integer + sigops: number; + fee: number; + feeDelta: number; + ancestors: string[]; + cluster: string[]; + effectiveFeePerVsize: number; +} + +interface MinerTransaction extends TemplateTransaction { + inputs: string[]; + feePerVsize: number; + relativesSet: boolean; + ancestorMap: Map; + children: Set; + ancestorFee: number; + ancestorVsize: number; + ancestorSigops: number; + score: number; + used: boolean; + modified: boolean; + dependencyRate: number; +} + +/* +* Build a block using an approximation of the transaction selection algorithm from Bitcoin Core +* (see BlockAssembler in https://github.com/bitcoin/bitcoin/blob/master/src/node/miner.cpp) +*/ +export function makeBlockTemplate(candidates: IEsploraApi.Transaction[], accelerations: Acceleration[], maxBlocks: number = 8, weightLimit: number = BLOCK_WEIGHT_UNITS, sigopLimit: number = BLOCK_SIGOPS): TemplateTransaction[] { + const auditPool: Map = new Map(); + const mempoolArray: MinerTransaction[] = []; + + candidates.forEach(tx => { + // initializing everything up front helps V8 optimize property access later + const adjustedVsize = Math.ceil(Math.max(tx.weight / 4, 5 * (tx.sigops || 0))); + const feePerVsize = (tx.fee / adjustedVsize); + auditPool.set(tx.txid, { + txid: tx.txid, + order: txidToOrdering(tx.txid), + fee: tx.fee, + feeDelta: 0, + weight: tx.weight, + adjustedVsize, + feePerVsize: feePerVsize, + effectiveFeePerVsize: feePerVsize, + dependencyRate: feePerVsize, + sigops: tx.sigops || 0, + inputs: (tx.vin?.map(vin => vin.txid) || []) as string[], + relativesSet: false, + ancestors: [], + cluster: [], + ancestorMap: new Map(), + children: new Set(), + ancestorFee: 0, + ancestorVsize: 0, + ancestorSigops: 0, + score: 0, + used: false, + modified: false, + }); + mempoolArray.push(auditPool.get(tx.txid) as MinerTransaction); + }); + + // set accelerated effective fee + for (const acceleration of accelerations) { + const tx = auditPool.get(acceleration.txid); + if (tx) { + tx.feeDelta = acceleration.max_bid; + tx.feePerVsize = ((tx.fee + tx.feeDelta) / tx.adjustedVsize); + tx.effectiveFeePerVsize = tx.feePerVsize; + tx.dependencyRate = tx.feePerVsize; + } + } + + // Build relatives graph & calculate ancestor scores + for (const tx of mempoolArray) { + if (!tx.relativesSet) { + setRelatives(tx, auditPool); + } + } + + // Sort by descending ancestor score + mempoolArray.sort(priorityComparator); + + // Build blocks by greedily choosing the highest feerate package + // (i.e. the package rooted in the transaction with the best ancestor score) + const blocks: number[][] = []; + let blockWeight = 0; + let blockSigops = 0; + const transactions: MinerTransaction[] = []; + let modified: MinerTransaction[] = []; + const overflow: MinerTransaction[] = []; + let failures = 0; + while (mempoolArray.length || modified.length) { + // skip invalid transactions + while (mempoolArray[0].used || mempoolArray[0].modified) { + mempoolArray.shift(); + } + + // Select best next package + let nextTx; + const nextPoolTx = mempoolArray[0]; + const nextModifiedTx = modified[0]; + if (nextPoolTx && (!nextModifiedTx || (nextPoolTx.score || 0) > (nextModifiedTx.score || 0))) { + nextTx = nextPoolTx; + mempoolArray.shift(); + } else { + modified.shift(); + if (nextModifiedTx) { + nextTx = nextModifiedTx; + } + } + + if (nextTx && !nextTx?.used) { + // Check if the package fits into this block + if (blocks.length >= (maxBlocks - 1) || ((blockWeight + (4 * nextTx.ancestorVsize) < weightLimit) && (blockSigops + nextTx.ancestorSigops <= sigopLimit))) { + const ancestors: MinerTransaction[] = Array.from(nextTx.ancestorMap.values()); + // sort ancestors by dependency graph (equivalent to sorting by ascending ancestor count) + const sortedTxSet = [...ancestors.sort((a, b) => { return (a.ancestorMap.size || 0) - (b.ancestorMap.size || 0); }), nextTx]; + const clusterTxids = sortedTxSet.map(tx => tx.txid); + const effectiveFeeRate = Math.min(nextTx.dependencyRate || Infinity, nextTx.ancestorFee / nextTx.ancestorVsize); + const used: MinerTransaction[] = []; + while (sortedTxSet.length) { + const ancestor = sortedTxSet.pop(); + if (!ancestor) { + continue; + } + ancestor.used = true; + ancestor.usedBy = nextTx.txid; + // update this tx with effective fee rate & relatives data + if (ancestor.effectiveFeePerVsize !== effectiveFeeRate) { + ancestor.effectiveFeePerVsize = effectiveFeeRate; + } + ancestor.cluster = clusterTxids; + transactions.push(ancestor); + blockWeight += ancestor.weight; + blockSigops += ancestor.sigops; + used.push(ancestor); + } + + // remove these as valid package ancestors for any descendants remaining in the mempool + if (used.length) { + used.forEach(tx => { + modified = updateDescendants(tx, auditPool, modified, effectiveFeeRate); + }); + } + + failures = 0; + } else { + // hold this package in an overflow list while we check for smaller options + overflow.push(nextTx); + failures++; + } + } + + // this block is full + const exceededPackageTries = failures > 1000 && blockWeight > (weightLimit - 4000); + const queueEmpty = !mempoolArray.length && !modified.length; + + if (exceededPackageTries || queueEmpty) { + break; + } + } + + for (const tx of transactions) { + tx.ancestors = Object.values(tx.ancestorMap); + } + + return transactions; +} + +// traverse in-mempool ancestors +// recursion unavoidable, but should be limited to depth < 25 by mempool policy +function setRelatives( + tx: MinerTransaction, + mempool: Map, +): void { + for (const parent of tx.inputs) { + const parentTx = mempool.get(parent); + if (parentTx && !tx.ancestorMap?.has(parent)) { + tx.ancestorMap.set(parent, parentTx); + parentTx.children.add(tx); + // visit each node only once + if (!parentTx.relativesSet) { + setRelatives(parentTx, mempool); + } + parentTx.ancestorMap.forEach((ancestor) => { + tx.ancestorMap.set(ancestor.txid, ancestor); + }); + } + }; + tx.ancestorFee = (tx.fee + tx.feeDelta); + tx.ancestorVsize = tx.adjustedVsize || 0; + tx.ancestorSigops = tx.sigops || 0; + tx.ancestorMap.forEach((ancestor) => { + tx.ancestorFee += (ancestor.fee + ancestor.feeDelta); + tx.ancestorVsize += ancestor.adjustedVsize; + tx.ancestorSigops += ancestor.sigops; + }); + tx.score = tx.ancestorFee / tx.ancestorVsize; + tx.relativesSet = true; +} + +// iterate over remaining descendants, removing the root as a valid ancestor & updating the ancestor score +// avoids recursion to limit call stack depth +function updateDescendants( + rootTx: MinerTransaction, + mempool: Map, + modified: MinerTransaction[], + clusterRate: number, +): MinerTransaction[] { + const descendantSet: Set = new Set(); + // stack of nodes left to visit + const descendants: MinerTransaction[] = []; + let descendantTx: MinerTransaction | undefined; + rootTx.children.forEach(childTx => { + if (!descendantSet.has(childTx)) { + descendants.push(childTx); + descendantSet.add(childTx); + } + }); + while (descendants.length) { + descendantTx = descendants.pop(); + if (descendantTx && descendantTx.ancestorMap && descendantTx.ancestorMap.has(rootTx.txid)) { + // remove tx as ancestor + descendantTx.ancestorMap.delete(rootTx.txid); + descendantTx.ancestorFee -= (rootTx.fee + rootTx.feeDelta); + descendantTx.ancestorVsize -= rootTx.adjustedVsize; + descendantTx.ancestorSigops -= rootTx.sigops; + descendantTx.score = descendantTx.ancestorFee / descendantTx.ancestorVsize; + descendantTx.dependencyRate = descendantTx.dependencyRate ? Math.min(descendantTx.dependencyRate, clusterRate) : clusterRate; + + if (!descendantTx.modified) { + descendantTx.modified = true; + modified.push(descendantTx); + } + + // add this node's children to the stack + descendantTx.children.forEach(childTx => { + // visit each node only once + if (!descendantSet.has(childTx)) { + descendants.push(childTx); + descendantSet.add(childTx); + } + }); + } + } + // return new, resorted modified list + return modified.sort(priorityComparator); +} + +// Used to sort an array of MinerTransactions by descending ancestor score +function priorityComparator(a: MinerTransaction, b: MinerTransaction): number { + if (b.score === a.score) { + // tie-break by txid for stability + return a.order - b.order; + } else { + return b.score - a.score; + } +} + +// returns the most significant 4 bytes of the txid as an integer +function txidToOrdering(txid: string): number { + return parseInt( + txid.substring(62, 64) + + txid.substring(60, 62) + + txid.substring(58, 60) + + txid.substring(56, 58), + 16 + ); +} diff --git a/backend/src/api/audit.ts b/backend/src/api/audit.ts index 0ddfc2cc2..fb0d05baa 100644 --- a/backend/src/api/audit.ts +++ b/backend/src/api/audit.ts @@ -7,13 +7,14 @@ const PROPAGATION_MARGIN = 180; // in seconds, time since a transaction is first class Audit { auditBlock(transactions: MempoolTransactionExtended[], projectedBlocks: MempoolBlockWithTransactions[], mempool: { [txId: string]: MempoolTransactionExtended }, useAccelerations: boolean = false) - : { censored: string[], added: string[], fresh: string[], sigop: string[], fullrbf: string[], accelerated: string[], score: number, similarity: number } { + : { censored: string[], added: string[], prioritized: string[], fresh: string[], sigop: string[], fullrbf: string[], accelerated: string[], score: number, similarity: number } { if (!projectedBlocks?.[0]?.transactionIds || !mempool) { - return { censored: [], added: [], fresh: [], sigop: [], fullrbf: [], accelerated: [], score: 1, similarity: 1 }; + return { censored: [], added: [], prioritized: [], fresh: [], sigop: [], fullrbf: [], accelerated: [], score: 1, similarity: 1 }; } const matches: string[] = []; // present in both mined block and template const added: string[] = []; // present in mined block, not in template + const prioritized: string[] = [] // present in the mined block, not in the template, but further down in the mempool const fresh: string[] = []; // missing, but firstSeen or lastBoosted within PROPAGATION_MARGIN const rbf: string[] = []; // either missing or present, and either part of a full-rbf replacement, or a conflict with the mined block const accelerated: string[] = []; // prioritized by the mempool accelerator @@ -68,20 +69,27 @@ class Audit { // we can expect an honest miner to include 'displaced' transactions in place of recent arrivals and censored txs // these displaced transactions should occupy the first N weight units of the next projected block - let displacedWeightRemaining = displacedWeight; + let displacedWeightRemaining = displacedWeight + 4000; let index = 0; let lastFeeRate = Infinity; let failures = 0; - while (projectedBlocks[1] && index < projectedBlocks[1].transactionIds.length && failures < 500) { - const txid = projectedBlocks[1].transactionIds[index]; + let blockIndex = 1; + while (projectedBlocks[blockIndex] && failures < 500) { + if (index >= projectedBlocks[blockIndex].transactionIds.length) { + index = 0; + blockIndex++; + } + const txid = projectedBlocks[blockIndex].transactionIds[index]; const tx = mempool[txid]; if (tx) { const fits = (tx.weight - displacedWeightRemaining) < 4000; - const feeMatches = tx.effectiveFeePerVsize >= lastFeeRate; + // 0.005 margin of error for any remaining vsize rounding issues + const feeMatches = tx.effectiveFeePerVsize >= (lastFeeRate - 0.005); if (fits || feeMatches) { isDisplaced[txid] = true; if (fits) { - lastFeeRate = Math.min(lastFeeRate, tx.effectiveFeePerVsize); + // (tx.effectiveFeePerVsize * tx.vsize) / Math.ceil(tx.vsize) attempts to correct for vsize rounding in the simple non-CPFP case + lastFeeRate = Math.min(lastFeeRate, (tx.effectiveFeePerVsize * tx.vsize) / Math.ceil(tx.vsize)); } if (tx.firstSeen == null || (now - (tx?.firstSeen || 0)) > PROPAGATION_MARGIN) { displacedWeightRemaining -= tx.weight; @@ -106,7 +114,11 @@ class Audit { if (rbfCache.has(tx.txid)) { rbf.push(tx.txid); } else if (!isDisplaced[tx.txid]) { - added.push(tx.txid); + if (mempool[tx.txid]) { + prioritized.push(tx.txid); + } else { + added.push(tx.txid); + } } overflowWeight += tx.weight; } @@ -155,6 +167,7 @@ class Audit { return { censored: Object.keys(isCensored), added, + prioritized, fresh, sigop: [], fullrbf: rbf, diff --git a/backend/src/api/backend-info.ts b/backend/src/api/backend-info.ts index fc3181524..d4500a837 100644 --- a/backend/src/api/backend-info.ts +++ b/backend/src/api/backend-info.ts @@ -9,8 +9,8 @@ class BackendInfo { constructor() { // This file is created by ./fetch-version.ts during building - const versionFile = path.join(__dirname, 'version.json') - var versionInfo; + const versionFile = path.join(__dirname, 'version.json'); + let versionInfo; if (fs.existsSync(versionFile)) { versionInfo = JSON.parse(fs.readFileSync(versionFile).toString()); } else { @@ -24,7 +24,8 @@ class BackendInfo { hostname: os.hostname(), version: versionInfo.version, gitCommit: versionInfo.gitCommit, - lightning: config.LIGHTNING.ENABLED + lightning: config.LIGHTNING.ENABLED, + backend: config.MEMPOOL.BACKEND, }; } @@ -32,7 +33,7 @@ class BackendInfo { return this.backendInfo; } - public getShortCommitHash() { + public getShortCommitHash(): string { return this.backendInfo.gitCommit.slice(0, 7); } } diff --git a/backend/src/api/bisq/bisq.routes.ts b/backend/src/api/bisq/bisq.routes.ts deleted file mode 100644 index 8f002836f..000000000 --- a/backend/src/api/bisq/bisq.routes.ts +++ /dev/null @@ -1,381 +0,0 @@ -import { Application, Request, Response } from 'express'; -import config from '../../config'; -import { RequiredSpec } from '../../mempool.interfaces'; -import bisq from './bisq'; -import { MarketsApiError } from './interfaces'; -import marketsApi from './markets-api'; - -class BisqRoutes { - public initRoutes(app: Application) { - app - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/stats', this.getBisqStats) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/tx/:txId', this.getBisqTransaction) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/block/:hash', this.getBisqBlock) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/blocks/tip/height', this.getBisqTip) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/blocks/:index/:length', this.getBisqBlocks) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/address/:address', this.getBisqAddress) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/txs/:index/:length', this.getBisqTransactions) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/currencies', this.getBisqMarketCurrencies.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/depth', this.getBisqMarketDepth.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/hloc', this.getBisqMarketHloc.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/markets', this.getBisqMarketMarkets.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/offers', this.getBisqMarketOffers.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/ticker', this.getBisqMarketTicker.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/trades', this.getBisqMarketTrades.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/volumes', this.getBisqMarketVolumes.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/volumes/7d', this.getBisqMarketVolumes7d.bind(this)) - ; - } - - - private getBisqStats(req: Request, res: Response) { - const result = bisq.getStats(); - res.json(result); - } - - private getBisqTip(req: Request, res: Response) { - const result = bisq.getLatestBlockHeight(); - res.type('text/plain'); - res.send(result.toString()); - } - - private getBisqTransaction(req: Request, res: Response) { - const result = bisq.getTransaction(req.params.txId); - if (result) { - res.json(result); - } else { - res.status(404).send('Bisq transaction not found'); - } - } - - private getBisqTransactions(req: Request, res: Response) { - const types: string[] = []; - req.query.types = req.query.types || []; - if (!Array.isArray(req.query.types)) { - res.status(500).send('Types is not an array'); - return; - } - - for (const _type in req.query.types) { - if (typeof req.query.types[_type] === 'string') { - types.push(req.query.types[_type].toString()); - } - } - - const index = parseInt(req.params.index, 10) || 0; - const length = parseInt(req.params.length, 10) > 100 ? 100 : parseInt(req.params.length, 10) || 25; - const [transactions, count] = bisq.getTransactions(index, length, types); - res.header('X-Total-Count', count.toString()); - res.json(transactions); - } - - private getBisqBlock(req: Request, res: Response) { - const result = bisq.getBlock(req.params.hash); - if (result) { - res.json(result); - } else { - res.status(404).send('Bisq block not found'); - } - } - - private getBisqBlocks(req: Request, res: Response) { - const index = parseInt(req.params.index, 10) || 0; - const length = parseInt(req.params.length, 10) > 100 ? 100 : parseInt(req.params.length, 10) || 25; - const [transactions, count] = bisq.getBlocks(index, length); - res.header('X-Total-Count', count.toString()); - res.json(transactions); - } - - private getBisqAddress(req: Request, res: Response) { - const result = bisq.getAddress(req.params.address.substr(1)); - if (result) { - res.json(result); - } else { - res.status(404).send('Bisq address not found'); - } - } - - private getBisqMarketCurrencies(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'type': { - required: false, - types: ['crypto', 'fiat', 'all'] - }, - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getCurrencies(p.type); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketCurrencies error')); - } - } - - private getBisqMarketDepth(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'market': { - required: true, - types: ['@string'] - }, - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getDepth(p.market); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketDepth error')); - } - } - - private getBisqMarketMarkets(req: Request, res: Response) { - const result = marketsApi.getMarkets(); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketMarkets error')); - } - } - - private getBisqMarketTrades(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'market': { - required: true, - types: ['@string'] - }, - 'timestamp_from': { - required: false, - types: ['@number'] - }, - 'timestamp_to': { - required: false, - types: ['@number'] - }, - 'trade_id_to': { - required: false, - types: ['@string'] - }, - 'trade_id_from': { - required: false, - types: ['@string'] - }, - 'direction': { - required: false, - types: ['buy', 'sell'] - }, - 'limit': { - required: false, - types: ['@number'] - }, - 'sort': { - required: false, - types: ['asc', 'desc'] - } - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getTrades(p.market, p.timestamp_from, - p.timestamp_to, p.trade_id_from, p.trade_id_to, p.direction, p.limit, p.sort); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketTrades error')); - } - } - - private getBisqMarketOffers(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'market': { - required: true, - types: ['@string'] - }, - 'direction': { - required: false, - types: ['buy', 'sell'] - }, - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getOffers(p.market, p.direction); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketOffers error')); - } - } - - private getBisqMarketVolumes(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'market': { - required: false, - types: ['@string'] - }, - 'interval': { - required: false, - types: ['minute', 'half_hour', 'hour', 'half_day', 'day', 'week', 'month', 'year', 'auto'] - }, - 'timestamp_from': { - required: false, - types: ['@number'] - }, - 'timestamp_to': { - required: false, - types: ['@number'] - }, - 'milliseconds': { - required: false, - types: ['@boolean'] - }, - 'timestamp': { - required: false, - types: ['no', 'yes'] - }, - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getVolumes(p.market, p.timestamp_from, p.timestamp_to, p.interval, p.milliseconds, p.timestamp); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketVolumes error')); - } - } - - private getBisqMarketHloc(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'market': { - required: true, - types: ['@string'] - }, - 'interval': { - required: false, - types: ['minute', 'half_hour', 'hour', 'half_day', 'day', 'week', 'month', 'year', 'auto'] - }, - 'timestamp_from': { - required: false, - types: ['@number'] - }, - 'timestamp_to': { - required: false, - types: ['@number'] - }, - 'milliseconds': { - required: false, - types: ['@boolean'] - }, - 'timestamp': { - required: false, - types: ['no', 'yes'] - }, - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getHloc(p.market, p.interval, p.timestamp_from, p.timestamp_to, p.milliseconds, p.timestamp); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketHloc error')); - } - } - - private getBisqMarketTicker(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'market': { - required: false, - types: ['@string'] - }, - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getTicker(p.market); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketTicker error')); - } - } - - private getBisqMarketVolumes7d(req: Request, res: Response) { - const result = marketsApi.getVolumesByTime(604800); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketVolumes7d error')); - } - } - - private parseRequestParameters(requestParams: object, params: RequiredSpec): { [name: string]: any; } { - const final = {}; - for (const i in params) { - if (params.hasOwnProperty(i)) { - if (params[i].required && requestParams[i] === undefined) { - return { error: i + ' parameter missing'}; - } - if (typeof requestParams[i] === 'string') { - const str = (requestParams[i] || '').toString().toLowerCase(); - if (params[i].types.indexOf('@number') > -1) { - const number = parseInt((str).toString(), 10); - final[i] = number; - } else if (params[i].types.indexOf('@string') > -1) { - final[i] = str; - } else if (params[i].types.indexOf('@boolean') > -1) { - final[i] = str === 'true' || str === 'yes'; - } else if (params[i].types.indexOf(str) > -1) { - final[i] = str; - } else { - return { error: i + ' parameter invalid'}; - } - } else if (typeof requestParams[i] === 'number') { - final[i] = requestParams[i]; - } - } - } - return final; - } - - private getBisqMarketErrorResponse(message: string): MarketsApiError { - return { - 'success': 0, - 'error': message - }; - } - -} - -export default new BisqRoutes; diff --git a/backend/src/api/bisq/bisq.ts b/backend/src/api/bisq/bisq.ts deleted file mode 100644 index 4171284bb..000000000 --- a/backend/src/api/bisq/bisq.ts +++ /dev/null @@ -1,359 +0,0 @@ -import config from '../../config'; -import * as fs from 'fs'; -import axios, { AxiosResponse } from 'axios'; -import * as http from 'http'; -import * as https from 'https'; -import { SocksProxyAgent } from 'socks-proxy-agent'; -import { BisqBlocks, BisqBlock, BisqTransaction, BisqStats, BisqTrade } from './interfaces'; -import { Common } from '../common'; -import { BlockExtended } from '../../mempool.interfaces'; -import backendInfo from '../backend-info'; -import logger from '../../logger'; - -class Bisq { - private static BLOCKS_JSON_FILE_PATH = config.BISQ.DATA_PATH + '/json/all/blocks.json'; - private latestBlockHeight = 0; - private blocks: BisqBlock[] = []; - private allBlocks: BisqBlock[] = []; - private transactions: BisqTransaction[] = []; - private transactionIndex: { [txId: string]: BisqTransaction } = {}; - private blockIndex: { [hash: string]: BisqBlock } = {}; - private addressIndex: { [address: string]: BisqTransaction[] } = {}; - private stats: BisqStats = { - minted: 0, - burnt: 0, - addresses: 0, - unspent_txos: 0, - spent_txos: 0, - }; - private price: number = 0; - private priceUpdateCallbackFunction: ((price: number) => void) | undefined; - private topDirectoryWatcher: fs.FSWatcher | undefined; - private subdirectoryWatcher: fs.FSWatcher | undefined; - - constructor() {} - - startBisqService(): void { - try { - this.checkForBisqDataFolder(); - } catch (e) { - logger.info('Retrying to start bisq service in 3 minutes'); - setTimeout(this.startBisqService.bind(this), 180000); - return; - } - this.loadBisqDumpFile(); - setInterval(this.updatePrice.bind(this), 1000 * 60 * 60); - this.updatePrice(); - this.startTopDirectoryWatcher(); - this.startSubDirectoryWatcher(); - } - - handleNewBitcoinBlock(block: BlockExtended): void { - if (block.height - 10 > this.latestBlockHeight && this.latestBlockHeight !== 0) { - logger.warn(`Bitcoin block height (#${block.height}) has diverged from the latest Bisq block height (#${this.latestBlockHeight}). Restarting watchers...`); - this.startTopDirectoryWatcher(); - this.startSubDirectoryWatcher(); - } - } - - getTransaction(txId: string): BisqTransaction | undefined { - return this.transactionIndex[txId]; - } - - getTransactions(start: number, length: number, types: string[]): [BisqTransaction[], number] { - let transactions = this.transactions; - if (types.length) { - transactions = transactions.filter((tx) => types.indexOf(tx.txType) > -1); - } - return [transactions.slice(start, length + start), transactions.length]; - } - - getBlock(hash: string): BisqBlock | undefined { - return this.blockIndex[hash]; - } - - getAddress(hash: string): BisqTransaction[] { - return this.addressIndex[hash]; - } - - getBlocks(start: number, length: number): [BisqBlock[], number] { - return [this.blocks.slice(start, length + start), this.blocks.length]; - } - - getStats(): BisqStats { - return this.stats; - } - - setPriceCallbackFunction(fn: (price: number) => void) { - this.priceUpdateCallbackFunction = fn; - } - - getLatestBlockHeight(): number { - return this.latestBlockHeight; - } - - private checkForBisqDataFolder() { - if (!fs.existsSync(Bisq.BLOCKS_JSON_FILE_PATH)) { - logger.warn(Bisq.BLOCKS_JSON_FILE_PATH + ` doesn't exist. Make sure Bisq is running and the config is correct before starting the server.`); - throw new Error(`Cannot load BISQ ${Bisq.BLOCKS_JSON_FILE_PATH} file`); - } - } - - private startTopDirectoryWatcher() { - if (this.topDirectoryWatcher) { - this.topDirectoryWatcher.close(); - } - let fsWait: NodeJS.Timeout | null = null; - this.topDirectoryWatcher = fs.watch(config.BISQ.DATA_PATH + '/json', () => { - if (fsWait) { - clearTimeout(fsWait); - } - if (this.subdirectoryWatcher) { - this.subdirectoryWatcher.close(); - } - fsWait = setTimeout(() => { - logger.debug(`Bisq restart detected. Resetting both watchers in 3 minutes.`); - setTimeout(() => { - this.startTopDirectoryWatcher(); - this.startSubDirectoryWatcher(); - this.loadBisqDumpFile(); - }, 180000); - }, 15000); - }); - } - - private startSubDirectoryWatcher() { - if (this.subdirectoryWatcher) { - this.subdirectoryWatcher.close(); - } - if (!fs.existsSync(Bisq.BLOCKS_JSON_FILE_PATH)) { - logger.warn(Bisq.BLOCKS_JSON_FILE_PATH + ` doesn't exist. Trying to restart sub directory watcher again in 3 minutes.`); - setTimeout(() => this.startSubDirectoryWatcher(), 180000); - return; - } - let fsWait: NodeJS.Timeout | null = null; - this.subdirectoryWatcher = fs.watch(config.BISQ.DATA_PATH + '/json/all', () => { - if (fsWait) { - clearTimeout(fsWait); - } - fsWait = setTimeout(() => { - logger.debug(`Change detected in the Bisq data folder.`); - this.loadBisqDumpFile(); - }, 2000); - }); - } - private async updatePrice() { - type axiosOptions = { - headers: { - 'User-Agent': string - }; - timeout: number; - httpAgent?: http.Agent; - httpsAgent?: https.Agent; - } - const setDelay = (secs: number = 1): Promise => new Promise(resolve => setTimeout(() => resolve(), secs * 1000)); - const BISQ_URL = (config.SOCKS5PROXY.ENABLED === true) && (config.SOCKS5PROXY.USE_ONION === true) ? config.EXTERNAL_DATA_SERVER.BISQ_ONION : config.EXTERNAL_DATA_SERVER.BISQ_URL; - const isHTTP = (new URL(BISQ_URL).protocol.split(':')[0] === 'http') ? true : false; - const axiosOptions: axiosOptions = { - headers: { - 'User-Agent': (config.MEMPOOL.USER_AGENT === 'mempool') ? `mempool/v${backendInfo.getBackendInfo().version}` : `${config.MEMPOOL.USER_AGENT}` - }, - timeout: config.SOCKS5PROXY.ENABLED ? 30000 : 10000 - }; - let retry = 0; - - while(retry < config.MEMPOOL.EXTERNAL_MAX_RETRY) { - try { - if (config.SOCKS5PROXY.ENABLED) { - const socksOptions: any = { - agentOptions: { - keepAlive: true, - }, - hostname: config.SOCKS5PROXY.HOST, - port: config.SOCKS5PROXY.PORT - }; - - if (config.SOCKS5PROXY.USERNAME && config.SOCKS5PROXY.PASSWORD) { - socksOptions.username = config.SOCKS5PROXY.USERNAME; - socksOptions.password = config.SOCKS5PROXY.PASSWORD; - } else { - // Retry with different tor circuits https://stackoverflow.com/a/64960234 - socksOptions.username = `circuit${retry}`; - } - - // Handle proxy agent for onion addresses - if (isHTTP) { - axiosOptions.httpAgent = new SocksProxyAgent(socksOptions); - } else { - axiosOptions.httpsAgent = new SocksProxyAgent(socksOptions); - } - } - - const data: AxiosResponse = await axios.get(`${BISQ_URL}/trades/?market=bsq_btc`, axiosOptions); - if (data.statusText === 'error' || !data.data) { - throw new Error(`Could not fetch data from Bisq market, Error: ${data.status}`); - } - const prices: number[] = []; - data.data.forEach((trade) => { - prices.push(parseFloat(trade.price) * 100000000); - }); - prices.sort((a, b) => a - b); - this.price = Common.median(prices); - if (this.priceUpdateCallbackFunction) { - this.priceUpdateCallbackFunction(this.price); - } - logger.debug('Successfully updated Bisq market price'); - break; - } catch (e) { - logger.err('Error updating Bisq market price: ' + (e instanceof Error ? e.message : e)); - await setDelay(config.MEMPOOL.EXTERNAL_RETRY_INTERVAL); - retry++; - } - } - } - - private async loadBisqDumpFile(): Promise { - this.allBlocks = []; - try { - await this.loadData(); - this.buildIndex(); - this.calculateStats(); - } catch (e) { - logger.info('Cannot load bisq dump file because: ' + (e instanceof Error ? e.message : e)); - } - } - - private buildIndex() { - const start = new Date().getTime(); - this.transactions = []; - this.transactionIndex = {}; - this.addressIndex = {}; - - this.allBlocks.forEach((block) => { - /* Build block index */ - if (!this.blockIndex[block.hash]) { - this.blockIndex[block.hash] = block; - } - - /* Build transactions index */ - block.txs.forEach((tx) => { - this.transactions.push(tx); - this.transactionIndex[tx.id] = tx; - }); - }); - - /* Build address index */ - this.transactions.forEach((tx) => { - tx.inputs.forEach((input) => { - if (!this.addressIndex[input.address]) { - this.addressIndex[input.address] = []; - } - if (this.addressIndex[input.address].indexOf(tx) === -1) { - this.addressIndex[input.address].push(tx); - } - }); - tx.outputs.forEach((output) => { - if (!this.addressIndex[output.address]) { - this.addressIndex[output.address] = []; - } - if (this.addressIndex[output.address].indexOf(tx) === -1) { - this.addressIndex[output.address].push(tx); - } - }); - }); - - const time = new Date().getTime() - start; - logger.debug('Bisq data index rebuilt in ' + time + ' ms'); - } - - private calculateStats() { - let minted = 0; - let burned = 0; - let unspent = 0; - let spent = 0; - - this.transactions.forEach((tx) => { - tx.outputs.forEach((output) => { - if (output.opReturn) { - return; - } - if (output.txOutputType === 'GENESIS_OUTPUT' || output.txOutputType === 'ISSUANCE_CANDIDATE_OUTPUT' && output.isVerified) { - minted += output.bsqAmount; - } - if (output.isUnspent) { - unspent++; - } else { - spent++; - } - }); - burned += tx['burntFee']; - }); - - this.stats = { - addresses: Object.keys(this.addressIndex).length, - minted: minted / 100, - burnt: burned / 100, - spent_txos: spent, - unspent_txos: unspent, - }; - } - - private async loadData(): Promise { - if (!fs.existsSync(Bisq.BLOCKS_JSON_FILE_PATH)) { - throw new Error(Bisq.BLOCKS_JSON_FILE_PATH + ` doesn't exist`); - } - - const readline = require('readline'); - const events = require('events'); - - const rl = readline.createInterface({ - input: fs.createReadStream(Bisq.BLOCKS_JSON_FILE_PATH), - crlfDelay: Infinity - }); - - let blockBuffer = ''; - let readingBlock = false; - let lineCount = 1; - const start = new Date().getTime(); - - logger.debug('Processing Bisq data dump...'); - - rl.on('line', (line) => { - if (lineCount === 2) { - line = line.replace(' "chainHeight": ', ''); - this.latestBlockHeight = parseInt(line, 10); - } - - if (line === ' {') { - readingBlock = true; - } else if (line === ' },') { - blockBuffer += '}'; - try { - const block: BisqBlock = JSON.parse(blockBuffer); - this.allBlocks.push(block); - readingBlock = false; - blockBuffer = ''; - } catch (e) { - logger.debug(blockBuffer); - throw Error(`Unable to parse Bisq data dump at line ${lineCount}` + (e instanceof Error ? e.message : e)); - } - } - - if (readingBlock === true) { - blockBuffer += line; - } - - ++lineCount; - }); - - await events.once(rl, 'close'); - - this.allBlocks.reverse(); - this.blocks = this.allBlocks.filter((block) => block.txs.length > 0); - - const time = new Date().getTime() - start; - logger.debug('Bisq dump processed in ' + time + ' ms'); - } -} - -export default new Bisq(); diff --git a/backend/src/api/bisq/interfaces.ts b/backend/src/api/bisq/interfaces.ts deleted file mode 100644 index eb10d2fa7..000000000 --- a/backend/src/api/bisq/interfaces.ts +++ /dev/null @@ -1,258 +0,0 @@ - -export interface BisqBlocks { - chainHeight: number; - blocks: BisqBlock[]; -} - -export interface BisqBlock { - height: number; - time: number; - hash: string; - previousBlockHash: string; - txs: BisqTransaction[]; -} - -export interface BisqTransaction { - txVersion: string; - id: string; - blockHeight: number; - blockHash: string; - time: number; - inputs: BisqInput[]; - outputs: BisqOutput[]; - txType: string; - txTypeDisplayString: string; - burntFee: number; - invalidatedBsq: number; - unlockBlockHeight: number; -} - -export interface BisqStats { - minted: number; - burnt: number; - addresses: number; - unspent_txos: number; - spent_txos: number; -} - -interface BisqInput { - spendingTxOutputIndex: number; - spendingTxId: string; - bsqAmount: number; - isVerified: boolean; - address: string; - time: number; -} - -interface BisqOutput { - txVersion: string; - txId: string; - index: number; - bsqAmount: number; - btcAmount: number; - height: number; - isVerified: boolean; - burntFee: number; - invalidatedBsq: number; - address: string; - scriptPubKey: BisqScriptPubKey; - time: any; - txType: string; - txTypeDisplayString: string; - txOutputType: string; - txOutputTypeDisplayString: string; - lockTime: number; - isUnspent: boolean; - spentInfo: SpentInfo; - opReturn?: string; -} - -interface BisqScriptPubKey { - addresses: string[]; - asm: string; - hex: string; - reqSigs?: number; - type: string; -} - -interface SpentInfo { - height: number; - inputIndex: number; - txId: string; -} - -export interface BisqTrade { - direction: string; - price: string; - amount: string; - volume: string; - payment_method: string; - trade_id: string; - trade_date: number; - market?: string; -} - -export interface Currencies { [txid: string]: Currency; } - -export interface Currency { - code: string; - name: string; - precision: number; - - _type: string; -} - -export interface Depth { [market: string]: Market; } - -interface Market { - 'buys': string[]; - 'sells': string[]; -} - -export interface HighLowOpenClose { - period_start: number | string; - open: string; - high: string; - low: string; - close: string; - volume_left: string; - volume_right: string; - avg: string; -} - -export interface Markets { [txid: string]: Pair; } - -interface Pair { - pair: string; - lname: string; - rname: string; - lsymbol: string; - rsymbol: string; - lprecision: number; - rprecision: number; - ltype: string; - rtype: string; - name: string; -} - -export interface Offers { [market: string]: OffersMarket; } - -interface OffersMarket { - buys: Offer[] | null; - sells: Offer[] | null; -} - -export interface OffersData { - direction: string; - currencyCode: string; - minAmount: number; - amount: number; - price: number; - date: number; - useMarketBasedPrice: boolean; - marketPriceMargin: number; - paymentMethod: string; - id: string; - currencyPair: string; - primaryMarketDirection: string; - priceDisplayString: string; - primaryMarketAmountDisplayString: string; - primaryMarketMinAmountDisplayString: string; - primaryMarketVolumeDisplayString: string; - primaryMarketMinVolumeDisplayString: string; - primaryMarketPrice: number; - primaryMarketAmount: number; - primaryMarketMinAmount: number; - primaryMarketVolume: number; - primaryMarketMinVolume: number; -} - -export interface Offer { - offer_id: string; - offer_date: number; - direction: string; - min_amount: string; - amount: string; - price: string; - volume: string; - payment_method: string; - offer_fee_txid: any; -} - -export interface Tickers { [market: string]: Ticker | null; } - -export interface Ticker { - last: string; - high: string; - low: string; - volume_left: string; - volume_right: string; - buy: string | null; - sell: string | null; -} - -export interface Trade { - direction: string; - price: string; - amount: string; - volume: string; - payment_method: string; - trade_id: string; - trade_date: number; -} - -export interface TradesData { - currency: string; - direction: string; - tradePrice: number; - tradeAmount: number; - tradeDate: number; - paymentMethod: string; - offerDate: number; - useMarketBasedPrice: boolean; - marketPriceMargin: number; - offerAmount: number; - offerMinAmount: number; - offerId: string; - depositTxId?: string; - currencyPair: string; - primaryMarketDirection: string; - primaryMarketTradePrice: number; - primaryMarketTradeAmount: number; - primaryMarketTradeVolume: number; - - _market: string; - _tradePriceStr: string; - _tradeAmountStr: string; - _tradeVolumeStr: string; - _offerAmountStr: string; - _tradePrice: number; - _tradeAmount: number; - _tradeVolume: number; - _offerAmount: number; -} - -export interface MarketVolume { - period_start: number; - num_trades: number; - volume: string; -} - -export interface MarketsApiError { - success: number; - error: string; -} - -export type Interval = 'minute' | 'half_hour' | 'hour' | 'half_day' | 'day' | 'week' | 'month' | 'year' | 'auto'; - -export interface SummarizedIntervals { [market: string]: SummarizedInterval; } -export interface SummarizedInterval { - 'period_start': number; - 'open': number; - 'close': number; - 'high': number; - 'low': number; - 'avg': number; - 'volume_right': number; - 'volume_left': number; -} diff --git a/backend/src/api/bisq/markets-api.ts b/backend/src/api/bisq/markets-api.ts deleted file mode 100644 index 54e0297b7..000000000 --- a/backend/src/api/bisq/markets-api.ts +++ /dev/null @@ -1,679 +0,0 @@ -import { Currencies, OffersData, TradesData, Depth, Currency, Interval, HighLowOpenClose, - Markets, Offers, Offer, BisqTrade, MarketVolume, Tickers, Ticker, SummarizedIntervals, SummarizedInterval } from './interfaces'; - -const strtotime = require('./strtotime'); - -class BisqMarketsApi { - private cryptoCurrencyData: Currency[] = []; - private fiatCurrencyData: Currency[] = []; - private activeCryptoCurrencyData: Currency[] = []; - private activeFiatCurrencyData: Currency[] = []; - private offersData: OffersData[] = []; - private tradesData: TradesData[] = []; - private fiatCurrenciesIndexed: { [code: string]: true } = {}; - private allCurrenciesIndexed: { [code: string]: Currency } = {}; - private tradeDataByMarket: { [market: string]: TradesData[] } = {}; - private tickersCache: Ticker | Tickers | null = null; - - constructor() { } - - setOffersData(offers: OffersData[]) { - this.offersData = offers; - } - - setTradesData(trades: TradesData[]) { - this.tradesData = trades; - this.tradeDataByMarket = {}; - - this.tradesData.forEach((trade) => { - trade._market = trade.currencyPair.toLowerCase().replace('/', '_'); - if (!this.tradeDataByMarket[trade._market]) { - this.tradeDataByMarket[trade._market] = []; - } - this.tradeDataByMarket[trade._market].push(trade); - }); - } - - setCurrencyData(cryptoCurrency: Currency[], fiatCurrency: Currency[], activeCryptoCurrency: Currency[], activeFiatCurrency: Currency[]) { - this.cryptoCurrencyData = cryptoCurrency, - this.fiatCurrencyData = fiatCurrency, - this.activeCryptoCurrencyData = activeCryptoCurrency, - this.activeFiatCurrencyData = activeFiatCurrency; - - this.fiatCurrenciesIndexed = {}; - this.allCurrenciesIndexed = {}; - - this.fiatCurrencyData.forEach((currency) => { - currency._type = 'fiat'; - this.fiatCurrenciesIndexed[currency.code] = true; - this.allCurrenciesIndexed[currency.code] = currency; - }); - this.cryptoCurrencyData.forEach((currency) => { - currency._type = 'crypto'; - this.allCurrenciesIndexed[currency.code] = currency; - }); - } - - updateCache() { - this.tickersCache = null; - this.tickersCache = this.getTicker(); - } - - getCurrencies( - type: 'crypto' | 'fiat' | 'active' | 'all' = 'all', - ): Currencies { - let currencies: Currency[]; - - switch (type) { - case 'fiat': - currencies = this.fiatCurrencyData; - break; - case 'crypto': - currencies = this.cryptoCurrencyData; - break; - case 'active': - currencies = this.activeCryptoCurrencyData.concat(this.activeFiatCurrencyData); - break; - case 'all': - default: - currencies = this.cryptoCurrencyData.concat(this.fiatCurrencyData); - } - const result = {}; - currencies.forEach((currency) => { - result[currency.code] = currency; - }); - return result; - } - - getDepth( - market: string, - ): Depth { - const currencyPair = market.replace('_', '/').toUpperCase(); - - const buys = this.offersData - .filter((offer) => offer.currencyPair === currencyPair && offer.primaryMarketDirection === 'BUY') - .map((offer) => offer.price) - .sort((a, b) => b - a) - .map((price) => this.intToBtc(price)); - - const sells = this.offersData - .filter((offer) => offer.currencyPair === currencyPair && offer.primaryMarketDirection === 'SELL') - .map((offer) => offer.price) - .sort((a, b) => a - b) - .map((price) => this.intToBtc(price)); - - const result = {}; - result[market] = { - 'buys': buys, - 'sells': sells, - }; - return result; - } - - getOffers( - market: string, - direction?: 'buy' | 'sell', - ): Offers { - const currencyPair = market.replace('_', '/').toUpperCase(); - - let buys: Offer[] | null = null; - let sells: Offer[] | null = null; - - if (!direction || direction === 'buy') { - buys = this.offersData - .filter((offer) => offer.currencyPair === currencyPair && offer.primaryMarketDirection === 'BUY') - .sort((a, b) => b.price - a.price) - .map((offer) => this.offerDataToOffer(offer, market)); - } - - if (!direction || direction === 'sell') { - sells = this.offersData - .filter((offer) => offer.currencyPair === currencyPair && offer.primaryMarketDirection === 'SELL') - .sort((a, b) => a.price - b.price) - .map((offer) => this.offerDataToOffer(offer, market)); - } - - const result: Offers = {}; - result[market] = { - 'buys': buys, - 'sells': sells, - }; - return result; - } - - getMarkets(): Markets { - const allCurrencies = this.getCurrencies(); - const activeCurrencies = this.getCurrencies('active'); - const markets = {}; - - for (const currency of Object.keys(activeCurrencies)) { - if (allCurrencies[currency].code === 'BTC') { - continue; - } - - const isFiat = allCurrencies[currency]._type === 'fiat'; - const pmarketname = allCurrencies['BTC']['name']; - - const lsymbol = isFiat ? 'BTC' : currency; - const rsymbol = isFiat ? currency : 'BTC'; - const lname = isFiat ? pmarketname : allCurrencies[currency].name; - const rname = isFiat ? allCurrencies[currency].name : pmarketname; - const ltype = isFiat ? 'crypto' : allCurrencies[currency]._type; - const rtype = isFiat ? 'fiat' : 'crypto'; - const lprecision = 8; - const rprecision = isFiat ? 2 : 8; - const pair = lsymbol.toLowerCase() + '_' + rsymbol.toLowerCase(); - - markets[pair] = { - 'pair': pair, - 'lname': lname, - 'rname': rname, - 'lsymbol': lsymbol, - 'rsymbol': rsymbol, - 'lprecision': lprecision, - 'rprecision': rprecision, - 'ltype': ltype, - 'rtype': rtype, - 'name': lname + '/' + rname, - }; - } - - return markets; - } - - getTrades( - market: string, - timestamp_from?: number, - timestamp_to?: number, - trade_id_from?: string, - trade_id_to?: string, - direction?: 'buy' | 'sell', - limit: number = 100, - sort: 'asc' | 'desc' = 'desc', - ): BisqTrade[] { - limit = Math.min(limit, 2000); - const _market = market === 'all' ? undefined : market; - - if (!timestamp_from) { - timestamp_from = new Date('2016-01-01').getTime() / 1000; - } - if (!timestamp_to) { - timestamp_to = new Date().getTime() / 1000; - } - - const matches = this.getTradesByCriteria(_market, timestamp_to, timestamp_from, - trade_id_to, trade_id_from, direction, sort, limit, false); - - if (sort === 'asc') { - matches.sort((a, b) => a.tradeDate - b.tradeDate); - } else { - matches.sort((a, b) => b.tradeDate - a.tradeDate); - } - - return matches.map((trade) => { - const bsqTrade: BisqTrade = { - direction: trade.primaryMarketDirection, - price: trade._tradePriceStr, - amount: trade._tradeAmountStr, - volume: trade._tradeVolumeStr, - payment_method: trade.paymentMethod, - trade_id: trade.offerId, - trade_date: trade.tradeDate, - }; - if (market === 'all') { - bsqTrade.market = trade._market; - } - return bsqTrade; - }); - } - - getVolumes( - market?: string, - timestamp_from?: number, - timestamp_to?: number, - interval: Interval = 'auto', - milliseconds?: boolean, - timestamp: 'no' | 'yes' = 'yes', - ): MarketVolume[] { - if (milliseconds) { - timestamp_from = timestamp_from ? timestamp_from / 1000 : timestamp_from; - timestamp_to = timestamp_to ? timestamp_to / 1000 : timestamp_to; - } - if (!timestamp_from) { - timestamp_from = new Date('2016-01-01').getTime() / 1000; - } - if (!timestamp_to) { - timestamp_to = new Date().getTime() / 1000; - } - - const trades = this.getTradesByCriteria(market, timestamp_to, timestamp_from, - undefined, undefined, undefined, 'asc', Number.MAX_SAFE_INTEGER); - - if (interval === 'auto') { - const range = timestamp_to - timestamp_from; - interval = this.getIntervalFromRange(range); - } - - const intervals: any = {}; - const marketVolumes: MarketVolume[] = []; - - for (const trade of trades) { - const traded_at = trade['tradeDate'] / 1000; - const interval_start = this.intervalStart(traded_at, interval); - - if (!intervals[interval_start]) { - intervals[interval_start] = { - 'volume': 0, - 'num_trades': 0, - }; - } - - const period = intervals[interval_start]; - period['period_start'] = interval_start; - period['volume'] += this.fiatCurrenciesIndexed[trade.currency] ? trade._tradeAmount : trade._tradeVolume; - period['num_trades']++; - } - - for (const p in intervals) { - if (intervals.hasOwnProperty(p)) { - const period = intervals[p]; - marketVolumes.push({ - period_start: timestamp === 'no' ? new Date(period['period_start'] * 1000).toISOString() : period['period_start'], - num_trades: period['num_trades'], - volume: this.intToBtc(period['volume']), - }); - } - } - - return marketVolumes; - } - - getTicker( - market?: string, - ): Tickers | Ticker | null { - if (market) { - return this.getTickerFromMarket(market); - } - - if (this.tickersCache) { - return this.tickersCache; - } - - const allMarkets = this.getMarkets(); - const tickers = {}; - for (const m in allMarkets) { - if (allMarkets.hasOwnProperty(m)) { - tickers[allMarkets[m].pair] = this.getTickerFromMarket(allMarkets[m].pair); - } - } - - return tickers; - } - - getTickerFromMarket(market: string): Ticker | null { - let ticker: Ticker; - const timestamp_from = strtotime('-24 hour'); - const timestamp_to = new Date().getTime() / 1000; - const trades = this.getTradesByCriteria(market, timestamp_to, timestamp_from, - undefined, undefined, undefined, 'asc', Number.MAX_SAFE_INTEGER); - - const periods: SummarizedInterval[] = Object.values(this.getTradesSummarized(trades, timestamp_from)); - - const allCurrencies = this.getCurrencies(); - const currencyRight = allCurrencies[market.split('_')[1].toUpperCase()]; - - if (periods[0]) { - ticker = { - 'last': this.intToBtc(periods[0].close), - 'high': this.intToBtc(periods[0].high), - 'low': this.intToBtc(periods[0].low), - 'volume_left': this.intToBtc(periods[0].volume_left), - 'volume_right': this.intToBtc(periods[0].volume_right), - 'buy': null, - 'sell': null, - }; - } else { - const lastTrade = this.tradeDataByMarket[market]; - if (!lastTrade) { - return null; - } - const tradePrice = lastTrade[0].primaryMarketTradePrice * Math.pow(10, 8 - currencyRight.precision); - - const lastTradePrice = this.intToBtc(tradePrice); - ticker = { - 'last': lastTradePrice, - 'high': lastTradePrice, - 'low': lastTradePrice, - 'volume_left': '0', - 'volume_right': '0', - 'buy': null, - 'sell': null, - }; - } - - const timestampFromMilli = timestamp_from * 1000; - const timestampToMilli = timestamp_to * 1000; - - const currencyPair = market.replace('_', '/').toUpperCase(); - const offersData = this.offersData.slice().sort((a, b) => a.price - b.price); - - const buy = offersData.find((offer) => offer.currencyPair === currencyPair - && offer.primaryMarketDirection === 'BUY' - && offer.date >= timestampFromMilli - && offer.date <= timestampToMilli - ); - const sell = offersData.find((offer) => offer.currencyPair === currencyPair - && offer.primaryMarketDirection === 'SELL' - && offer.date >= timestampFromMilli - && offer.date <= timestampToMilli - ); - - if (buy) { - ticker.buy = this.intToBtc(buy.primaryMarketPrice * Math.pow(10, 8 - currencyRight.precision)); - } - if (sell) { - ticker.sell = this.intToBtc(sell.primaryMarketPrice * Math.pow(10, 8 - currencyRight.precision)); - } - - return ticker; - } - - getHloc( - market: string, - interval: Interval = 'auto', - timestamp_from?: number, - timestamp_to?: number, - milliseconds?: boolean, - timestamp: 'no' | 'yes' = 'yes', - ): HighLowOpenClose[] { - if (milliseconds) { - timestamp_from = timestamp_from ? timestamp_from / 1000 : timestamp_from; - timestamp_to = timestamp_to ? timestamp_to / 1000 : timestamp_to; - } - if (!timestamp_from) { - timestamp_from = new Date('2016-01-01').getTime() / 1000; - } - if (!timestamp_to) { - timestamp_to = new Date().getTime() / 1000; - } - - const trades = this.getTradesByCriteria(market, timestamp_to, timestamp_from, - undefined, undefined, undefined, 'asc', Number.MAX_SAFE_INTEGER); - - if (interval === 'auto') { - const range = timestamp_to - timestamp_from; - interval = this.getIntervalFromRange(range); - } - - const intervals = this.getTradesSummarized(trades, timestamp_from, interval); - - const hloc: HighLowOpenClose[] = []; - - for (const p in intervals) { - if (intervals.hasOwnProperty(p)) { - const period = intervals[p]; - hloc.push({ - period_start: timestamp === 'no' ? new Date(period['period_start'] * 1000).toISOString() : period['period_start'], - open: this.intToBtc(period['open']), - close: this.intToBtc(period['close']), - high: this.intToBtc(period['high']), - low: this.intToBtc(period['low']), - avg: this.intToBtc(period['avg']), - volume_right: this.intToBtc(period['volume_right']), - volume_left: this.intToBtc(period['volume_left']), - }); - } - } - - return hloc; - } - - private getIntervalFromRange(range: number): Interval { - // two days range loads minute data - if (range <= 3600) { - // up to one hour range loads minutely data - return 'minute'; - } else if (range <= 1 * 24 * 3600) { - // up to one day range loads half-hourly data - return 'half_hour'; - } else if (range <= 3 * 24 * 3600) { - // up to 3 day range loads hourly data - return 'hour'; - } else if (range <= 7 * 24 * 3600) { - // up to 7 day range loads half-daily data - return 'half_day'; - } else if (range <= 60 * 24 * 3600) { - // up to 2 month range loads daily data - return 'day'; - } else if (range <= 12 * 31 * 24 * 3600) { - // up to one year range loads weekly data - return 'week'; - } else if (range <= 12 * 31 * 24 * 3600) { - // up to 5 year range loads monthly data - return 'month'; - } else { - // greater range loads yearly data - return 'year'; - } - } - - getVolumesByTime(time: number): MarketVolume[] { - const timestamp_from = new Date().getTime() / 1000 - time; - const timestamp_to = new Date().getTime() / 1000; - - const trades = this.getTradesByCriteria(undefined, timestamp_to, timestamp_from, - undefined, undefined, undefined, 'asc', Number.MAX_SAFE_INTEGER); - - const markets: any = {}; - - for (const trade of trades) { - if (!markets[trade._market]) { - markets[trade._market] = { - 'volume': 0, - 'num_trades': 0, - }; - } - - markets[trade._market]['volume'] += this.fiatCurrenciesIndexed[trade.currency] ? trade._tradeAmount : trade._tradeVolume; - markets[trade._market]['num_trades']++; - } - - return markets; - } - - private getTradesSummarized(trades: TradesData[], timestamp_from: number, interval?: string): SummarizedIntervals { - const intervals: any = {}; - const intervals_prices: any = {}; - - for (const trade of trades) { - const traded_at = trade.tradeDate / 1000; - const interval_start = !interval ? timestamp_from : this.intervalStart(traded_at, interval); - - if (!intervals[interval_start]) { - intervals[interval_start] = { - 'open': 0, - 'close': 0, - 'high': 0, - 'low': 0, - 'avg': 0, - 'volume_right': 0, - 'volume_left': 0, - }; - intervals_prices[interval_start] = []; - } - const period = intervals[interval_start]; - const price = trade._tradePrice; - - if (!intervals_prices[interval_start]['leftvol']) { - intervals_prices[interval_start]['leftvol'] = []; - } - if (!intervals_prices[interval_start]['rightvol']) { - intervals_prices[interval_start]['rightvol'] = []; - } - - intervals_prices[interval_start]['leftvol'].push(trade._tradeAmount); - intervals_prices[interval_start]['rightvol'].push(trade._tradeVolume); - - if (price) { - const plow = period['low']; - period['period_start'] = interval_start; - period['open'] = period['open'] || price; - period['close'] = price; - period['high'] = price > period['high'] ? price : period['high']; - period['low'] = (plow && price > plow) ? period['low'] : price; - period['avg'] = intervals_prices[interval_start]['rightvol'].reduce((p: number, c: number) => c + p, 0) - / intervals_prices[interval_start]['leftvol'].reduce((c: number, p: number) => c + p, 0) * 100000000; - period['volume_left'] += trade._tradeAmount; - period['volume_right'] += trade._tradeVolume; - } - } - return intervals; - } - - private getTradesByCriteria( - market: string | undefined, - timestamp_to: number, - timestamp_from: number, - trade_id_to: string | undefined, - trade_id_from: string | undefined, - direction: 'buy' | 'sell' | undefined, - sort: string, - limit: number, - integerAmounts: boolean = true, - ): TradesData[] { - let trade_id_from_ts: number | null = null; - let trade_id_to_ts: number | null = null; - const allCurrencies = this.getCurrencies(); - - const timestampFromMilli = timestamp_from * 1000; - const timestampToMilli = timestamp_to * 1000; - - // note: the offer_id_from/to depends on iterating over trades in - // descending chronological order. - const tradesDataSorted = this.tradesData.slice(); - if (sort === 'asc') { - tradesDataSorted.reverse(); - } - - let matches: TradesData[] = []; - for (const trade of tradesDataSorted) { - if (trade_id_from === trade.offerId) { - trade_id_from_ts = trade.tradeDate; - } - if (trade_id_to === trade.offerId) { - trade_id_to_ts = trade.tradeDate; - } - if (trade_id_to && trade_id_to_ts === null) { - continue; - } - if (trade_id_from && trade_id_from_ts != null && trade_id_from_ts !== trade.tradeDate) { - continue; - } - if (market && market !== trade._market) { - continue; - } - if (timestampFromMilli && timestampFromMilli > trade.tradeDate) { - continue; - } - if (timestampToMilli && timestampToMilli < trade.tradeDate) { - continue; - } - if (direction && direction !== trade.direction.toLowerCase()) { - continue; - } - - // Filter out bogus trades with BTC/BTC or XXX/XXX market. - // See github issue: https://github.com/bitsquare/bitsquare/issues/883 - const currencyPairs = trade.currencyPair.split('/'); - if (currencyPairs[0] === currencyPairs[1]) { - continue; - } - - const currencyLeft = allCurrencies[currencyPairs[0]]; - const currencyRight = allCurrencies[currencyPairs[1]]; - - if (!currencyLeft || !currencyRight) { - continue; - } - - const tradePrice = trade.primaryMarketTradePrice * Math.pow(10, 8 - currencyRight.precision); - const tradeAmount = trade.primaryMarketTradeAmount * Math.pow(10, 8 - currencyLeft.precision); - const tradeVolume = trade.primaryMarketTradeVolume * Math.pow(10, 8 - currencyRight.precision); - - if (integerAmounts) { - trade._tradePrice = tradePrice; - trade._tradeAmount = tradeAmount; - trade._tradeVolume = tradeVolume; - trade._offerAmount = trade.offerAmount; - } else { - trade._tradePriceStr = this.intToBtc(tradePrice); - trade._tradeAmountStr = this.intToBtc(tradeAmount); - trade._tradeVolumeStr = this.intToBtc(tradeVolume); - trade._offerAmountStr = this.intToBtc(trade.offerAmount); - } - - matches.push(trade); - - if (matches.length >= limit) { - break; - } - } - - if ((trade_id_from && !trade_id_from_ts) || (trade_id_to && !trade_id_to_ts)) { - matches = []; - } - return matches; - } - - private intervalStart(ts: number, interval: string): number { - switch (interval) { - case 'minute': - return (ts - (ts % 60)); - case '10_minute': - return (ts - (ts % 600)); - case 'half_hour': - return (ts - (ts % 1800)); - case 'hour': - return (ts - (ts % 3600)); - case 'half_day': - return (ts - (ts % (3600 * 12))); - case 'day': - return strtotime('midnight today', ts); - case 'week': - return strtotime('midnight sunday last week', ts); - case 'month': - return strtotime('midnight first day of this month', ts); - case 'year': - return strtotime('midnight first day of january', ts); - default: - throw new Error('Unsupported interval: ' + interval); - } -} - - private offerDataToOffer(offer: OffersData, market: string): Offer { - const currencyPairs = market.split('_'); - const currencyRight = this.allCurrenciesIndexed[currencyPairs[1].toUpperCase()]; - const currencyLeft = this.allCurrenciesIndexed[currencyPairs[0].toUpperCase()]; - const price = offer['primaryMarketPrice'] * Math.pow( 10, 8 - currencyRight['precision']); - const amount = offer['primaryMarketAmount'] * Math.pow( 10, 8 - currencyLeft['precision']); - const volume = offer['primaryMarketVolume'] * Math.pow( 10, 8 - currencyRight['precision']); - - return { - offer_id: offer.id, - offer_date: offer.date, - direction: offer.primaryMarketDirection, - min_amount: this.intToBtc(offer.minAmount), - amount: this.intToBtc(amount), - price: this.intToBtc(price), - volume: this.intToBtc(volume), - payment_method: offer.paymentMethod, - offer_fee_txid: null, - }; - } - - private intToBtc(val: number): string { - return (val / 100000000).toFixed(8); - } -} - -export default new BisqMarketsApi(); diff --git a/backend/src/api/bisq/markets.ts b/backend/src/api/bisq/markets.ts deleted file mode 100644 index 08f40d772..000000000 --- a/backend/src/api/bisq/markets.ts +++ /dev/null @@ -1,137 +0,0 @@ -import config from '../../config'; -import * as fs from 'fs'; -import { OffersData as OffersData, TradesData, Currency } from './interfaces'; -import bisqMarket from './markets-api'; -import logger from '../../logger'; - -class Bisq { - private static FOLDER_WATCH_CHANGE_DETECTION_DEBOUNCE = 4000; - private static MARKET_JSON_PATH = config.BISQ.DATA_PATH; - private static MARKET_JSON_FILE_PATHS = { - activeCryptoCurrency: '/active_crypto_currency_list.json', - activeFiatCurrency: '/active_fiat_currency_list.json', - cryptoCurrency: '/crypto_currency_list.json', - fiatCurrency: '/fiat_currency_list.json', - offers: '/offers_statistics.json', - trades: '/trade_statistics.json', - }; - - private cryptoCurrencyLastMtime = new Date('2016-01-01'); - private fiatCurrencyLastMtime = new Date('2016-01-01'); - private offersLastMtime = new Date('2016-01-01'); - private tradesLastMtime = new Date('2016-01-01'); - - private subdirectoryWatcher: fs.FSWatcher | undefined; - - constructor() {} - - startBisqService(): void { - try { - this.checkForBisqDataFolder(); - } catch (e) { - logger.info('Retrying to start bisq service (markets) in 3 minutes'); - setTimeout(this.startBisqService.bind(this), 180000); - return; - } - this.loadBisqDumpFile(); - this.startBisqDirectoryWatcher(); - } - - private checkForBisqDataFolder() { - if (!fs.existsSync(Bisq.MARKET_JSON_PATH + Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency)) { - logger.err(Bisq.MARKET_JSON_PATH + Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency + ` doesn't exist. Make sure Bisq is running and the config is correct before starting the server.`); - throw new Error(`Cannot load BISQ ${Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency} file`); - } - } - - private startBisqDirectoryWatcher() { - if (this.subdirectoryWatcher) { - this.subdirectoryWatcher.close(); - } - if (!fs.existsSync(Bisq.MARKET_JSON_PATH + Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency)) { - logger.warn(Bisq.MARKET_JSON_PATH + Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency + ` doesn't exist. Trying to restart sub directory watcher again in 3 minutes.`); - setTimeout(() => this.startBisqDirectoryWatcher(), 180000); - return; - } - let fsWait: NodeJS.Timeout | null = null; - this.subdirectoryWatcher = fs.watch(Bisq.MARKET_JSON_PATH, () => { - if (fsWait) { - clearTimeout(fsWait); - } - fsWait = setTimeout(() => { - logger.debug(`Change detected in the Bisq market data folder.`); - this.loadBisqDumpFile(); - }, Bisq.FOLDER_WATCH_CHANGE_DETECTION_DEBOUNCE); - }); - } - - private async loadBisqDumpFile(): Promise { - const start = new Date().getTime(); - try { - let marketsDataUpdated = false; - const cryptoMtime = this.getFileMtime(Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency); - const fiatMtime = this.getFileMtime(Bisq.MARKET_JSON_FILE_PATHS.fiatCurrency); - if (cryptoMtime > this.cryptoCurrencyLastMtime || fiatMtime > this.fiatCurrencyLastMtime) { - const cryptoCurrencyData = await this.loadData(Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency); - const fiatCurrencyData = await this.loadData(Bisq.MARKET_JSON_FILE_PATHS.fiatCurrency); - const activeCryptoCurrencyData = await this.loadData(Bisq.MARKET_JSON_FILE_PATHS.activeCryptoCurrency); - const activeFiatCurrencyData = await this.loadData(Bisq.MARKET_JSON_FILE_PATHS.activeFiatCurrency); - logger.debug('Updating Bisq Market Currency Data'); - bisqMarket.setCurrencyData(cryptoCurrencyData, fiatCurrencyData, activeCryptoCurrencyData, activeFiatCurrencyData); - if (cryptoMtime > this.cryptoCurrencyLastMtime) { - this.cryptoCurrencyLastMtime = cryptoMtime; - } - if (fiatMtime > this.fiatCurrencyLastMtime) { - this.fiatCurrencyLastMtime = fiatMtime; - } - marketsDataUpdated = true; - } - const offersMtime = this.getFileMtime(Bisq.MARKET_JSON_FILE_PATHS.offers); - if (offersMtime > this.offersLastMtime) { - const offersData = await this.loadData(Bisq.MARKET_JSON_FILE_PATHS.offers); - logger.debug('Updating Bisq Market Offers Data'); - bisqMarket.setOffersData(offersData); - this.offersLastMtime = offersMtime; - marketsDataUpdated = true; - } - const tradesMtime = this.getFileMtime(Bisq.MARKET_JSON_FILE_PATHS.trades); - if (tradesMtime > this.tradesLastMtime) { - const tradesData = await this.loadData(Bisq.MARKET_JSON_FILE_PATHS.trades); - logger.debug('Updating Bisq Market Trades Data'); - bisqMarket.setTradesData(tradesData); - this.tradesLastMtime = tradesMtime; - marketsDataUpdated = true; - } - if (marketsDataUpdated) { - bisqMarket.updateCache(); - const time = new Date().getTime() - start; - logger.debug('Bisq market data updated in ' + time + ' ms'); - } - } catch (e) { - logger.err('loadBisqMarketDataDumpFile() error.' + (e instanceof Error ? e.message : e)); - } - } - - private getFileMtime(path: string): Date { - const stats = fs.statSync(Bisq.MARKET_JSON_PATH + path); - return stats.mtime; - } - - private loadData(path: string): Promise { - return new Promise((resolve, reject) => { - fs.readFile(Bisq.MARKET_JSON_PATH + path, 'utf8', (err, data) => { - if (err) { - reject(err); - } - try { - const parsedData = JSON.parse(data); - resolve(parsedData); - } catch (e) { - reject('JSON parse error (' + path + ')'); - } - }); - }); - } -} - -export default new Bisq(); diff --git a/backend/src/api/bisq/strtotime.ts b/backend/src/api/bisq/strtotime.ts deleted file mode 100644 index 912f00ec9..000000000 --- a/backend/src/api/bisq/strtotime.ts +++ /dev/null @@ -1,1375 +0,0 @@ -// @ts-nocheck - -/* -Copyright (c) 2007-2016 Kevin van Zonneveld (https://kvz.io) -and Contributors (https://locutus.io/authors) - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/* - -https://github.com/locutusjs/locutus/blob/master/src/php/datetime/strtotime.js - -*/ - -const reSpace = '[ \\t]+' -const reSpaceOpt = '[ \\t]*' -const reMeridian = '(?:([ap])\\.?m\\.?([\\t ]|$))' -const reHour24 = '(2[0-4]|[01]?[0-9])' -const reHour24lz = '([01][0-9]|2[0-4])' -const reHour12 = '(0?[1-9]|1[0-2])' -const reMinute = '([0-5]?[0-9])' -const reMinutelz = '([0-5][0-9])' -const reSecond = '(60|[0-5]?[0-9])' -const reSecondlz = '(60|[0-5][0-9])' -const reFrac = '(?:\\.([0-9]+))' - -const reDayfull = 'sunday|monday|tuesday|wednesday|thursday|friday|saturday' -const reDayabbr = 'sun|mon|tue|wed|thu|fri|sat' -const reDaytext = reDayfull + '|' + reDayabbr + '|weekdays?' - -const reReltextnumber = 'first|second|third|fourth|fifth|sixth|seventh|eighth?|ninth|tenth|eleventh|twelfth' -const reReltexttext = 'next|last|previous|this' -const reReltextunit = '(?:second|sec|minute|min|hour|day|fortnight|forthnight|month|year)s?|weeks|' + reDaytext - -const reYear = '([0-9]{1,4})' -const reYear2 = '([0-9]{2})' -const reYear4 = '([0-9]{4})' -const reYear4withSign = '([+-]?[0-9]{4})' -const reMonth = '(1[0-2]|0?[0-9])' -const reMonthlz = '(0[0-9]|1[0-2])' -const reDay = '(?:(3[01]|[0-2]?[0-9])(?:st|nd|rd|th)?)' -const reDaylz = '(0[0-9]|[1-2][0-9]|3[01])' - -const reMonthFull = 'january|february|march|april|may|june|july|august|september|october|november|december' -const reMonthAbbr = 'jan|feb|mar|apr|may|jun|jul|aug|sept?|oct|nov|dec' -const reMonthroman = 'i[vx]|vi{0,3}|xi{0,2}|i{1,3}' -const reMonthText = '(' + reMonthFull + '|' + reMonthAbbr + '|' + reMonthroman + ')' - -const reTzCorrection = '((?:GMT)?([+-])' + reHour24 + ':?' + reMinute + '?)' -const reTzAbbr = '\\(?([a-zA-Z]{1,6})\\)?' -const reDayOfYear = '(00[1-9]|0[1-9][0-9]|[12][0-9][0-9]|3[0-5][0-9]|36[0-6])' -const reWeekOfYear = '(0[1-9]|[1-4][0-9]|5[0-3])' - -const reDateNoYear = reMonthText + '[ .\\t-]*' + reDay + '[,.stndrh\\t ]*' - -function processMeridian (hour, meridian) { - meridian = meridian && meridian.toLowerCase() - - switch (meridian) { - case 'a': - hour += hour === 12 ? -12 : 0 - break - case 'p': - hour += hour !== 12 ? 12 : 0 - break - } - - return hour -} - -function processYear (yearStr) { - let year = +yearStr - - if (yearStr.length < 4 && year < 100) { - year += year < 70 ? 2000 : 1900 - } - - return year -} - -function lookupMonth (monthStr) { - return { - jan: 0, - january: 0, - i: 0, - feb: 1, - february: 1, - ii: 1, - mar: 2, - march: 2, - iii: 2, - apr: 3, - april: 3, - iv: 3, - may: 4, - v: 4, - jun: 5, - june: 5, - vi: 5, - jul: 6, - july: 6, - vii: 6, - aug: 7, - august: 7, - viii: 7, - sep: 8, - sept: 8, - september: 8, - ix: 8, - oct: 9, - october: 9, - x: 9, - nov: 10, - november: 10, - xi: 10, - dec: 11, - december: 11, - xii: 11 - }[monthStr.toLowerCase()] -} - -function lookupWeekday (dayStr, desiredSundayNumber = 0) { - const dayNumbers = { - mon: 1, - monday: 1, - tue: 2, - tuesday: 2, - wed: 3, - wednesday: 3, - thu: 4, - thursday: 4, - fri: 5, - friday: 5, - sat: 6, - saturday: 6, - sun: 0, - sunday: 0 - } - - return dayNumbers[dayStr.toLowerCase()] || desiredSundayNumber -} - -function lookupRelative (relText) { - const relativeNumbers = { - last: -1, - previous: -1, - this: 0, - first: 1, - next: 1, - second: 2, - third: 3, - fourth: 4, - fifth: 5, - sixth: 6, - seventh: 7, - eight: 8, - eighth: 8, - ninth: 9, - tenth: 10, - eleventh: 11, - twelfth: 12 - } - - const relativeBehavior = { - this: 1 - } - - const relTextLower = relText.toLowerCase() - - return { - amount: relativeNumbers[relTextLower], - behavior: relativeBehavior[relTextLower] || 0 - } -} - -function processTzCorrection (tzOffset, oldValue) { - const reTzCorrectionLoose = /(?:GMT)?([+-])(\d+)(:?)(\d{0,2})/i - tzOffset = tzOffset && tzOffset.match(reTzCorrectionLoose) - - if (!tzOffset) { - return oldValue - } - - const sign = tzOffset[1] === '-' ? -1 : 1 - let hours = +tzOffset[2] - let minutes = +tzOffset[4] - - if (!tzOffset[4] && !tzOffset[3]) { - minutes = Math.floor(hours % 100) - hours = Math.floor(hours / 100) - } - - // timezone offset in seconds - return sign * (hours * 60 + minutes) * 60 -} - -// tz abbrevation : tz offset in seconds -const tzAbbrOffsets = { - acdt: 37800, - acst: 34200, - addt: -7200, - adt: -10800, - aedt: 39600, - aest: 36000, - ahdt: -32400, - ahst: -36000, - akdt: -28800, - akst: -32400, - amt: -13840, - apt: -10800, - ast: -14400, - awdt: 32400, - awst: 28800, - awt: -10800, - bdst: 7200, - bdt: -36000, - bmt: -14309, - bst: 3600, - cast: 34200, - cat: 7200, - cddt: -14400, - cdt: -18000, - cemt: 10800, - cest: 7200, - cet: 3600, - cmt: -15408, - cpt: -18000, - cst: -21600, - cwt: -18000, - chst: 36000, - dmt: -1521, - eat: 10800, - eddt: -10800, - edt: -14400, - eest: 10800, - eet: 7200, - emt: -26248, - ept: -14400, - est: -18000, - ewt: -14400, - ffmt: -14660, - fmt: -4056, - gdt: 39600, - gmt: 0, - gst: 36000, - hdt: -34200, - hkst: 32400, - hkt: 28800, - hmt: -19776, - hpt: -34200, - hst: -36000, - hwt: -34200, - iddt: 14400, - idt: 10800, - imt: 25025, - ist: 7200, - jdt: 36000, - jmt: 8440, - jst: 32400, - kdt: 36000, - kmt: 5736, - kst: 30600, - lst: 9394, - mddt: -18000, - mdst: 16279, - mdt: -21600, - mest: 7200, - met: 3600, - mmt: 9017, - mpt: -21600, - msd: 14400, - msk: 10800, - mst: -25200, - mwt: -21600, - nddt: -5400, - ndt: -9052, - npt: -9000, - nst: -12600, - nwt: -9000, - nzdt: 46800, - nzmt: 41400, - nzst: 43200, - pddt: -21600, - pdt: -25200, - pkst: 21600, - pkt: 18000, - plmt: 25590, - pmt: -13236, - ppmt: -17340, - ppt: -25200, - pst: -28800, - pwt: -25200, - qmt: -18840, - rmt: 5794, - sast: 7200, - sdmt: -16800, - sjmt: -20173, - smt: -13884, - sst: -39600, - tbmt: 10751, - tmt: 12344, - uct: 0, - utc: 0, - wast: 7200, - wat: 3600, - wemt: 7200, - west: 3600, - wet: 0, - wib: 25200, - wita: 28800, - wit: 32400, - wmt: 5040, - yddt: -25200, - ydt: -28800, - ypt: -28800, - yst: -32400, - ywt: -28800, - a: 3600, - b: 7200, - c: 10800, - d: 14400, - e: 18000, - f: 21600, - g: 25200, - h: 28800, - i: 32400, - k: 36000, - l: 39600, - m: 43200, - n: -3600, - o: -7200, - p: -10800, - q: -14400, - r: -18000, - s: -21600, - t: -25200, - u: -28800, - v: -32400, - w: -36000, - x: -39600, - y: -43200, - z: 0 -} - -const formats = { - yesterday: { - regex: /^yesterday/i, - name: 'yesterday', - callback () { - this.rd -= 1 - return this.resetTime() - } - }, - - now: { - regex: /^now/i, - name: 'now' - // do nothing - }, - - noon: { - regex: /^noon/i, - name: 'noon', - callback () { - return this.resetTime() && this.time(12, 0, 0, 0) - } - }, - - midnightOrToday: { - regex: /^(midnight|today)/i, - name: 'midnight | today', - callback () { - return this.resetTime() - } - }, - - tomorrow: { - regex: /^tomorrow/i, - name: 'tomorrow', - callback () { - this.rd += 1 - return this.resetTime() - } - }, - - timestamp: { - regex: /^@(-?\d+)/i, - name: 'timestamp', - callback (match, timestamp) { - this.rs += +timestamp - this.y = 1970 - this.m = 0 - this.d = 1 - this.dates = 0 - - return this.resetTime() && this.zone(0) - } - }, - - firstOrLastDay: { - regex: /^(first|last) day of/i, - name: 'firstdayof | lastdayof', - callback (match, day) { - if (day.toLowerCase() === 'first') { - this.firstOrLastDayOfMonth = 1 - } else { - this.firstOrLastDayOfMonth = -1 - } - } - }, - - backOrFrontOf: { - regex: RegExp('^(back|front) of ' + reHour24 + reSpaceOpt + reMeridian + '?', 'i'), - name: 'backof | frontof', - callback (match, side, hours, meridian) { - const back = side.toLowerCase() === 'back' - let hour = +hours - let minute = 15 - - if (!back) { - hour -= 1 - minute = 45 - } - - hour = processMeridian(hour, meridian) - - return this.resetTime() && this.time(hour, minute, 0, 0) - } - }, - - weekdayOf: { - regex: RegExp('^(' + reReltextnumber + '|' + reReltexttext + ')' + reSpace + '(' + reDayfull + '|' + reDayabbr + ')' + reSpace + 'of', 'i'), - name: 'weekdayof' - // todo - }, - - mssqltime: { - regex: RegExp('^' + reHour12 + ':' + reMinutelz + ':' + reSecondlz + '[:.]([0-9]+)' + reMeridian, 'i'), - name: 'mssqltime', - callback (match, hour, minute, second, frac, meridian) { - return this.time(processMeridian(+hour, meridian), +minute, +second, +frac.substr(0, 3)) - } - }, - - timeLong12: { - regex: RegExp('^' + reHour12 + '[:.]' + reMinute + '[:.]' + reSecondlz + reSpaceOpt + reMeridian, 'i'), - name: 'timelong12', - callback (match, hour, minute, second, meridian) { - return this.time(processMeridian(+hour, meridian), +minute, +second, 0) - } - }, - - timeShort12: { - regex: RegExp('^' + reHour12 + '[:.]' + reMinutelz + reSpaceOpt + reMeridian, 'i'), - name: 'timeshort12', - callback (match, hour, minute, meridian) { - return this.time(processMeridian(+hour, meridian), +minute, 0, 0) - } - }, - - timeTiny12: { - regex: RegExp('^' + reHour12 + reSpaceOpt + reMeridian, 'i'), - name: 'timetiny12', - callback (match, hour, meridian) { - return this.time(processMeridian(+hour, meridian), 0, 0, 0) - } - }, - - soap: { - regex: RegExp('^' + reYear4 + '-' + reMonthlz + '-' + reDaylz + 'T' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz + reFrac + reTzCorrection + '?', 'i'), - name: 'soap', - callback (match, year, month, day, hour, minute, second, frac, tzCorrection) { - return this.ymd(+year, month - 1, +day) && - this.time(+hour, +minute, +second, +frac.substr(0, 3)) && - this.zone(processTzCorrection(tzCorrection)) - } - }, - - wddx: { - regex: RegExp('^' + reYear4 + '-' + reMonth + '-' + reDay + 'T' + reHour24 + ':' + reMinute + ':' + reSecond), - name: 'wddx', - callback (match, year, month, day, hour, minute, second) { - return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0) - } - }, - - exif: { - regex: RegExp('^' + reYear4 + ':' + reMonthlz + ':' + reDaylz + ' ' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz, 'i'), - name: 'exif', - callback (match, year, month, day, hour, minute, second) { - return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0) - } - }, - - xmlRpc: { - regex: RegExp('^' + reYear4 + reMonthlz + reDaylz + 'T' + reHour24 + ':' + reMinutelz + ':' + reSecondlz), - name: 'xmlrpc', - callback (match, year, month, day, hour, minute, second) { - return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0) - } - }, - - xmlRpcNoColon: { - regex: RegExp('^' + reYear4 + reMonthlz + reDaylz + '[Tt]' + reHour24 + reMinutelz + reSecondlz), - name: 'xmlrpcnocolon', - callback (match, year, month, day, hour, minute, second) { - return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0) - } - }, - - clf: { - regex: RegExp('^' + reDay + '/(' + reMonthAbbr + ')/' + reYear4 + ':' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz + reSpace + reTzCorrection, 'i'), - name: 'clf', - callback (match, day, month, year, hour, minute, second, tzCorrection) { - return this.ymd(+year, lookupMonth(month), +day) && - this.time(+hour, +minute, +second, 0) && - this.zone(processTzCorrection(tzCorrection)) - } - }, - - iso8601long: { - regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond + reFrac, 'i'), - name: 'iso8601long', - callback (match, hour, minute, second, frac) { - return this.time(+hour, +minute, +second, +frac.substr(0, 3)) - } - }, - - dateTextual: { - regex: RegExp('^' + reMonthText + '[ .\\t-]*' + reDay + '[,.stndrh\\t ]+' + reYear, 'i'), - name: 'datetextual', - callback (match, month, day, year) { - return this.ymd(processYear(year), lookupMonth(month), +day) - } - }, - - pointedDate4: { - regex: RegExp('^' + reDay + '[.\\t-]' + reMonth + '[.-]' + reYear4), - name: 'pointeddate4', - callback (match, day, month, year) { - return this.ymd(+year, month - 1, +day) - } - }, - - pointedDate2: { - regex: RegExp('^' + reDay + '[.\\t]' + reMonth + '\\.' + reYear2), - name: 'pointeddate2', - callback (match, day, month, year) { - return this.ymd(processYear(year), month - 1, +day) - } - }, - - timeLong24: { - regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond), - name: 'timelong24', - callback (match, hour, minute, second) { - return this.time(+hour, +minute, +second, 0) - } - }, - - dateNoColon: { - regex: RegExp('^' + reYear4 + reMonthlz + reDaylz), - name: 'datenocolon', - callback (match, year, month, day) { - return this.ymd(+year, month - 1, +day) - } - }, - - pgydotd: { - regex: RegExp('^' + reYear4 + '\\.?' + reDayOfYear), - name: 'pgydotd', - callback (match, year, day) { - return this.ymd(+year, 0, +day) - } - }, - - timeShort24: { - regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute, 'i'), - name: 'timeshort24', - callback (match, hour, minute) { - return this.time(+hour, +minute, 0, 0) - } - }, - - iso8601noColon: { - regex: RegExp('^t?' + reHour24lz + reMinutelz + reSecondlz, 'i'), - name: 'iso8601nocolon', - callback (match, hour, minute, second) { - return this.time(+hour, +minute, +second, 0) - } - }, - - iso8601dateSlash: { - // eventhough the trailing slash is optional in PHP - // here it's mandatory and inputs without the slash - // are handled by dateslash - regex: RegExp('^' + reYear4 + '/' + reMonthlz + '/' + reDaylz + '/'), - name: 'iso8601dateslash', - callback (match, year, month, day) { - return this.ymd(+year, month - 1, +day) - } - }, - - dateSlash: { - regex: RegExp('^' + reYear4 + '/' + reMonth + '/' + reDay), - name: 'dateslash', - callback (match, year, month, day) { - return this.ymd(+year, month - 1, +day) - } - }, - - american: { - regex: RegExp('^' + reMonth + '/' + reDay + '/' + reYear), - name: 'american', - callback (match, month, day, year) { - return this.ymd(processYear(year), month - 1, +day) - } - }, - - americanShort: { - regex: RegExp('^' + reMonth + '/' + reDay), - name: 'americanshort', - callback (match, month, day) { - return this.ymd(this.y, month - 1, +day) - } - }, - - gnuDateShortOrIso8601date2: { - // iso8601date2 is complete subset of gnudateshort - regex: RegExp('^' + reYear + '-' + reMonth + '-' + reDay), - name: 'gnudateshort | iso8601date2', - callback (match, year, month, day) { - return this.ymd(processYear(year), month - 1, +day) - } - }, - - iso8601date4: { - regex: RegExp('^' + reYear4withSign + '-' + reMonthlz + '-' + reDaylz), - name: 'iso8601date4', - callback (match, year, month, day) { - return this.ymd(+year, month - 1, +day) - } - }, - - gnuNoColon: { - regex: RegExp('^t?' + reHour24lz + reMinutelz, 'i'), - name: 'gnunocolon', - callback (match, hour, minute) { - // this rule is a special case - // if time was already set once by any preceding rule, it sets the captured value as year - switch (this.times) { - case 0: - return this.time(+hour, +minute, 0, this.f) - case 1: - this.y = hour * 100 + +minute - this.times++ - - return true - default: - return false - } - } - }, - - gnuDateShorter: { - regex: RegExp('^' + reYear4 + '-' + reMonth), - name: 'gnudateshorter', - callback (match, year, month) { - return this.ymd(+year, month - 1, 1) - } - }, - - pgTextReverse: { - // note: allowed years are from 32-9999 - // years below 32 should be treated as days in datefull - regex: RegExp('^' + '(\\d{3,4}|[4-9]\\d|3[2-9])-(' + reMonthAbbr + ')-' + reDaylz, 'i'), - name: 'pgtextreverse', - callback (match, year, month, day) { - return this.ymd(processYear(year), lookupMonth(month), +day) - } - }, - - dateFull: { - regex: RegExp('^' + reDay + '[ \\t.-]*' + reMonthText + '[ \\t.-]*' + reYear, 'i'), - name: 'datefull', - callback (match, day, month, year) { - return this.ymd(processYear(year), lookupMonth(month), +day) - } - }, - - dateNoDay: { - regex: RegExp('^' + reMonthText + '[ .\\t-]*' + reYear4, 'i'), - name: 'datenoday', - callback (match, month, year) { - return this.ymd(+year, lookupMonth(month), 1) - } - }, - - dateNoDayRev: { - regex: RegExp('^' + reYear4 + '[ .\\t-]*' + reMonthText, 'i'), - name: 'datenodayrev', - callback (match, year, month) { - return this.ymd(+year, lookupMonth(month), 1) - } - }, - - pgTextShort: { - regex: RegExp('^(' + reMonthAbbr + ')-' + reDaylz + '-' + reYear, 'i'), - name: 'pgtextshort', - callback (match, month, day, year) { - return this.ymd(processYear(year), lookupMonth(month), +day) - } - }, - - dateNoYear: { - regex: RegExp('^' + reDateNoYear, 'i'), - name: 'datenoyear', - callback (match, month, day) { - return this.ymd(this.y, lookupMonth(month), +day) - } - }, - - dateNoYearRev: { - regex: RegExp('^' + reDay + '[ .\\t-]*' + reMonthText, 'i'), - name: 'datenoyearrev', - callback (match, day, month) { - return this.ymd(this.y, lookupMonth(month), +day) - } - }, - - isoWeekDay: { - regex: RegExp('^' + reYear4 + '-?W' + reWeekOfYear + '(?:-?([0-7]))?'), - name: 'isoweekday | isoweek', - callback (match, year, week, day) { - day = day ? +day : 1 - - if (!this.ymd(+year, 0, 1)) { - return false - } - - // get day of week for Jan 1st - let dayOfWeek = new Date(this.y, this.m, this.d).getDay() - - // and use the day to figure out the offset for day 1 of week 1 - dayOfWeek = 0 - (dayOfWeek > 4 ? dayOfWeek - 7 : dayOfWeek) - - this.rd += dayOfWeek + ((week - 1) * 7) + day - } - }, - - relativeText: { - regex: RegExp('^(' + reReltextnumber + '|' + reReltexttext + ')' + reSpace + '(' + reReltextunit + ')', 'i'), - name: 'relativetext', - callback (match, relValue, relUnit) { - // todo: implement handling of 'this time-unit' - // eslint-disable-next-line no-unused-vars - const { amount, behavior } = lookupRelative(relValue) - - switch (relUnit.toLowerCase()) { - case 'sec': - case 'secs': - case 'second': - case 'seconds': - this.rs += amount - break - case 'min': - case 'mins': - case 'minute': - case 'minutes': - this.ri += amount - break - case 'hour': - case 'hours': - this.rh += amount - break - case 'day': - case 'days': - this.rd += amount - break - case 'fortnight': - case 'fortnights': - case 'forthnight': - case 'forthnights': - this.rd += amount * 14 - break - case 'week': - case 'weeks': - this.rd += amount * 7 - break - case 'month': - case 'months': - this.rm += amount - break - case 'year': - case 'years': - this.ry += amount - break - case 'mon': case 'monday': - case 'tue': case 'tuesday': - case 'wed': case 'wednesday': - case 'thu': case 'thursday': - case 'fri': case 'friday': - case 'sat': case 'saturday': - case 'sun': case 'sunday': - this.resetTime() - this.weekday = lookupWeekday(relUnit, 7) - this.weekdayBehavior = 1 - this.rd += (amount > 0 ? amount - 1 : amount) * 7 - break - case 'weekday': - case 'weekdays': - // todo - break - } - } - }, - - relative: { - regex: RegExp('^([+-]*)[ \\t]*(\\d+)' + reSpaceOpt + '(' + reReltextunit + '|week)', 'i'), - name: 'relative', - callback (match, signs, relValue, relUnit) { - const minuses = signs.replace(/[^-]/g, '').length - - const amount = +relValue * Math.pow(-1, minuses) - - switch (relUnit.toLowerCase()) { - case 'sec': - case 'secs': - case 'second': - case 'seconds': - this.rs += amount - break - case 'min': - case 'mins': - case 'minute': - case 'minutes': - this.ri += amount - break - case 'hour': - case 'hours': - this.rh += amount - break - case 'day': - case 'days': - this.rd += amount - break - case 'fortnight': - case 'fortnights': - case 'forthnight': - case 'forthnights': - this.rd += amount * 14 - break - case 'week': - case 'weeks': - this.rd += amount * 7 - break - case 'month': - case 'months': - this.rm += amount - break - case 'year': - case 'years': - this.ry += amount - break - case 'mon': case 'monday': - case 'tue': case 'tuesday': - case 'wed': case 'wednesday': - case 'thu': case 'thursday': - case 'fri': case 'friday': - case 'sat': case 'saturday': - case 'sun': case 'sunday': - this.resetTime() - this.weekday = lookupWeekday(relUnit, 7) - this.weekdayBehavior = 1 - this.rd += (amount > 0 ? amount - 1 : amount) * 7 - break - case 'weekday': - case 'weekdays': - // todo - break - } - } - }, - - dayText: { - regex: RegExp('^(' + reDaytext + ')', 'i'), - name: 'daytext', - callback (match, dayText) { - this.resetTime() - this.weekday = lookupWeekday(dayText, 0) - - if (this.weekdayBehavior !== 2) { - this.weekdayBehavior = 1 - } - } - }, - - relativeTextWeek: { - regex: RegExp('^(' + reReltexttext + ')' + reSpace + 'week', 'i'), - name: 'relativetextweek', - callback (match, relText) { - this.weekdayBehavior = 2 - - switch (relText.toLowerCase()) { - case 'this': - this.rd += 0 - break - case 'next': - this.rd += 7 - break - case 'last': - case 'previous': - this.rd -= 7 - break - } - - if (isNaN(this.weekday)) { - this.weekday = 1 - } - } - }, - - monthFullOrMonthAbbr: { - regex: RegExp('^(' + reMonthFull + '|' + reMonthAbbr + ')', 'i'), - name: 'monthfull | monthabbr', - callback (match, month) { - return this.ymd(this.y, lookupMonth(month), this.d) - } - }, - - tzCorrection: { - regex: RegExp('^' + reTzCorrection, 'i'), - name: 'tzcorrection', - callback (tzCorrection) { - return this.zone(processTzCorrection(tzCorrection)) - } - }, - - tzAbbr: { - regex: RegExp('^' + reTzAbbr), - name: 'tzabbr', - callback (match, abbr) { - const offset = tzAbbrOffsets[abbr.toLowerCase()] - - if (isNaN(offset)) { - return false - } - - return this.zone(offset) - } - }, - - ago: { - regex: /^ago/i, - name: 'ago', - callback () { - this.ry = -this.ry - this.rm = -this.rm - this.rd = -this.rd - this.rh = -this.rh - this.ri = -this.ri - this.rs = -this.rs - this.rf = -this.rf - } - }, - - year4: { - regex: RegExp('^' + reYear4), - name: 'year4', - callback (match, year) { - this.y = +year - return true - } - }, - - whitespace: { - regex: /^[ .,\t]+/, - name: 'whitespace' - // do nothing - }, - - dateShortWithTimeLong: { - regex: RegExp('^' + reDateNoYear + 't?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond, 'i'), - name: 'dateshortwithtimelong', - callback (match, month, day, hour, minute, second) { - return this.ymd(this.y, lookupMonth(month), +day) && this.time(+hour, +minute, +second, 0) - } - }, - - dateShortWithTimeLong12: { - regex: RegExp('^' + reDateNoYear + reHour12 + '[:.]' + reMinute + '[:.]' + reSecondlz + reSpaceOpt + reMeridian, 'i'), - name: 'dateshortwithtimelong12', - callback (match, month, day, hour, minute, second, meridian) { - return this.ymd(this.y, lookupMonth(month), +day) && this.time(processMeridian(+hour, meridian), +minute, +second, 0) - } - }, - - dateShortWithTimeShort: { - regex: RegExp('^' + reDateNoYear + 't?' + reHour24 + '[:.]' + reMinute, 'i'), - name: 'dateshortwithtimeshort', - callback (match, month, day, hour, minute) { - return this.ymd(this.y, lookupMonth(month), +day) && this.time(+hour, +minute, 0, 0) - } - }, - - dateShortWithTimeShort12: { - regex: RegExp('^' + reDateNoYear + reHour12 + '[:.]' + reMinutelz + reSpaceOpt + reMeridian, 'i'), - name: 'dateshortwithtimeshort12', - callback (match, month, day, hour, minute, meridian) { - return this.ymd(this.y, lookupMonth(month), +day) && this.time(processMeridian(+hour, meridian), +minute, 0, 0) - } - } -} - -const resultProto = { - // date - y: NaN, - m: NaN, - d: NaN, - // time - h: NaN, - i: NaN, - s: NaN, - f: NaN, - - // relative shifts - ry: 0, - rm: 0, - rd: 0, - rh: 0, - ri: 0, - rs: 0, - rf: 0, - - // weekday related shifts - weekday: NaN, - weekdayBehavior: 0, - - // first or last day of month - // 0 none, 1 first, -1 last - firstOrLastDayOfMonth: 0, - - // timezone correction in minutes - z: NaN, - - // counters - dates: 0, - times: 0, - zones: 0, - - // helper functions - ymd (y, m, d) { - if (this.dates > 0) { - return false - } - - this.dates++ - this.y = y - this.m = m - this.d = d - return true - }, - - time (h, i, s, f) { - if (this.times > 0) { - return false - } - - this.times++ - this.h = h - this.i = i - this.s = s - this.f = f - - return true - }, - - resetTime () { - this.h = 0 - this.i = 0 - this.s = 0 - this.f = 0 - this.times = 0 - - return true - }, - - zone (minutes) { - if (this.zones <= 1) { - this.zones++ - this.z = minutes - return true - } - - return false - }, - - toDate (relativeTo) { - if (this.dates && !this.times) { - this.h = this.i = this.s = this.f = 0 - } - - // fill holes - if (isNaN(this.y)) { - this.y = relativeTo.getFullYear() - } - - if (isNaN(this.m)) { - this.m = relativeTo.getMonth() - } - - if (isNaN(this.d)) { - this.d = relativeTo.getDate() - } - - if (isNaN(this.h)) { - this.h = relativeTo.getHours() - } - - if (isNaN(this.i)) { - this.i = relativeTo.getMinutes() - } - - if (isNaN(this.s)) { - this.s = relativeTo.getSeconds() - } - - if (isNaN(this.f)) { - this.f = relativeTo.getMilliseconds() - } - - // adjust special early - switch (this.firstOrLastDayOfMonth) { - case 1: - this.d = 1 - break - case -1: - this.d = 0 - this.m += 1 - break - } - - if (!isNaN(this.weekday)) { - const date = new Date(relativeTo.getTime()) - date.setFullYear(this.y, this.m, this.d) - date.setHours(this.h, this.i, this.s, this.f) - - const dow = date.getDay() - - if (this.weekdayBehavior === 2) { - // To make "this week" work, where the current day of week is a "sunday" - if (dow === 0 && this.weekday !== 0) { - this.weekday = -6 - } - - // To make "sunday this week" work, where the current day of week is not a "sunday" - if (this.weekday === 0 && dow !== 0) { - this.weekday = 7 - } - - this.d -= dow - this.d += this.weekday - } else { - let diff = this.weekday - dow - - // some PHP magic - if ((this.rd < 0 && diff < 0) || (this.rd >= 0 && diff <= -this.weekdayBehavior)) { - diff += 7 - } - - if (this.weekday >= 0) { - this.d += diff - } else { - this.d -= (7 - (Math.abs(this.weekday) - dow)) - } - - this.weekday = NaN - } - } - - // adjust relative - this.y += this.ry - this.m += this.rm - this.d += this.rd - - this.h += this.rh - this.i += this.ri - this.s += this.rs - this.f += this.rf - - this.ry = this.rm = this.rd = 0 - this.rh = this.ri = this.rs = this.rf = 0 - - const result = new Date(relativeTo.getTime()) - // since Date constructor treats years <= 99 as 1900+ - // it can't be used, thus this weird way - result.setFullYear(this.y, this.m, this.d) - result.setHours(this.h, this.i, this.s, this.f) - - // note: this is done twice in PHP - // early when processing special relatives - // and late - // todo: check if the logic can be reduced - // to just one time action - switch (this.firstOrLastDayOfMonth) { - case 1: - result.setDate(1) - break - case -1: - result.setMonth(result.getMonth() + 1, 0) - break - } - - // adjust timezone - if (!isNaN(this.z) && result.getTimezoneOffset() !== this.z) { - result.setUTCFullYear( - result.getFullYear(), - result.getMonth(), - result.getDate()) - - result.setUTCHours( - result.getHours(), - result.getMinutes(), - result.getSeconds() - this.z, - result.getMilliseconds()) - } - - return result - } -} - -module.exports = function strtotime (str, now) { - // discuss at: https://locutus.io/php/strtotime/ - // original by: Caio Ariede (https://caioariede.com) - // improved by: Kevin van Zonneveld (https://kvz.io) - // improved by: Caio Ariede (https://caioariede.com) - // improved by: A. Matías Quezada (https://amatiasq.com) - // improved by: preuter - // improved by: Brett Zamir (https://brett-zamir.me) - // improved by: Mirko Faber - // input by: David - // bugfixed by: Wagner B. Soares - // bugfixed by: Artur Tchernychev - // bugfixed by: Stephan Bösch-Plepelits (https://github.com/plepe) - // reimplemented by: Rafał Kukawski - // note 1: Examples all have a fixed timestamp to prevent - // note 1: tests to fail because of variable time(zones) - // example 1: strtotime('+1 day', 1129633200) - // returns 1: 1129719600 - // example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200) - // returns 2: 1130425202 - // example 3: strtotime('last month', 1129633200) - // returns 3: 1127041200 - // example 4: strtotime('2009-05-04 08:30:00+00') - // returns 4: 1241425800 - // example 5: strtotime('2009-05-04 08:30:00+02:00') - // returns 5: 1241418600 - // example 6: strtotime('2009-05-04 08:30:00 YWT') - // returns 6: 1241454600 - - if (now == null) { - now = Math.floor(Date.now() / 1000) - } - - // the rule order is important - // if multiple rules match, the longest match wins - // if multiple rules match the same string, the first match wins - const rules = [ - formats.yesterday, - formats.now, - formats.noon, - formats.midnightOrToday, - formats.tomorrow, - formats.timestamp, - formats.firstOrLastDay, - formats.backOrFrontOf, - // formats.weekdayOf, // not yet implemented - formats.timeTiny12, - formats.timeShort12, - formats.timeLong12, - formats.mssqltime, - formats.timeShort24, - formats.timeLong24, - formats.iso8601long, - formats.gnuNoColon, - formats.iso8601noColon, - formats.americanShort, - formats.american, - formats.iso8601date4, - formats.iso8601dateSlash, - formats.dateSlash, - formats.gnuDateShortOrIso8601date2, - formats.gnuDateShorter, - formats.dateFull, - formats.pointedDate4, - formats.pointedDate2, - formats.dateNoDay, - formats.dateNoDayRev, - formats.dateTextual, - formats.dateNoYear, - formats.dateNoYearRev, - formats.dateNoColon, - formats.xmlRpc, - formats.xmlRpcNoColon, - formats.soap, - formats.wddx, - formats.exif, - formats.pgydotd, - formats.isoWeekDay, - formats.pgTextShort, - formats.pgTextReverse, - formats.clf, - formats.year4, - formats.ago, - formats.dayText, - formats.relativeTextWeek, - formats.relativeText, - formats.monthFullOrMonthAbbr, - formats.tzCorrection, - formats.tzAbbr, - formats.dateShortWithTimeShort12, - formats.dateShortWithTimeLong12, - formats.dateShortWithTimeShort, - formats.dateShortWithTimeLong, - formats.relative, - formats.whitespace - ] - - const result = Object.create(resultProto) - - while (str.length) { - let longestMatch = null - let finalRule = null - - for (let i = 0, l = rules.length; i < l; i++) { - const format = rules[i] - - const match = str.match(format.regex) - - if (match) { - if (!longestMatch || match[0].length > longestMatch[0].length) { - longestMatch = match - finalRule = format - } - } - } - - if (!finalRule || (finalRule.callback && finalRule.callback.apply(result, longestMatch) === false)) { - return false - } - - str = str.substr(longestMatch[0].length) - finalRule = null - longestMatch = null - } - - return Math.floor(result.toDate(new Date(now * 1000)) / 1000) -} \ No newline at end of file diff --git a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts index 02640efc0..cc0c801b5 100644 --- a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts +++ b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts @@ -29,6 +29,7 @@ export interface AbstractBitcoinApi { $getOutSpendsByOutpoint(outpoints: { txid: string, vout: number }[]): Promise; startHealthChecks(): void; + getHealthStatus(): HealthCheckHost[]; } export interface BitcoinRpcCredentials { host: string; @@ -38,3 +39,15 @@ export interface BitcoinRpcCredentials { timeout: number; cookie?: string; } + +export interface HealthCheckHost { + host: string; + active: boolean; + rtt: number; + latestHeight: number; + socket: boolean; + outOfSync: boolean; + unreachable: boolean; + checked: boolean; + lastChecked: number; +} diff --git a/backend/src/api/bitcoin/bitcoin-api.interface.ts b/backend/src/api/bitcoin/bitcoin-api.interface.ts index 3afc22897..e176566d7 100644 --- a/backend/src/api/bitcoin/bitcoin-api.interface.ts +++ b/backend/src/api/bitcoin/bitcoin-api.interface.ts @@ -106,6 +106,7 @@ export namespace IBitcoinApi { address?: string; // (string) bitcoin address addresses?: string[]; // (string) bitcoin addresses pegout_chain?: string; // (string) Elements peg-out chain + pegout_address?: string; // (string) Elements peg-out address pegout_addresses?: string[]; // (string) Elements peg-out addresses }; } diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index f54c836f8..d19eb06ac 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -1,5 +1,5 @@ import * as bitcoinjs from 'bitcoinjs-lib'; -import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory'; +import { AbstractBitcoinApi, HealthCheckHost } from './bitcoin-api-abstract-factory'; import { IBitcoinApi } from './bitcoin-api.interface'; import { IEsploraApi } from './esplora-api.interface'; import blocks from '../blocks'; @@ -382,6 +382,10 @@ class BitcoinApi implements AbstractBitcoinApi { } public startHealthChecks(): void {}; + + public getHealthStatus() { + return []; + } } export default BitcoinApi; diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index 4e9a83d2c..29c3f7c21 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -19,6 +19,7 @@ import bitcoinClient from './bitcoin-client'; import difficultyAdjustment from '../difficulty-adjustment'; import transactionRepository from '../../repositories/TransactionRepository'; import rbfCache from '../rbf-cache'; +import { calculateCpfp } from '../cpfp'; class BitcoinRoutes { public initRoutes(app: Application) { @@ -121,8 +122,10 @@ class BitcoinRoutes { .get(config.MEMPOOL.API_URL_PREFIX + 'block-height/:height', this.getBlockHeight) .get(config.MEMPOOL.API_URL_PREFIX + 'address/:address', this.getAddress) .get(config.MEMPOOL.API_URL_PREFIX + 'address/:address/txs', this.getAddressTransactions) + .get(config.MEMPOOL.API_URL_PREFIX + 'address/:address/txs/summary', this.getAddressTransactionSummary) .get(config.MEMPOOL.API_URL_PREFIX + 'scripthash/:scripthash', this.getScriptHash) .get(config.MEMPOOL.API_URL_PREFIX + 'scripthash/:scripthash/txs', this.getScriptHashTransactions) + .get(config.MEMPOOL.API_URL_PREFIX + 'scripthash/:scripthash/txs/summary', this.getScriptHashTransactionSummary) .get(config.MEMPOOL.API_URL_PREFIX + 'address-prefix/:prefix', this.getAddressPrefix) ; } @@ -215,7 +218,7 @@ class BitcoinRoutes { return; } - const cpfpInfo = Common.setRelativesAndGetCpfpInfo(tx, mempool.getMempool()); + const cpfpInfo = calculateCpfp(tx, mempool.getMempool()); res.json(cpfpInfo); return; @@ -415,7 +418,7 @@ class BitcoinRoutes { const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10); res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(await blocks.$getBlocks(height, 15)); - } else { // Liquid, Bisq + } else { // Liquid return await this.getLegacyBlocks(req, res); } } catch (e) { @@ -425,7 +428,7 @@ class BitcoinRoutes { private async getBlocksByBulk(req: Request, res: Response) { try { - if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) { // Liquid, Bisq - Not implemented + if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) { // Liquid - Not implemented return res.status(404).send(`This API is only available for Bitcoin networks`); } if (config.MEMPOOL.MAX_BLOCKS_BULK_QUERY <= 0) { @@ -566,6 +569,13 @@ class BitcoinRoutes { } } + private async getAddressTransactionSummary(req: Request, res: Response): Promise { + if (config.MEMPOOL.BACKEND !== 'esplora') { + res.status(405).send('Address summary lookups require mempool/electrs backend.'); + return; + } + } + private async getScriptHash(req: Request, res: Response) { if (config.MEMPOOL.BACKEND === 'none') { res.status(405).send('Address lookups cannot be used with bitcoind as backend.'); @@ -609,6 +619,13 @@ class BitcoinRoutes { } } + private async getScriptHashTransactionSummary(req: Request, res: Response): Promise { + if (config.MEMPOOL.BACKEND !== 'esplora') { + res.status(405).send('Scripthash summary lookups require mempool/electrs backend.'); + return; + } + } + private async getAddressPrefix(req: Request, res: Response) { try { const blockHash = await bitcoinApi.$getAddressPrefix(req.params.prefix); diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index c0b548b9a..a9dadf4a0 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -1,25 +1,30 @@ import config from '../../config'; -import axios, { AxiosResponse } from 'axios'; +import axios, { AxiosResponse, isAxiosError } from 'axios'; import http from 'http'; -import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory'; +import { AbstractBitcoinApi, HealthCheckHost } from './bitcoin-api-abstract-factory'; import { IEsploraApi } from './esplora-api.interface'; import logger from '../../logger'; +import { Common } from '../common'; interface FailoverHost { host: string, rtts: number[], rtt: number, + timedOut?: boolean, failures: number, latestHeight?: number, socket?: boolean, outOfSync?: boolean, unreachable?: boolean, preferred?: boolean, + checked: boolean, + lastChecked?: number, } class FailoverRouter { activeHost: FailoverHost; fallbackHost: FailoverHost; + maxHeight: number = 0; hosts: FailoverHost[]; multihost: boolean; pollInterval: number = 60000; @@ -34,6 +39,7 @@ class FailoverRouter { this.hosts = (config.ESPLORA.FALLBACK || []).map(domain => { return { host: domain, + checked: false, rtts: [], rtt: Infinity, failures: 0, @@ -46,6 +52,7 @@ class FailoverRouter { failures: 0, socket: !!config.ESPLORA.UNIX_SOCKET_PATH, preferred: true, + checked: false, }; this.fallbackHost = this.activeHost; this.hosts.unshift(this.activeHost); @@ -74,66 +81,93 @@ class FailoverRouter { clearTimeout(this.pollTimer); } - const results = await Promise.allSettled(this.hosts.map(async (host) => { - if (host.socket) { - return this.pollConnection.get('/blocks/tip/height', { socketPath: host.host, timeout: config.ESPLORA.FALLBACK_TIMEOUT }); - } else { - return this.pollConnection.get(host.host + '/blocks/tip/height', { timeout: config.ESPLORA.FALLBACK_TIMEOUT }); - } - })); - const maxHeight = results.reduce((max, result) => Math.max(max, result.status === 'fulfilled' ? result.value?.data || 0 : 0), 0); + const start = Date.now(); // update rtts & sync status - for (let i = 0; i < results.length; i++) { - const host = this.hosts[i]; - const result = results[i].status === 'fulfilled' ? (results[i] as PromiseFulfilledResult>).value : null; - if (result) { - const height = result.data; - const rtt = result.config['meta'].rtt; - host.rtts.unshift(rtt); - host.rtts.slice(0, 5); - host.rtt = host.rtts.reduce((acc, l) => acc + l, 0) / host.rtts.length; - host.latestHeight = height; - if (height == null || isNaN(height) || (maxHeight - height > 2)) { - host.outOfSync = true; + for (const host of this.hosts) { + try { + const result = await (host.socket + ? this.pollConnection.get('/blocks/tip/height', { socketPath: host.host, timeout: config.ESPLORA.FALLBACK_TIMEOUT }) + : this.pollConnection.get(host.host + '/blocks/tip/height', { timeout: config.ESPLORA.FALLBACK_TIMEOUT }) + ); + if (result) { + const height = result.data; + this.maxHeight = Math.max(height, this.maxHeight); + const rtt = result.config['meta'].rtt; + host.rtts.unshift(rtt); + host.rtts.slice(0, 5); + host.rtt = host.rtts.reduce((acc, l) => acc + l, 0) / host.rtts.length; + host.latestHeight = height; + if (height == null || isNaN(height) || (this.maxHeight - height > 2)) { + host.outOfSync = true; + } else { + host.outOfSync = false; + } + host.unreachable = false; } else { - host.outOfSync = false; + host.outOfSync = true; + host.unreachable = true; + host.rtts = []; + host.rtt = Infinity; } - host.unreachable = false; - } else { + host.timedOut = false; + } catch (e) { host.outOfSync = true; host.unreachable = true; + host.rtts = []; + host.rtt = Infinity; + if (isAxiosError(e) && (e.code === 'ECONNABORTED' || e.code === 'ETIMEDOUT')) { + host.timedOut = true; + } else { + host.timedOut = false; + } } + host.checked = true; + host.lastChecked = Date.now(); + + // switch if the current host is out of sync or significantly slower than the next best alternative + const rankOrder = this.sortHosts(); + // switch if the current host is out of sync or significantly slower than the next best alternative + if (this.activeHost.outOfSync || this.activeHost.unreachable || (this.activeHost !== rankOrder[0] && rankOrder[0].preferred) || (!this.activeHost.preferred && this.activeHost.rtt > (rankOrder[0].rtt * 2) + 50)) { + if (this.activeHost.unreachable) { + logger.warn(`🚨🚨🚨 Unable to reach ${this.activeHost.host}, failing over to next best alternative 🚨🚨🚨`); + } else if (this.activeHost.outOfSync) { + logger.warn(`🚨🚨🚨 ${this.activeHost.host} has fallen behind, failing over to next best alternative 🚨🚨🚨`); + } else { + logger.debug(`🛠️ ${this.activeHost.host} is no longer the best esplora host 🛠️`); + } + this.electHost(); + } + await Common.sleep$(50); } - this.sortHosts(); + const rankOrder = this.updateFallback(); + logger.debug(`Tomahawk ranking:\n${rankOrder.map((host, index) => this.formatRanking(index, host, this.activeHost, this.maxHeight)).join('\n')}`); - logger.debug(`Tomahawk ranking:\n${this.hosts.map((host, index) => this.formatRanking(index, host, this.activeHost, maxHeight)).join('\n')}`); + const elapsed = Date.now() - start; - // switch if the current host is out of sync or significantly slower than the next best alternative - if (this.activeHost.outOfSync || this.activeHost.unreachable || (this.activeHost !== this.hosts[0] && this.hosts[0].preferred) || (!this.activeHost.preferred && this.activeHost.rtt > (this.hosts[0].rtt * 2) + 50)) { - if (this.activeHost.unreachable) { - logger.warn(`🚨🚨🚨 Unable to reach ${this.activeHost.host}, failing over to next best alternative 🚨🚨🚨`); - } else if (this.activeHost.outOfSync) { - logger.warn(`🚨🚨🚨 ${this.activeHost.host} has fallen behind, failing over to next best alternative 🚨🚨🚨`); - } else { - logger.debug(`🛠️ ${this.activeHost.host} is no longer the best esplora host 🛠️`); - } - this.electHost(); - } - - this.pollTimer = setTimeout(() => { this.pollHosts(); }, this.pollInterval); + this.pollTimer = setTimeout(() => { this.pollHosts(); }, Math.max(1, this.pollInterval - elapsed)); } private formatRanking(index: number, host: FailoverHost, active: FailoverHost, maxHeight: number): string { - const heightStatus = host.outOfSync ? '🚫' : (host.latestHeight && host.latestHeight < maxHeight ? '🟧' : '✅'); - return `${host === active ? '⭐️' : ' '} ${host.rtt < Infinity ? Math.round(host.rtt).toString().padStart(5, ' ') + 'ms' : ' - '} ${host.unreachable ? '🔥' : '✅'} | block: ${host.latestHeight || '??????'} ${heightStatus} | ${host.host} ${host === active ? '⭐️' : ' '}`; + const heightStatus = !host.checked ? '⏳' : (host.outOfSync ? '🚫' : (host.latestHeight && host.latestHeight < maxHeight ? '🟧' : '✅')); + return `${host === active ? '⭐️' : ' '} ${host.rtt < Infinity ? Math.round(host.rtt).toString().padStart(5, ' ') + 'ms' : (host.timedOut ? ' ⌛️💥 ' : ' - ')} ${!host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅')} | block: ${host.latestHeight || '??????'} ${heightStatus} | ${host.host} ${host === active ? '⭐️' : ' '}`; + } + + private updateFallback(): FailoverHost[] { + const rankOrder = this.sortHosts(); + if (rankOrder.length > 1 && rankOrder[0] === this.activeHost) { + this.fallbackHost = rankOrder[1]; + } else { + this.fallbackHost = rankOrder[0]; + } + return rankOrder; } // sort hosts by connection quality, and update default fallback - private sortHosts(): void { + public sortHosts(): FailoverHost[] { // sort by connection quality - this.hosts.sort((a, b) => { + return this.hosts.slice().sort((a, b) => { if ((a.unreachable || a.outOfSync) === (b.unreachable || b.outOfSync)) { if (a.preferred === b.preferred) { // lower rtt is best @@ -145,19 +179,14 @@ class FailoverRouter { return (a.unreachable || a.outOfSync) ? 1 : -1; } }); - if (this.hosts.length > 1 && this.hosts[0] === this.activeHost) { - this.fallbackHost = this.hosts[1]; - } else { - this.fallbackHost = this.hosts[0]; - } } // depose the active host and choose the next best replacement private electHost(): void { this.activeHost.outOfSync = true; this.activeHost.failures = 0; - this.sortHosts(); - this.activeHost = this.hosts[0]; + const rankOrder = this.sortHosts(); + this.activeHost = rankOrder[0]; logger.warn(`Switching esplora host to ${this.activeHost.host}`); } @@ -321,6 +350,24 @@ class ElectrsApi implements AbstractBitcoinApi { public startHealthChecks(): void { this.failoverRouter.startHealthChecks(); } + + public getHealthStatus(): HealthCheckHost[] { + if (config.MEMPOOL.OFFICIAL) { + return this.failoverRouter.sortHosts().map(host => ({ + host: host.host, + active: host === this.failoverRouter.activeHost, + rtt: host.rtt, + latestHeight: host.latestHeight || 0, + socket: !!host.socket, + outOfSync: !!host.outOfSync, + unreachable: !!host.unreachable, + checked: !!host.checked, + lastChecked: host.lastChecked || 0, + })); + } else { + return []; + } + } } export default ElectrsApi; diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 21818dc62..28ca38152 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -2,7 +2,7 @@ import config from '../config'; import bitcoinApi, { bitcoinCoreApi } from './bitcoin/bitcoin-api-factory'; import logger from '../logger'; import memPool from './mempool'; -import { BlockExtended, BlockExtension, BlockSummary, PoolTag, TransactionExtended, TransactionMinerInfo, CpfpSummary, MempoolTransactionExtended, TransactionClassified } from '../mempool.interfaces'; +import { BlockExtended, BlockExtension, BlockSummary, PoolTag, TransactionExtended, TransactionMinerInfo, CpfpSummary, MempoolTransactionExtended, TransactionClassified, BlockAudit } from '../mempool.interfaces'; import { Common } from './common'; import diskCache from './disk-cache'; import transactionUtils from './transaction-utils'; @@ -37,8 +37,10 @@ class Blocks { private currentBits = 0; private lastDifficultyAdjustmentTime = 0; private previousDifficultyRetarget = 0; + private quarterEpochBlockTime: number | null = null; private newBlockCallbacks: ((block: BlockExtended, txIds: string[], transactions: TransactionExtended[]) => void)[] = []; private newAsyncBlockCallbacks: ((block: BlockExtended, txIds: string[], transactions: MempoolTransactionExtended[]) => Promise)[] = []; + private classifyingBlocks: boolean = false; private mainLoopTimeout: number = 120000; @@ -451,7 +453,9 @@ class Blocks { if (config.MEMPOOL.BACKEND === 'esplora') { const txs = (await bitcoinApi.$getTxsForBlock(block.hash)).map(tx => transactionUtils.extendTransaction(tx)); const cpfpSummary = await this.$indexCPFP(block.hash, block.height, txs); - await this.$getStrippedBlockTransactions(block.hash, true, true, cpfpSummary, block.height); // This will index the block summary + if (cpfpSummary) { + await this.$getStrippedBlockTransactions(block.hash, true, true, cpfpSummary, block.height); // This will index the block summary + } } else { await this.$getStrippedBlockTransactions(block.hash, true, true); // This will index the block summary } @@ -565,8 +569,13 @@ class Blocks { * [INDEXING] Index transaction classification flags for Goggles */ public async $classifyBlocks(): Promise { + if (this.classifyingBlocks) { + return; + } + this.classifyingBlocks = true; + // classification requires an esplora backend - if (!Common.blocksSummariesIndexingEnabled() || config.MEMPOOL.BACKEND !== 'esplora') { + if (!Common.gogglesIndexingEnabled() || config.MEMPOOL.BACKEND !== 'esplora') { return; } @@ -617,6 +626,7 @@ class Blocks { // classify const { transactions: classifiedTxs } = this.summarizeBlockTransactions(blockHash, cpfpSummary.transactions); await BlocksSummariesRepository.$saveTransactions(height, blockHash, classifiedTxs, 1); + await Common.sleep$(250); } if (unclassifiedTemplates[height]) { // classify template @@ -656,6 +666,7 @@ class Blocks { }); } await BlocksSummariesRepository.$saveTemplate({ height, template: { id: blockHash, transactions: classifiedTemplate }, version: 1 }); + await Common.sleep$(250); } } catch (e) { logger.warn(`Failed to classify template or block summary at ${height}`, logger.tags.goggles); @@ -674,6 +685,8 @@ class Blocks { indexedThisRun = 0; } } + + this.classifyingBlocks = false; } /** @@ -771,6 +784,16 @@ class Blocks { } else { this.currentBlockHeight = this.blocks[this.blocks.length - 1].height; } + if (this.currentBlockHeight >= 503) { + try { + const quarterEpochBlockHash = await bitcoinApi.$getBlockHash(this.currentBlockHeight - 503); + const quarterEpochBlock = await bitcoinApi.$getBlock(quarterEpochBlockHash); + this.quarterEpochBlockTime = quarterEpochBlock?.timestamp; + } catch (e) { + this.quarterEpochBlockTime = null; + logger.warn('failed to update last epoch block time: ' + (e instanceof Error ? e.message : e)); + } + } if (blockHeightTip - this.currentBlockHeight > config.MEMPOOL.INITIAL_BLOCKS_AMOUNT * 2) { logger.info(`${blockHeightTip - this.currentBlockHeight} blocks since tip. Fast forwarding to the ${config.MEMPOOL.INITIAL_BLOCKS_AMOUNT} recent blocks`); @@ -993,11 +1016,11 @@ class Blocks { return state; } - private updateTimerProgress(state, msg) { + private updateTimerProgress(state, msg): void { state.progress = msg; } - private clearTimer(state) { + private clearTimer(state): void { if (state.timer) { clearTimeout(state.timer); } @@ -1086,13 +1109,19 @@ class Blocks { summary = { id: hash, transactions: cpfpSummary.transactions.map(tx => { + let flags: number = 0; + try { + flags = tx.flags || Common.getTransactionFlags(tx); + } catch (e) { + logger.warn('Failed to classify transaction: ' + (e instanceof Error ? e.message : e)); + } return { txid: tx.txid, fee: tx.fee || 0, vsize: tx.vsize, value: Math.round(tx.vout.reduce((acc, vout) => acc + (vout.value ? vout.value : 0), 0)), rate: tx.effectiveFeePerVsize, - flags: tx.flags || Common.getTransactionFlags(tx), + flags: flags, }; }), }; @@ -1282,7 +1311,7 @@ class Blocks { return blocks; } - public async $getBlockAuditSummary(hash: string): Promise { + public async $getBlockAuditSummary(hash: string): Promise { if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK)) { return BlocksAuditsRepository.$getBlockAudit(hash); } else { @@ -1298,11 +1327,15 @@ class Blocks { return this.previousDifficultyRetarget; } + public getQuarterEpochBlockTime(): number | null { + return this.quarterEpochBlockTime; + } + public getCurrentBlockHeight(): number { return this.currentBlockHeight; } - public async $indexCPFP(hash: string, height: number, txs?: TransactionExtended[]): Promise { + public async $indexCPFP(hash: string, height: number, txs?: TransactionExtended[]): Promise { let transactions = txs; if (!transactions) { if (config.MEMPOOL.BACKEND === 'esplora') { @@ -1317,14 +1350,19 @@ class Blocks { } } - const summary = Common.calculateCpfp(height, transactions as TransactionExtended[]); + if (transactions?.length != null) { + const summary = Common.calculateCpfp(height, transactions as TransactionExtended[]); - await this.$saveCpfp(hash, height, summary); + await this.$saveCpfp(hash, height, summary); - const effectiveFeeStats = Common.calcEffectiveFeeStatistics(summary.transactions); - await blocksRepository.$saveEffectiveFeeStats(hash, effectiveFeeStats); + const effectiveFeeStats = Common.calcEffectiveFeeStatistics(summary.transactions); + await blocksRepository.$saveEffectiveFeeStats(hash, effectiveFeeStats); - return summary; + return summary; + } else { + logger.err(`Cannot index CPFP for block ${height} - missing transaction data`); + return null; + } } public async $saveCpfp(hash: string, height: number, cpfpSummary: CpfpSummary): Promise { diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index 52fa9042b..5053d4da3 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -6,6 +6,25 @@ import { NodeSocket } from '../repositories/NodesSocketsRepository'; import { isIP } from 'net'; import transactionUtils from './transaction-utils'; import { isPoint } from '../utils/secp256k1'; +import logger from '../logger'; +import { getVarIntLength, opcodes, parseMultisigScript } from '../utils/bitcoin-script'; + +// Bitcoin Core default policy settings +const TX_MAX_STANDARD_VERSION = 2; +const MAX_STANDARD_TX_WEIGHT = 400_000; +const MAX_BLOCK_SIGOPS_COST = 80_000; +const MAX_STANDARD_TX_SIGOPS_COST = (MAX_BLOCK_SIGOPS_COST / 5); +const MIN_STANDARD_TX_NONWITNESS_SIZE = 65; +const MAX_P2SH_SIGOPS = 15; +const MAX_STANDARD_P2WSH_STACK_ITEMS = 100; +const MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80; +const MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE = 80; +const MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600; +const MAX_STANDARD_SCRIPTSIG_SIZE = 1650; +const DUST_RELAY_TX_FEE = 3; +const MAX_OP_RETURN_RELAY = 83; +const DEFAULT_PERMIT_BAREMULTISIG = true; + export class Common { static nativeAssetId = config.MEMPOOL.NETWORK === 'liquidtestnet' ? '144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49' @@ -176,6 +195,140 @@ export class Common { ); } + /** + * Validates most standardness rules + * + * returns true early if any standardness rule is violated, otherwise false + * (except for non-mandatory-script-verify-flag and p2sh script evaluation rules which are *not* enforced) + */ + static isNonStandard(tx: TransactionExtended): boolean { + // version + if (tx.version > TX_MAX_STANDARD_VERSION) { + return true; + } + + // tx-size + if (tx.weight > MAX_STANDARD_TX_WEIGHT) { + return true; + } + + // tx-size-small + if (this.getNonWitnessSize(tx) < MIN_STANDARD_TX_NONWITNESS_SIZE) { + return true; + } + + // bad-txns-too-many-sigops + if (tx.sigops && tx.sigops > MAX_STANDARD_TX_SIGOPS_COST) { + return true; + } + + // input validation + for (const vin of tx.vin) { + if (vin.is_coinbase) { + // standardness rules don't apply to coinbase transactions + return false; + } + // scriptsig-size + if ((vin.scriptsig.length / 2) > MAX_STANDARD_SCRIPTSIG_SIZE) { + return true; + } + // scriptsig-not-pushonly + if (vin.scriptsig_asm) { + for (const op of vin.scriptsig_asm.split(' ')) { + if (opcodes[op] && opcodes[op] > opcodes['OP_16']) { + return true; + } + } + } + // bad-txns-nonstandard-inputs + if (vin.prevout?.scriptpubkey_type === 'p2sh') { + // TODO: evaluate script (https://github.com/bitcoin/bitcoin/blob/1ac627c485a43e50a9a49baddce186ee3ad4daad/src/policy/policy.cpp#L177) + // countScriptSigops returns the witness-scaled sigops, so divide by 4 before comparison with MAX_P2SH_SIGOPS + const sigops = (transactionUtils.countScriptSigops(vin.inner_redeemscript_asm) / 4); + if (sigops > MAX_P2SH_SIGOPS) { + return true; + } + } else if (['unknown', 'provably_unspendable', 'empty'].includes(vin.prevout?.scriptpubkey_type || '')) { + return true; + } + // TODO: bad-witness-nonstandard + } + + // output validation + let opreturnCount = 0; + for (const vout of tx.vout) { + // scriptpubkey + if (['unknown', 'provably_unspendable', 'empty'].includes(vout.scriptpubkey_type)) { + // (non-standard output type) + return true; + } else if (vout.scriptpubkey_type === 'multisig') { + if (!DEFAULT_PERMIT_BAREMULTISIG) { + // bare-multisig + return true; + } + const mOfN = parseMultisigScript(vout.scriptpubkey_asm); + if (!mOfN || mOfN.n < 1 || mOfN.n > 3 || mOfN.m < 1 || mOfN.m > mOfN.n) { + // (non-standard bare multisig threshold) + return true; + } + } else if (vout.scriptpubkey_type === 'op_return') { + opreturnCount++; + if ((vout.scriptpubkey.length / 2) > MAX_OP_RETURN_RELAY) { + // over default datacarrier limit + return true; + } + } + // dust + // (we could probably hardcode this for the different output types...) + if (vout.scriptpubkey_type !== 'op_return') { + let dustSize = (vout.scriptpubkey.length / 2); + // add varint length overhead + dustSize += getVarIntLength(dustSize); + // add value size + dustSize += 8; + if (['v0_p2wpkh', 'v0_p2wsh', 'v1_p2tr'].includes(vout.scriptpubkey_type)) { + dustSize += 67; + } else { + dustSize += 148; + } + if (vout.value < (dustSize * DUST_RELAY_TX_FEE)) { + // under minimum output size + return true; + } + } + } + + // multi-op-return + if (opreturnCount > 1) { + return true; + } + + // TODO: non-mandatory-script-verify-flag + + return false; + } + + static getNonWitnessSize(tx: TransactionExtended): number { + let weight = tx.weight; + let hasWitness = false; + for (const vin of tx.vin) { + if (vin.witness?.length) { + hasWitness = true; + // witness count + weight -= getVarIntLength(vin.witness.length); + for (const witness of vin.witness) { + // witness item size + content + weight -= getVarIntLength(witness.length / 2) + (witness.length / 2); + } + } + } + if (hasWitness) { + // marker & segwit flag + weight -= 2; + } + return Math.ceil(weight / 4); + } + static setSegwitSighashFlags(flags: bigint, witness: string[]): bigint { for (const w of witness) { if (this.isDERSig(w)) { @@ -244,8 +397,11 @@ export class Common { flags |= TransactionFlags.v1; } else if (tx.version === 2) { flags |= TransactionFlags.v2; + } else if (tx.version === 3) { + flags |= TransactionFlags.v3; } - const reusedAddresses: { [address: string ]: number } = {}; + const reusedInputAddresses: { [address: string ]: number } = {}; + const reusedOutputAddresses: { [address: string ]: number } = {}; const inValues = {}; const outValues = {}; let rbf = false; @@ -261,6 +417,9 @@ export class Common { case 'v0_p2wpkh': flags |= TransactionFlags.p2wpkh; break; case 'v0_p2wsh': flags |= TransactionFlags.p2wsh; break; case 'v1_p2tr': { + if (!vin.witness?.length) { + throw new Error('Taproot input missing witness data'); + } flags |= TransactionFlags.p2tr; // in taproot, if the last witness item begins with 0x50, it's an annex const hasAnnex = vin.witness?.[vin.witness.length - 1].startsWith('50'); @@ -286,7 +445,7 @@ export class Common { } if (vin.prevout?.scriptpubkey_address) { - reusedAddresses[vin.prevout?.scriptpubkey_address] = (reusedAddresses[vin.prevout?.scriptpubkey_address] || 0) + 1; + reusedInputAddresses[vin.prevout?.scriptpubkey_address] = (reusedInputAddresses[vin.prevout?.scriptpubkey_address] || 0) + 1; } inValues[vin.prevout?.value || Math.random()] = (inValues[vin.prevout?.value || Math.random()] || 0) + 1; } @@ -296,12 +455,14 @@ export class Common { flags |= TransactionFlags.no_rbf; } let hasFakePubkey = false; + let P2WSHCount = 0; + let olgaSize = 0; for (const vout of tx.vout) { switch (vout.scriptpubkey_type) { case 'p2pk': { flags |= TransactionFlags.p2pk; // detect fake pubkey (i.e. not a valid DER point on the secp256k1 curve) - hasFakePubkey = hasFakePubkey || !isPoint(vout.scriptpubkey.slice(2, -2)); + hasFakePubkey = hasFakePubkey || !isPoint(vout.scriptpubkey?.slice(2, -2)); } break; case 'multisig': { flags |= TransactionFlags.p2ms; @@ -321,7 +482,21 @@ export class Common { case 'op_return': flags |= TransactionFlags.op_return; break; } if (vout.scriptpubkey_address) { - reusedAddresses[vout.scriptpubkey_address] = (reusedAddresses[vout.scriptpubkey_address] || 0) + 1; + reusedOutputAddresses[vout.scriptpubkey_address] = (reusedOutputAddresses[vout.scriptpubkey_address] || 0) + 1; + } + if (vout.scriptpubkey_type === 'v0_p2wsh') { + if (!P2WSHCount) { + olgaSize = parseInt(vout.scriptpubkey.slice(4, 8), 16); + } + P2WSHCount++; + if (P2WSHCount === Math.ceil((olgaSize + 2) / 32)) { + const nullBytes = (P2WSHCount * 32) - olgaSize - 2; + if (vout.scriptpubkey.endsWith(''.padEnd(nullBytes * 2, '0'))) { + flags |= TransactionFlags.fake_scripthash; + } + } + } else { + P2WSHCount = 0; } outValues[vout.value || Math.random()] = (outValues[vout.value || Math.random()] || 0) + 1; } @@ -331,7 +506,7 @@ export class Common { // fast but bad heuristic to detect possible coinjoins // (at least 5 inputs and 5 outputs, less than half of which are unique amounts, with no address reuse) - const addressReuse = Object.values(reusedAddresses).reduce((acc, count) => Math.max(acc, count), 0) > 1; + const addressReuse = Object.keys(reusedOutputAddresses).reduce((acc, key) => Math.max(acc, (reusedInputAddresses[key] || 0) + (reusedOutputAddresses[key] || 0)), 0) > 1; if (!addressReuse && tx.vin.length >= 5 && tx.vout.length >= 5 && (Object.keys(inValues).length + Object.keys(outValues).length) <= (tx.vin.length + tx.vout.length) / 2 ) { flags |= TransactionFlags.coinjoin; } @@ -344,11 +519,20 @@ export class Common { flags |= TransactionFlags.batch_payout; } + if (this.isNonStandard(tx)) { + flags |= TransactionFlags.nonstandard; + } + return Number(flags); } static classifyTransaction(tx: TransactionExtended): TransactionClassified { - const flags = Common.getTransactionFlags(tx); + let flags = 0; + try { + flags = Common.getTransactionFlags(tx); + } catch (e) { + logger.warn('Failed to add classification flags to transaction: ' + (e instanceof Error ? e.message : e)); + } tx.flags = flags; return { ...Common.stripTransaction(tx), @@ -368,6 +552,7 @@ export class Common { value: tx.vout.reduce((acc, vout) => acc + (vout.value ? vout.value : 0), 0), acc: tx.acceleration || undefined, rate: tx.effectiveFeePerVsize, + time: tx.firstSeen || undefined, }; } @@ -390,69 +575,6 @@ export class Common { } } - static setRelativesAndGetCpfpInfo(tx: MempoolTransactionExtended, memPool: { [txid: string]: MempoolTransactionExtended }): CpfpInfo { - const parents = this.findAllParents(tx, memPool); - const lowerFeeParents = parents.filter((parent) => parent.adjustedFeePerVsize < tx.effectiveFeePerVsize); - - let totalWeight = (tx.adjustedVsize * 4) + lowerFeeParents.reduce((prev, val) => prev + (val.adjustedVsize * 4), 0); - let totalFees = tx.fee + lowerFeeParents.reduce((prev, val) => prev + val.fee, 0); - - tx.ancestors = parents - .map((t) => { - return { - txid: t.txid, - weight: (t.adjustedVsize * 4), - fee: t.fee, - }; - }); - - // Add high (high fee) decendant weight and fees - if (tx.bestDescendant) { - totalWeight += tx.bestDescendant.weight; - totalFees += tx.bestDescendant.fee; - } - - tx.effectiveFeePerVsize = Math.max(0, totalFees / (totalWeight / 4)); - tx.cpfpChecked = true; - - return { - ancestors: tx.ancestors, - bestDescendant: tx.bestDescendant || null, - }; - } - - - private static findAllParents(tx: MempoolTransactionExtended, memPool: { [txid: string]: MempoolTransactionExtended }): MempoolTransactionExtended[] { - let parents: MempoolTransactionExtended[] = []; - tx.vin.forEach((parent) => { - if (parents.find((p) => p.txid === parent.txid)) { - return; - } - - const parentTx = memPool[parent.txid]; - if (parentTx) { - if (tx.bestDescendant && tx.bestDescendant.fee / (tx.bestDescendant.weight / 4) > parentTx.adjustedFeePerVsize) { - if (parentTx.bestDescendant && parentTx.bestDescendant.fee < tx.fee + tx.bestDescendant.fee) { - parentTx.bestDescendant = { - weight: (tx.adjustedVsize * 4) + tx.bestDescendant.weight, - fee: tx.fee + tx.bestDescendant.fee, - txid: tx.txid, - }; - } - } else if (tx.adjustedFeePerVsize > parentTx.adjustedFeePerVsize) { - parentTx.bestDescendant = { - weight: (tx.adjustedVsize * 4), - fee: tx.fee, - txid: tx.txid - }; - } - parents.push(parentTx); - parents = parents.concat(this.findAllParents(parentTx, memPool)); - } - }); - return parents; - } - // calculates the ratio of matched transactions to projected transactions by weight static getSimilarity(projectedBlock: MempoolBlockWithTransactions, transactions: TransactionExtended[]): number { let matchedWeight = 0; @@ -508,6 +630,13 @@ export class Common { ); } + static gogglesIndexingEnabled(): boolean { + return ( + Common.blocksSummariesIndexingEnabled() && + config.MEMPOOL.GOGGLES_INDEXING === true + ); + } + static cpfpIndexingEnabled(): boolean { return ( Common.indexingEnabled() && diff --git a/backend/src/api/cpfp.ts b/backend/src/api/cpfp.ts new file mode 100644 index 000000000..604c1b3c9 --- /dev/null +++ b/backend/src/api/cpfp.ts @@ -0,0 +1,286 @@ +import { CpfpInfo, MempoolTransactionExtended } from '../mempool.interfaces'; +import memPool from './mempool'; + +const CPFP_UPDATE_INTERVAL = 60_000; // update CPFP info at most once per 60s per transaction +const MAX_GRAPH_SIZE = 50; // the maximum number of in-mempool relatives to consider + +interface GraphTx extends MempoolTransactionExtended { + depends: string[]; + spentby: string[]; + ancestorMap: Map; + fees: { + base: number; + ancestor: number; + }; + ancestorcount: number; + ancestorsize: number; + ancestorRate: number; + individualRate: number; + score: number; +} + +/** + * Takes a mempool transaction and a copy of the current mempool, and calculates the CPFP data for + * that transaction (and all others in the same cluster) + */ +export function calculateCpfp(tx: MempoolTransactionExtended, mempool: { [txid: string]: MempoolTransactionExtended }): CpfpInfo { + if (tx.cpfpUpdated && Date.now() < (tx.cpfpUpdated + CPFP_UPDATE_INTERVAL)) { + tx.cpfpDirty = false; + return { + ancestors: tx.ancestors || [], + bestDescendant: tx.bestDescendant || null, + descendants: tx.descendants || [], + effectiveFeePerVsize: tx.effectiveFeePerVsize || tx.adjustedFeePerVsize || tx.feePerVsize, + sigops: tx.sigops, + adjustedVsize: tx.adjustedVsize, + acceleration: tx.acceleration + }; + } + + const ancestorMap = new Map(); + const graphTx = mempoolToGraphTx(tx); + ancestorMap.set(tx.txid, graphTx); + + const allRelatives = expandRelativesGraph(mempool, ancestorMap); + const relativesMap = initializeRelatives(allRelatives); + const cluster = calculateCpfpCluster(tx.txid, relativesMap); + + let totalVsize = 0; + let totalFee = 0; + for (const tx of cluster.values()) { + totalVsize += tx.adjustedVsize; + totalFee += tx.fee; + } + const effectiveFeePerVsize = totalFee / totalVsize; + for (const tx of cluster.values()) { + mempool[tx.txid].effectiveFeePerVsize = effectiveFeePerVsize; + mempool[tx.txid].ancestors = Array.from(tx.ancestorMap.values()).map(tx => ({ txid: tx.txid, weight: tx.weight, fee: tx.fee })); + mempool[tx.txid].descendants = Array.from(cluster.values()).filter(entry => entry.txid !== tx.txid && !tx.ancestorMap.has(entry.txid)).map(tx => ({ txid: tx.txid, weight: tx.weight, fee: tx.fee })); + mempool[tx.txid].bestDescendant = null; + mempool[tx.txid].cpfpChecked = true; + mempool[tx.txid].cpfpDirty = true; + mempool[tx.txid].cpfpUpdated = Date.now(); + } + + tx = mempool[tx.txid]; + + return { + ancestors: tx.ancestors || [], + bestDescendant: tx.bestDescendant || null, + descendants: tx.descendants || [], + effectiveFeePerVsize: tx.effectiveFeePerVsize || tx.adjustedFeePerVsize || tx.feePerVsize, + sigops: tx.sigops, + adjustedVsize: tx.adjustedVsize, + acceleration: tx.acceleration + }; +} + +function mempoolToGraphTx(tx: MempoolTransactionExtended): GraphTx { + return { + ...tx, + depends: tx.vin.map(v => v.txid), + spentby: tx.vout.map((v, i) => memPool.getFromSpendMap(tx.txid, i)).map(tx => tx?.txid).filter(txid => txid != null) as string[], + ancestorMap: new Map(), + fees: { + base: tx.fee, + ancestor: tx.fee, + }, + ancestorcount: 1, + ancestorsize: tx.adjustedVsize, + ancestorRate: 0, + individualRate: 0, + score: 0, + }; +} + +/** + * Takes a map of transaction ancestors, and expands it into a full graph of up to MAX_GRAPH_SIZE in-mempool relatives + */ +function expandRelativesGraph(mempool: { [txid: string]: MempoolTransactionExtended }, ancestors: Map): Map { + const relatives: Map = new Map(); + const stack: GraphTx[] = Array.from(ancestors.values()); + while (stack.length > 0) { + if (relatives.size > MAX_GRAPH_SIZE) { + return relatives; + } + + const nextTx = stack.pop(); + if (!nextTx) { + continue; + } + relatives.set(nextTx.txid, nextTx); + + for (const relativeTxid of [...nextTx.depends, ...nextTx.spentby]) { + if (relatives.has(relativeTxid)) { + // already processed this tx + continue; + } + let mempoolTx = ancestors.get(relativeTxid); + if (!mempoolTx && mempool[relativeTxid]) { + mempoolTx = mempoolToGraphTx(mempool[relativeTxid]); + } + if (mempoolTx) { + stack.push(mempoolTx); + } + } + } + + return relatives; +} + + /** + * Efficiently sets a Map of in-mempool ancestors for each member of an expanded relative graph + * by running setAncestors on each leaf, and caching intermediate results. + * then initializes ancestor data for each transaction + * + * @param all + */ + function initializeRelatives(mempoolTxs: Map): Map { + const visited: Map> = new Map(); + const leaves: GraphTx[] = Array.from(mempoolTxs.values()).filter(entry => entry.spentby.length === 0); + for (const leaf of leaves) { + setAncestors(leaf, mempoolTxs, visited); + } + mempoolTxs.forEach(entry => { + entry.ancestorMap?.forEach(ancestor => { + entry.ancestorcount++; + entry.ancestorsize += ancestor.adjustedVsize; + entry.fees.ancestor += ancestor.fees.base; + }); + setAncestorScores(entry); + }); + return mempoolTxs; +} + +/** + * Given a root transaction and a list of in-mempool ancestors, + * Calculate the CPFP cluster + * + * @param tx + * @param ancestors + */ +function calculateCpfpCluster(txid: string, graph: Map): Map { + const tx = graph.get(txid); + if (!tx) { + return new Map([]); + } + + // Initialize individual & ancestor fee rates + graph.forEach(entry => setAncestorScores(entry)); + + // Sort by descending ancestor score + let sortedRelatives = Array.from(graph.values()).sort(mempoolComparator); + + // Iterate until we reach a cluster that includes our target tx + let maxIterations = MAX_GRAPH_SIZE; + let best = sortedRelatives.shift(); + let bestCluster = new Map(best?.ancestorMap?.entries() || []); + while (sortedRelatives.length && best && (best.txid !== tx.txid && !best.ancestorMap.has(tx.txid)) && maxIterations > 0) { + maxIterations--; + if ((best && best.txid === tx.txid) || (bestCluster && bestCluster.has(tx.txid))) { + break; + } else { + // Remove this cluster (it doesn't include our target tx) + // and update scores, ancestor totals and dependencies for the survivors + removeAncestors(bestCluster, graph); + + // re-sort + sortedRelatives = Array.from(graph.values()).sort(mempoolComparator); + + // Grab the next highest scoring entry + best = sortedRelatives.shift(); + if (best) { + bestCluster = new Map(best?.ancestorMap?.entries() || []); + bestCluster.set(best?.txid, best); + } + } + } + + bestCluster.set(tx.txid, tx); + + return bestCluster; +} + + /** + * Remove a cluster of transactions from an in-mempool dependency graph + * and update the survivors' scores and ancestors + * + * @param cluster + * @param ancestors + */ + function removeAncestors(cluster: Map, all: Map): void { + // remove + cluster.forEach(tx => { + all.delete(tx.txid); + }); + + // update survivors + all.forEach(tx => { + cluster.forEach(remove => { + if (tx.ancestorMap?.has(remove.txid)) { + // remove as dependency + tx.ancestorMap.delete(remove.txid); + tx.depends = tx.depends.filter(parent => parent !== remove.txid); + // update ancestor sizes and fees + tx.ancestorsize -= remove.adjustedVsize; + tx.fees.ancestor -= remove.fees.base; + } + }); + // recalculate fee rates + setAncestorScores(tx); + }); +} + +/** + * Recursively traverses an in-mempool dependency graph, and sets a Map of in-mempool ancestors + * for each transaction. + * + * @param tx + * @param all + */ +function setAncestors(tx: GraphTx, all: Map, visited: Map>, depth: number = 0): Map { + // sanity check for infinite recursion / too many ancestors (should never happen) + if (depth > MAX_GRAPH_SIZE) { + return tx.ancestorMap; + } + + // initialize the ancestor map for this tx + tx.ancestorMap = new Map(); + tx.depends.forEach(parentId => { + const parent = all.get(parentId); + if (parent) { + // add the parent + tx.ancestorMap?.set(parentId, parent); + // check for a cached copy of this parent's ancestors + let ancestors = visited.get(parent.txid); + if (!ancestors) { + // recursively fetch the parent's ancestors + ancestors = setAncestors(parent, all, visited, depth + 1); + } + // and add to this tx's map + ancestors.forEach((ancestor, ancestorId) => { + tx.ancestorMap?.set(ancestorId, ancestor); + }); + } + }); + visited.set(tx.txid, tx.ancestorMap); + + return tx.ancestorMap; +} + +/** + * Take a mempool transaction, and set the fee rates and ancestor score + * + * @param tx + */ +function setAncestorScores(tx: GraphTx): GraphTx { + tx.individualRate = (tx.fees.base * 100_000_000) / tx.adjustedVsize; + tx.ancestorRate = (tx.fees.ancestor * 100_000_000) / tx.ancestorsize; + tx.score = Math.min(tx.individualRate, tx.ancestorRate); + return tx; +} + +// Sort by descending score +function mempoolComparator(a: GraphTx, b: GraphTx): number { + return b.score - a.score; +} \ No newline at end of file diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index 162616af6..ae68582af 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository'; import { RowDataPacket } from 'mysql2'; class DatabaseMigration { - private static currentVersion = 67; + private static currentVersion = 76; private queryTimeout = 3600_000; private statisticsAddedIndexed = false; private uniqueLogs: string[] = []; @@ -566,6 +566,99 @@ class DatabaseMigration { await this.$executeQuery('ALTER TABLE `blocks_templates` ADD INDEX `version` (`version`)'); await this.updateToSchemaVersion(67); } + + if (databaseSchemaVersion < 68 && config.MEMPOOL.NETWORK === "liquid") { + await this.$executeQuery('TRUNCATE TABLE elements_pegs'); + await this.$executeQuery('ALTER TABLE elements_pegs ADD PRIMARY KEY (txid, txindex);'); + await this.$executeQuery(`UPDATE state SET number = 0 WHERE name = 'last_elements_block';`); + // Create the federation_addresses table and add the two Liquid Federation change addresses in + await this.$executeQuery(this.getCreateFederationAddressesTableQuery(), await this.$checkIfTableExists('federation_addresses')); + await this.$executeQuery(`INSERT INTO federation_addresses (bitcoinaddress) VALUES ('bc1qxvay4an52gcghxq5lavact7r6qe9l4laedsazz8fj2ee2cy47tlqff4aj4')`); // Federation change address + await this.$executeQuery(`INSERT INTO federation_addresses (bitcoinaddress) VALUES ('3EiAcrzq1cELXScc98KeCswGWZaPGceT1d')`); // Federation change address + // Create the federation_txos table that uses the federation_addresses table as a foreign key + await this.$executeQuery(this.getCreateFederationTxosTableQuery(), await this.$checkIfTableExists('federation_txos')); + await this.$executeQuery(`INSERT INTO state VALUES('last_bitcoin_block_audit', 0, NULL);`); + await this.updateToSchemaVersion(68); + } + + if (databaseSchemaVersion < 69 && config.MEMPOOL.NETWORK === 'mainnet') { + await this.$executeQuery(this.getCreateAccelerationsTableQuery(), await this.$checkIfTableExists('accelerations')); + await this.updateToSchemaVersion(69); + } + + if (databaseSchemaVersion < 70 && config.MEMPOOL.NETWORK === 'mainnet') { + await this.$executeQuery('ALTER TABLE accelerations MODIFY COLUMN added DATETIME;'); + await this.updateToSchemaVersion(70); + } + + if (databaseSchemaVersion < 71 && config.MEMPOOL.NETWORK === 'liquid') { + await this.$executeQuery('TRUNCATE TABLE elements_pegs'); + await this.$executeQuery('TRUNCATE TABLE federation_txos'); + await this.$executeQuery('SET FOREIGN_KEY_CHECKS = 0'); + await this.$executeQuery('TRUNCATE TABLE federation_addresses'); + await this.$executeQuery('SET FOREIGN_KEY_CHECKS = 1'); + await this.$executeQuery(`INSERT INTO federation_addresses (bitcoinaddress) VALUES ('bc1qxvay4an52gcghxq5lavact7r6qe9l4laedsazz8fj2ee2cy47tlqff4aj4')`); // Federation change address + await this.$executeQuery(`INSERT INTO federation_addresses (bitcoinaddress) VALUES ('3EiAcrzq1cELXScc98KeCswGWZaPGceT1d')`); // Federation change address + await this.$executeQuery(`UPDATE state SET number = 0 WHERE name = 'last_elements_block';`); + await this.$executeQuery(`UPDATE state SET number = 0 WHERE name = 'last_bitcoin_block_audit';`); + await this.$executeQuery('ALTER TABLE `federation_txos` ADD timelock INT NOT NULL DEFAULT 0'); + await this.$executeQuery('ALTER TABLE `federation_txos` ADD expiredAt INT NOT NULL DEFAULT 0'); + await this.$executeQuery('ALTER TABLE `federation_txos` ADD emergencyKey TINYINT NOT NULL DEFAULT 0'); + await this.updateToSchemaVersion(71); + } + + if (databaseSchemaVersion < 72 && isBitcoin === true) { + // reindex Goggles flags for mined block templates above height 832000 + await this.$executeQuery('UPDATE blocks_summaries SET version = 0 WHERE height >= 832000;'); + await this.updateToSchemaVersion(72); + } + + if (databaseSchemaVersion < 73 && config.MEMPOOL.NETWORK === 'mainnet') { + // Clear bad data + await this.$executeQuery(`TRUNCATE accelerations`); + this.uniqueLog(logger.notice, `'accelerations' table has been truncated`); + await this.updateToSchemaVersion(73); + } + + if (databaseSchemaVersion < 74 && config.MEMPOOL.NETWORK === 'mainnet') { + await this.$executeQuery(`INSERT INTO state(name, number) VALUE ('last_acceleration_block', 0);`); + await this.updateToSchemaVersion(74); + } + + if (databaseSchemaVersion < 75) { + await this.$executeQuery('ALTER TABLE `prices` ADD `BGN` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `BRL` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `CNY` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `CZK` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `DKK` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `HKD` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `HRK` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `HUF` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `IDR` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `ILS` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `INR` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `ISK` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `KRW` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `MXN` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `MYR` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `NOK` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `NZD` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `PHP` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `PLN` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `RON` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `RUB` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `SEK` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `SGD` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `THB` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `TRY` float DEFAULT "-1"'); + await this.$executeQuery('ALTER TABLE `prices` ADD `ZAR` float DEFAULT "-1"'); + await this.updateToSchemaVersion(75); + } + + if (databaseSchemaVersion < 76 && isBitcoin === true) { + await this.$executeQuery('ALTER TABLE `blocks_audits` ADD prioritized_txs JSON DEFAULT "[]"'); + await this.updateToSchemaVersion(76); + } } /** @@ -813,6 +906,32 @@ class DatabaseMigration { ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`; } + private getCreateFederationAddressesTableQuery(): string { + return `CREATE TABLE IF NOT EXISTS federation_addresses ( + bitcoinaddress varchar(100) NOT NULL, + PRIMARY KEY (bitcoinaddress) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`; + } + + private getCreateFederationTxosTableQuery(): string { + return `CREATE TABLE IF NOT EXISTS federation_txos ( + txid varchar(65) NOT NULL, + txindex int(11) NOT NULL, + bitcoinaddress varchar(100) NOT NULL, + amount bigint(20) unsigned NOT NULL, + blocknumber int(11) unsigned NOT NULL, + blocktime int(11) unsigned NOT NULL, + unspent tinyint(1) NOT NULL, + lastblockupdate int(11) unsigned NOT NULL, + lasttimeupdate int(11) unsigned NOT NULL, + pegtxid varchar(65) NOT NULL, + pegindex int(11) NOT NULL, + pegblocktime int(11) unsigned NOT NULL, + PRIMARY KEY (txid, txindex), + FOREIGN KEY (bitcoinaddress) REFERENCES federation_addresses (bitcoinaddress) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`; + } + private getCreatePoolsTableQuery(): string { return `CREATE TABLE IF NOT EXISTS pools ( id int(11) NOT NULL AUTO_INCREMENT, @@ -1083,6 +1202,23 @@ class DatabaseMigration { ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`; } + private getCreateAccelerationsTableQuery(): string { + return `CREATE TABLE IF NOT EXISTS accelerations ( + txid varchar(65) NOT NULL, + added datetime NOT NULL, + height int(10) NOT NULL, + pool smallint unsigned NULL, + effective_vsize int(10) NOT NULL, + effective_fee bigint(20) unsigned NOT NULL, + boost_rate float unsigned, + boost_cost bigint(20) unsigned NOT NULL, + PRIMARY KEY (txid), + INDEX (added), + INDEX (height), + INDEX (pool) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`; + } + public async $blocksReindexingTruncate(): Promise { logger.warn(`Truncating pools, blocks, hashrates and difficulty_adjustments tables for re-indexing (using '--reindex-blocks'). You can cancel this command within 5 seconds`); await Common.sleep$(5000); diff --git a/backend/src/api/difficulty-adjustment.ts b/backend/src/api/difficulty-adjustment.ts index d93a5f91a..2f3b21029 100644 --- a/backend/src/api/difficulty-adjustment.ts +++ b/backend/src/api/difficulty-adjustment.ts @@ -12,6 +12,7 @@ export interface DifficultyAdjustment { previousTime: number; // Unix time in ms nextRetargetHeight: number; // Block Height timeAvg: number; // Duration of time in ms + adjustedTimeAvg; // Expected block interval with hashrate implied over last 504 blocks timeOffset: number; // (Testnet) Time since last block (cap @ 20min) in ms expectedBlocks: number; // Block count } @@ -80,6 +81,7 @@ export function calcBitsDifference(oldBits: number, newBits: number): number { export function calcDifficultyAdjustment( DATime: number, + quarterEpochTime: number | null, nowSeconds: number, blockHeight: number, previousRetarget: number, @@ -100,8 +102,20 @@ export function calcDifficultyAdjustment( let difficultyChange = 0; let timeAvgSecs = blocksInEpoch ? diffSeconds / blocksInEpoch : BLOCK_SECONDS_TARGET; + let adjustedTimeAvgSecs = timeAvgSecs; + + // for the first 504 blocks of the epoch, calculate the expected avg block interval + // from a sliding window over the last 504 blocks + if (quarterEpochTime && blocksInEpoch < 503) { + const timeLastEpoch = DATime - quarterEpochTime; + const adjustedTimeLastEpoch = timeLastEpoch * (1 + (previousRetarget / 100)); + const adjustedTimeSpan = diffSeconds + adjustedTimeLastEpoch; + adjustedTimeAvgSecs = adjustedTimeSpan / 503; + difficultyChange = (BLOCK_SECONDS_TARGET / (adjustedTimeSpan / 504) - 1) * 100; + } else { + difficultyChange = (BLOCK_SECONDS_TARGET / (actualTimespan / (blocksInEpoch + 1)) - 1) * 100; + } - difficultyChange = (BLOCK_SECONDS_TARGET / (actualTimespan / (blocksInEpoch + 1)) - 1) * 100; // Max increase is x4 (+300%) if (difficultyChange > 300) { difficultyChange = 300; @@ -126,7 +140,8 @@ export function calcDifficultyAdjustment( } const timeAvg = Math.floor(timeAvgSecs * 1000); - const remainingTime = remainingBlocks * timeAvg; + const adjustedTimeAvg = Math.floor(adjustedTimeAvgSecs * 1000); + const remainingTime = remainingBlocks * adjustedTimeAvg; const estimatedRetargetDate = remainingTime + nowSeconds * 1000; return { @@ -139,6 +154,7 @@ export function calcDifficultyAdjustment( previousTime: DATime, nextRetargetHeight, timeAvg, + adjustedTimeAvg, timeOffset, expectedBlocks, }; @@ -155,9 +171,10 @@ class DifficultyAdjustmentApi { return null; } const nowSeconds = Math.floor(new Date().getTime() / 1000); + const quarterEpochBlockTime = blocks.getQuarterEpochBlockTime(); return calcDifficultyAdjustment( - DATime, nowSeconds, blockHeight, previousRetarget, + DATime, quarterEpochBlockTime, nowSeconds, blockHeight, previousRetarget, config.MEMPOOL.NETWORK, latestBlock.timestamp ); } diff --git a/backend/src/api/explorer/channels.api.ts b/backend/src/api/explorer/channels.api.ts index cf38610b4..2faf06c33 100644 --- a/backend/src/api/explorer/channels.api.ts +++ b/backend/src/api/explorer/channels.api.ts @@ -81,7 +81,7 @@ class ChannelsApi { public async $searchChannelsById(search: string): Promise { try { // restrict search to valid id/short_id prefix formats - let searchStripped = search.match(/[0-9]+[0-9x]*/)?.[0] || ''; + let searchStripped = search.match(/^[0-9]+[0-9x]*$/)?.[0] || ''; if (!searchStripped.length) { return []; } diff --git a/backend/src/api/liquid/elements-parser.ts b/backend/src/api/liquid/elements-parser.ts index 12439e037..727865b95 100644 --- a/backend/src/api/liquid/elements-parser.ts +++ b/backend/src/api/liquid/elements-parser.ts @@ -5,8 +5,12 @@ import { Common } from '../common'; import DB from '../../database'; import logger from '../../logger'; +const federationChangeAddresses = ['bc1qxvay4an52gcghxq5lavact7r6qe9l4laedsazz8fj2ee2cy47tlqff4aj4', '3EiAcrzq1cELXScc98KeCswGWZaPGceT1d']; +const auditBlockOffsetWithTip = 1; // Wait for 1 block confirmation before processing the block in the audit process to reduce the risk of reorgs + class ElementsParser { private isRunning = false; + private isUtxosUpdatingRunning = false; constructor() { } @@ -32,12 +36,6 @@ class ElementsParser { } } - public async $getPegDataByMonth(): Promise { - const query = `SELECT SUM(amount) AS amount, DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y-%m-01') AS date FROM elements_pegs GROUP BY DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y%m')`; - const [rows] = await DB.query(query); - return rows; - } - protected async $parseBlock(block: IBitcoinApi.Block) { for (const tx of block.tx) { await this.$parseInputs(tx, block); @@ -55,29 +53,30 @@ class ElementsParser { protected async $parsePegIn(input: IBitcoinApi.Vin, vindex: number, txid: string, block: IBitcoinApi.Block) { const bitcoinTx: IBitcoinApi.Transaction = await bitcoinSecondClient.getRawTransaction(input.txid, true); + const bitcoinBlock: IBitcoinApi.Block = await bitcoinSecondClient.getBlock(bitcoinTx.blockhash); const prevout = bitcoinTx.vout[input.vout || 0]; const outputAddress = prevout.scriptPubKey.address || (prevout.scriptPubKey.addresses && prevout.scriptPubKey.addresses[0]) || ''; await this.$savePegToDatabase(block.height, block.time, prevout.value * 100000000, txid, vindex, - outputAddress, bitcoinTx.txid, prevout.n, 1); + outputAddress, bitcoinTx.txid, prevout.n, bitcoinBlock.height, bitcoinBlock.time, 1); } protected async $parseOutputs(tx: IBitcoinApi.Transaction, block: IBitcoinApi.Block) { for (const output of tx.vout) { if (output.scriptPubKey.pegout_chain) { await this.$savePegToDatabase(block.height, block.time, 0 - output.value * 100000000, tx.txid, output.n, - (output.scriptPubKey.pegout_addresses && output.scriptPubKey.pegout_addresses[0] || ''), '', 0, 0); + (output.scriptPubKey.pegout_address || ''), '', 0, 0, 0, 0); } if (!output.scriptPubKey.pegout_chain && output.scriptPubKey.type === 'nulldata' && output.value && output.value > 0 && output.asset && output.asset === Common.nativeAssetId) { await this.$savePegToDatabase(block.height, block.time, 0 - output.value * 100000000, tx.txid, output.n, - (output.scriptPubKey.pegout_addresses && output.scriptPubKey.pegout_addresses[0] || ''), '', 0, 1); + (output.scriptPubKey.pegout_address || ''), '', 0, 0, 0, 1); } } } protected async $savePegToDatabase(height: number, blockTime: number, amount: number, txid: string, - txindex: number, bitcoinaddress: string, bitcointxid: string, bitcoinindex: number, final_tx: number): Promise { - const query = `INSERT INTO elements_pegs( + txindex: number, bitcoinaddress: string, bitcointxid: string, bitcoinindex: number, bitcoinblock: number, bitcoinBlockTime: number, final_tx: number): Promise { + const query = `INSERT IGNORE INTO elements_pegs( block, datetime, amount, txid, txindex, bitcoinaddress, bitcointxid, bitcoinindex, final_tx ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`; @@ -85,7 +84,22 @@ class ElementsParser { height, blockTime, amount, txid, txindex, bitcoinaddress, bitcointxid, bitcoinindex, final_tx ]; await DB.query(query, params); - logger.debug(`Saved L-BTC peg from block height #${height} with TXID ${txid}.`); + logger.debug(`Saved L-BTC peg from Liquid block height #${height} with TXID ${txid}.`); + + if (amount > 0) { // Peg-in + + // Add the address to the federation addresses table + await DB.query(`INSERT IGNORE INTO federation_addresses (bitcoinaddress) VALUES (?)`, [bitcoinaddress]); + + // Add the UTXO to the federation txos table + const query_utxos = `INSERT IGNORE INTO federation_txos (txid, txindex, bitcoinaddress, amount, blocknumber, blocktime, unspent, lastblockupdate, lasttimeupdate, timelock, expiredAt, emergencyKey, pegtxid, pegindex, pegblocktime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; + const params_utxos: (string | number)[] = [bitcointxid, bitcoinindex, bitcoinaddress, amount, bitcoinblock, bitcoinBlockTime, 1, bitcoinblock - 1, 0, 4032, 0, 0, txid, txindex, blockTime]; + await DB.query(query_utxos, params_utxos); + const [minBlockUpdate] = await DB.query(`SELECT MIN(lastblockupdate) AS lastblockupdate FROM federation_txos WHERE unspent = 1`) + await this.$saveLastBlockAuditToDatabase(minBlockUpdate[0]['lastblockupdate']); + logger.debug(`Saved new Federation UTXO ${bitcointxid}:${bitcoinindex} belonging to ${bitcoinaddress} to federation txos`); + + } } protected async $getLatestBlockHeightFromDatabase(): Promise { @@ -98,6 +112,382 @@ class ElementsParser { const query = `UPDATE state SET number = ? WHERE name = 'last_elements_block'`; await DB.query(query, [blockHeight]); } + + ///////////// FEDERATION AUDIT ////////////// + + public async $updateFederationUtxos() { + if (this.isUtxosUpdatingRunning) { + return; + } + + this.isUtxosUpdatingRunning = true; + + try { + let auditProgress = await this.$getAuditProgress(); + // If no peg in transaction was found in the database, return + if (!auditProgress.lastBlockAudit) { + logger.debug(`No Federation UTXOs found in the database. Waiting for some to be confirmed before starting the Federation UTXOs audit`); + this.isUtxosUpdatingRunning = false; + return; + } + + const bitcoinBlocksToSync = await this.$getBitcoinBlockchainState(); + // If the bitcoin blockchain is not synced yet, return + if (bitcoinBlocksToSync.bitcoinHeaders > bitcoinBlocksToSync.bitcoinBlocks + 1) { + logger.debug(`Bitcoin client is not synced yet. ${bitcoinBlocksToSync.bitcoinHeaders - bitcoinBlocksToSync.bitcoinBlocks} blocks remaining to sync before the Federation audit process can start`); + this.isUtxosUpdatingRunning = false; + return; + } + + auditProgress.lastBlockAudit++; + + // Logging + let indexedThisRun = 0; + let timer = Date.now() / 1000; + const startedAt = Date.now() / 1000; + const indexingSpeeds: number[] = []; + + while (auditProgress.lastBlockAudit <= auditProgress.confirmedTip) { + + // First, get the current UTXOs that need to be scanned in the block + const utxos = await this.$getFederationUtxosToScan(auditProgress.lastBlockAudit); + + // Get the peg-out addresses that need to be scanned + const redeemAddresses = await this.$getRedeemAddressesToScan(); + + // The fast way: check if these UTXOs are still unspent as of the current block with gettxout + let spentAsTip: any[]; + let unspentAsTip: any[]; + if (auditProgress.confirmedTip - auditProgress.lastBlockAudit <= 150) { // If the audit status is not too far in the past, we can use gettxout (fast way) + const utxosToParse = await this.$getFederationUtxosToParse(utxos); + spentAsTip = utxosToParse.spentAsTip; + unspentAsTip = utxosToParse.unspentAsTip; + logger.debug(`Found ${utxos.length} Federation UTXOs and ${redeemAddresses.length} Peg-Out Addresses to scan in Bitcoin block height #${auditProgress.lastBlockAudit} / #${auditProgress.confirmedTip}`); + logger.debug(`${unspentAsTip.length} / ${utxos.length} Federation UTXOs are unspent as of tip`); + } else { // If the audit status is too far in the past, it is useless and wasteful to look for still unspent txos since they will all be spent as of the tip + spentAsTip = utxos; + unspentAsTip = []; + + // Logging + const elapsedSeconds = (Date.now() / 1000) - timer; + if (elapsedSeconds > 5) { + const runningFor = (Date.now() / 1000) - startedAt; + const blockPerSeconds = indexedThisRun / elapsedSeconds; + indexingSpeeds.push(blockPerSeconds); + if (indexingSpeeds.length > 100) indexingSpeeds.shift(); // Keep the length of the up to 100 last indexing speeds + const meanIndexingSpeed = indexingSpeeds.reduce((a, b) => a + b, 0) / indexingSpeeds.length; + const eta = (auditProgress.confirmedTip - auditProgress.lastBlockAudit) / meanIndexingSpeed; + logger.debug(`Scanning ${utxos.length} Federation UTXOs and ${redeemAddresses.length} Peg-Out Addresses at Bitcoin block height #${auditProgress.lastBlockAudit} / #${auditProgress.confirmedTip} | ~${meanIndexingSpeed.toFixed(2)} blocks/sec | elapsed: ${(runningFor / 60).toFixed(0)} minutes | ETA: ${(eta / 60).toFixed(0)} minutes`); + timer = Date.now() / 1000; + indexedThisRun = 0; + } + } + + // The slow way: parse the block to look for the spending tx + const blockHash: IBitcoinApi.ChainTips = await bitcoinSecondClient.getBlockHash(auditProgress.lastBlockAudit); + const block: IBitcoinApi.Block = await bitcoinSecondClient.getBlock(blockHash, 2); + await this.$parseBitcoinBlock(block, spentAsTip, unspentAsTip, auditProgress.confirmedTip, redeemAddresses); + + // Finally, update the lastblockupdate of the remaining UTXOs and save to the database + const [minBlockUpdate] = await DB.query(`SELECT MIN(lastblockupdate) AS lastblockupdate FROM federation_txos WHERE unspent = 1`) + await this.$saveLastBlockAuditToDatabase(minBlockUpdate[0]['lastblockupdate']); + + auditProgress = await this.$getAuditProgress(); + auditProgress.lastBlockAudit++; + indexedThisRun++; + } + + this.isUtxosUpdatingRunning = false; + } catch (e) { + this.isUtxosUpdatingRunning = false; + throw new Error(e instanceof Error ? e.message : 'Error'); + } + } + + // Get the UTXOs that need to be scanned in block height (UTXOs that were last updated in the block height - 1) + protected async $getFederationUtxosToScan(height: number) { + const query = `SELECT txid, txindex, bitcoinaddress, amount, blocknumber, timelock, expiredAt FROM federation_txos WHERE lastblockupdate = ? AND unspent = 1`; + const [rows] = await DB.query(query, [height - 1]); + return rows as any[]; + } + + // Returns the UTXOs that are spent as of tip and need to be scanned + protected async $getFederationUtxosToParse(utxos: any[]): Promise { + const spentAsTip: any[] = []; + const unspentAsTip: any[] = []; + + for (const utxo of utxos) { + const result = await bitcoinSecondClient.getTxOut(utxo.txid, utxo.txindex, false); + result ? unspentAsTip.push(utxo) : spentAsTip.push(utxo); + } + + return {spentAsTip, unspentAsTip}; + } + + protected async $parseBitcoinBlock(block: IBitcoinApi.Block, spentAsTip: any[], unspentAsTip: any[], confirmedTip: number, redeemAddressesData: any[] = []) { + const redeemAddresses: string[] = redeemAddressesData.map(redeemAddress => redeemAddress.bitcoinaddress); + for (const tx of block.tx) { + let mightRedeemInThisTx = false; + // Check if the Federation UTXOs that was spent as of tip are spent in this block + for (const input of tx.vin) { + const txo = spentAsTip.find(txo => txo.txid === input.txid && txo.txindex === input.vout); + if (txo) { + mightRedeemInThisTx = true; // A Federation UTXO is spent in this block: we might find a peg-out address in the outputs + if (txo.expiredAt > 0 ) { + if (input.txinwitness?.length !== 13) { // Check if the witness data of the input contains the 11 signatures: if it doesn't, emergency keys are being used + await DB.query(`UPDATE federation_txos SET unspent = 0, lastblockupdate = ?, lasttimeupdate = ?, emergencyKey = 1 WHERE txid = ? AND txindex = ?`, [block.height, block.time, txo.txid, txo.txindex]); + logger.debug(`Expired Federation UTXO ${txo.txid}:${txo.txindex} (${txo.amount} sats) was spent in block ${block.height} using emergency keys!`); + } else { + await DB.query(`UPDATE federation_txos SET unspent = 0, lastblockupdate = ?, lasttimeupdate = ? WHERE txid = ? AND txindex = ?`, [block.height, block.time, txo.txid, txo.txindex]); + logger.debug(`Expired Federation UTXO ${txo.txid}:${txo.txindex} (${txo.amount} sats) was spent in block ${block.height} using regular 11-of-15 signatures`); + } + } else { + await DB.query(`UPDATE federation_txos SET unspent = 0, lastblockupdate = ?, lasttimeupdate = ? WHERE txid = ? AND txindex = ?`, [block.height, block.time, txo.txid, txo.txindex]); + logger.debug(`Federation UTXO ${txo.txid}:${txo.txindex} (${txo.amount} sats) was spent in block ${block.height}`); + } + // Remove the TXO from the utxo array + spentAsTip.splice(spentAsTip.indexOf(txo), 1); + } + } + // Check if an output is sent to a change address of the federation + for (const output of tx.vout) { + if (output.scriptPubKey.address && federationChangeAddresses.includes(output.scriptPubKey.address)) { + // Check that the UTXO was not already added in the DB by previous scans + const [rows_check] = await DB.query(`SELECT txid FROM federation_txos WHERE txid = ? AND txindex = ?`, [tx.txid, output.n]) as any[]; + if (rows_check.length === 0) { + const timelock = output.scriptPubKey.address === federationChangeAddresses[0] ? 4032 : 2016; // P2WSH change address has a 4032 timelock, P2SH change address has a 2016 timelock + const query_utxos = `INSERT INTO federation_txos (txid, txindex, bitcoinaddress, amount, blocknumber, blocktime, unspent, lastblockupdate, lasttimeupdate, timelock, expiredAt, emergencyKey, pegtxid, pegindex, pegblocktime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; + const params_utxos: (string | number)[] = [tx.txid, output.n, output.scriptPubKey.address, output.value * 100000000, block.height, block.time, 1, block.height, 0, timelock, 0, 0, '', 0, 0]; + await DB.query(query_utxos, params_utxos); + // Add the UTXO to the utxo array + spentAsTip.push({ + txid: tx.txid, + txindex: output.n, + bitcoinaddress: output.scriptPubKey.address, + amount: output.value * 100000000, + blocknumber: block.height, + timelock: timelock, + expiredAt: 0, + }); + logger.debug(`Added new Federation UTXO ${tx.txid}:${output.n} (${Math.round(output.value * 100000000)} sats), change address: ${output.scriptPubKey.address}`); + } + } + if (mightRedeemInThisTx && output.scriptPubKey.address && redeemAddresses.includes(output.scriptPubKey.address)) { + // Find the number of times output.scriptPubKey.address appears in redeemAddresses. There can be address reuse for peg-outs... + const matchingAddress: any[] = redeemAddressesData.filter(redeemAddress => redeemAddress.bitcoinaddress === output.scriptPubKey.address && -redeemAddress.amount === Math.round(output.value * 100000000)); + if (matchingAddress.length > 0) { + if (matchingAddress.length > 1) { + // If there are more than one peg out address with the same amount, we can't know which one redeemed the UTXO: we take the oldest one + matchingAddress.sort((a, b) => a.datetime - b.datetime); + logger.debug(`Found redeem txid ${tx.txid}:${output.n} to peg-out address ${matchingAddress[0].bitcoinaddress}, amount ${matchingAddress[0].amount}, datetime ${matchingAddress[0].datetime}`); + } else { + logger.debug(`Found redeem txid ${tx.txid}:${output.n} to peg-out address ${matchingAddress[0].bitcoinaddress}, amount ${matchingAddress[0].amount}`); + } + const query_add_redeem = `UPDATE elements_pegs SET bitcointxid = ?, bitcoinindex = ? WHERE bitcoinaddress = ? AND amount = ? AND datetime = ?`; + const params_add_redeem: (string | number)[] = [tx.txid, output.n, matchingAddress[0].bitcoinaddress, matchingAddress[0].amount, matchingAddress[0].datetime]; + await DB.query(query_add_redeem, params_add_redeem); + const index = redeemAddressesData.indexOf(matchingAddress[0]); + redeemAddressesData.splice(index, 1); + redeemAddresses.splice(index, 1); + } else { // The output amount does not match the peg-out amount... log it + logger.debug(`Found redeem txid ${tx.txid}:${output.n} to peg-out address ${output.scriptPubKey.address} but output amount ${Math.round(output.value * 100000000)} does not match the peg-out amount!`); + } + } + } + } + + for (const utxo of spentAsTip) { + if (utxo.expiredAt === 0 && block.height >= utxo.blocknumber + utxo.timelock) { // The UTXO is expiring in this block + await DB.query(`UPDATE federation_txos SET lastblockupdate = ?, expiredAt = ? WHERE txid = ? AND txindex = ?`, [block.height, block.time, utxo.txid, utxo.txindex]); + } else { + await DB.query(`UPDATE federation_txos SET lastblockupdate = ? WHERE txid = ? AND txindex = ?`, [block.height, utxo.txid, utxo.txindex]); + } + } + + for (const utxo of unspentAsTip) { + if (utxo.expiredAt === 0 && block.height >= utxo.blocknumber + utxo.timelock) { // The UTXO is expiring in this block + await DB.query(`UPDATE federation_txos SET unspent = 0, lastblockupdate = ?, expiredAt = ? WHERE txid = ? AND txindex = ?`, [confirmedTip, block.time, utxo.txid, utxo.txindex]); + } else if (utxo.expiredAt === 0 && confirmedTip >= utxo.blocknumber + utxo.timelock) { // The UTXO is expiring before the tip: we need to keep track of it + await DB.query(`UPDATE federation_txos SET lastblockupdate = ? WHERE txid = ? AND txindex = ?`, [utxo.blocknumber + utxo.timelock - 1, utxo.txid, utxo.txindex]); + } else { + await DB.query(`UPDATE federation_txos SET lastblockupdate = ? WHERE txid = ? AND txindex = ?`, [confirmedTip, utxo.txid, utxo.txindex]); + } + } + } + + protected async $saveLastBlockAuditToDatabase(blockHeight: number) { + const query = `UPDATE state SET number = ? WHERE name = 'last_bitcoin_block_audit'`; + await DB.query(query, [blockHeight]); + } + + // Get the bitcoin block where the audit process was last updated + protected async $getAuditProgress(): Promise { + const lastblockaudit = await this.$getLastBlockAudit(); + const bitcoinBlocksToSync = await this.$getBitcoinBlockchainState(); + return { + lastBlockAudit: lastblockaudit, + confirmedTip: bitcoinBlocksToSync.bitcoinBlocks - auditBlockOffsetWithTip, + }; + } + + // Get the bitcoin blocks remaining to be synced + protected async $getBitcoinBlockchainState(): Promise { + const result = await bitcoinSecondClient.getBlockchainInfo(); + return { + bitcoinBlocks: result.blocks, + bitcoinHeaders: result.headers, + } + } + + protected async $getLastBlockAudit(): Promise { + const query = `SELECT number FROM state WHERE name = 'last_bitcoin_block_audit'`; + const [rows] = await DB.query(query); + return rows[0]['number']; + } + + protected async $getRedeemAddressesToScan(): Promise { + const query = `SELECT datetime, amount, bitcoinaddress FROM elements_pegs where amount < 0 AND bitcoinaddress != '' AND bitcointxid = '';`; + const [rows]: any[] = await DB.query(query); + return rows; + } + + protected isDust(amount: number, feeRate: number): boolean { + return amount <= (450 * feeRate); // A P2WSH 11-of-15 multisig input is around 450 bytes + } + + ///////////// DATA QUERY ////////////// + + public async $getAuditStatus(): Promise { + const lastBlockAudit = await this.$getLastBlockAudit(); + const bitcoinBlocksToSync = await this.$getBitcoinBlockchainState(); + return { + bitcoinBlocks: bitcoinBlocksToSync.bitcoinBlocks, + bitcoinHeaders: bitcoinBlocksToSync.bitcoinHeaders, + lastBlockAudit: lastBlockAudit, + isAuditSynced: bitcoinBlocksToSync.bitcoinHeaders - bitcoinBlocksToSync.bitcoinBlocks <= 2 && bitcoinBlocksToSync.bitcoinBlocks - lastBlockAudit <= 3, + }; + } + + public async $getPegDataByMonth(): Promise { + const query = `SELECT SUM(amount) AS amount, DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y-%m-01') AS date FROM elements_pegs GROUP BY DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y%m')`; + const [rows] = await DB.query(query); + return rows; + } + + public async $getFederationReservesByMonth(): Promise { + const query = ` + SELECT SUM(amount) AS amount, DATE_FORMAT(FROM_UNIXTIME(blocktime), '%Y-%m-01') AS date FROM federation_txos + WHERE + (blocktime > UNIX_TIMESTAMP(LAST_DAY(FROM_UNIXTIME(blocktime) - INTERVAL 1 MONTH) + INTERVAL 1 DAY)) + AND + ((unspent = 1) OR (unspent = 0 AND lasttimeupdate > UNIX_TIMESTAMP(LAST_DAY(FROM_UNIXTIME(blocktime)) + INTERVAL 1 DAY))) + AND + (expiredAt = 0 OR expiredAt > UNIX_TIMESTAMP(LAST_DAY(FROM_UNIXTIME(blocktime)) + INTERVAL 1 DAY)) + GROUP BY + date;`; + const [rows] = await DB.query(query); + return rows; + } + + // Get the current L-BTC pegs and the last Liquid block it was updated + public async $getCurrentLbtcSupply(): Promise { + const [rows] = await DB.query(`SELECT SUM(amount) AS LBTC_supply FROM elements_pegs;`); + const lastblockupdate = await this.$getLatestBlockHeightFromDatabase(); + const hash = await bitcoinClient.getBlockHash(lastblockupdate); + return { + amount: rows[0]['LBTC_supply'], + lastBlockUpdate: lastblockupdate, + hash: hash + }; + } + + // Get the current reserves of the federation and the last Bitcoin block it was updated + public async $getCurrentFederationReserves(): Promise { + const [rows] = await DB.query(`SELECT SUM(amount) AS total_balance FROM federation_txos WHERE unspent = 1 AND expiredAt = 0;`); + const lastblockaudit = await this.$getLastBlockAudit(); + const hash = await bitcoinSecondClient.getBlockHash(lastblockaudit); + return { + amount: rows[0]['total_balance'], + lastBlockUpdate: lastblockaudit, + hash: hash + }; + } + + // Get all of the federation addresses, most balances first + public async $getFederationAddresses(): Promise { + const query = `SELECT bitcoinaddress, SUM(amount) AS balance FROM federation_txos WHERE unspent = 1 AND expiredAt = 0 GROUP BY bitcoinaddress ORDER BY balance DESC;`; + const [rows] = await DB.query(query); + return rows; + } + + // Get all of the UTXOs held by the federation, most recent first + public async $getFederationUtxos(): Promise { + const query = `SELECT txid, txindex, bitcoinaddress, amount, blocknumber, blocktime, pegtxid, pegindex, pegblocktime, timelock, expiredAt FROM federation_txos WHERE unspent = 1 AND expiredAt = 0 ORDER BY blocktime DESC;`; + const [rows] = await DB.query(query); + return rows; + } + + // Get expired UTXOs, most recent first + public async $getExpiredUtxos(): Promise { + const query = `SELECT txid, txindex, bitcoinaddress, amount, blocknumber, blocktime, pegtxid, pegindex, pegblocktime, timelock, expiredAt FROM federation_txos WHERE unspent = 1 AND expiredAt > 0 ORDER BY blocktime DESC;`; + const [rows]: any[] = await DB.query(query); + const feeRate = Math.round((await bitcoinSecondClient.estimateSmartFee(1)).feerate * 100000000 / 1000); + for (const row of rows) { + row.isDust = this.isDust(row.amount, feeRate); + } + return rows; + } + + // Get utxos that were spent using emergency keys + public async $getEmergencySpentUtxos(): Promise { + const query = `SELECT txid, txindex, bitcoinaddress, amount, blocknumber, blocktime, pegtxid, pegindex, pegblocktime, timelock, expiredAt FROM federation_txos WHERE emergencyKey = 1 ORDER BY blocktime DESC;`; + const [rows] = await DB.query(query); + return rows; + } + + // Get the total number of federation addresses + public async $getFederationAddressesNumber(): Promise { + const query = `SELECT COUNT(DISTINCT bitcoinaddress) AS address_count FROM federation_txos WHERE unspent = 1 AND expiredAt = 0;`; + const [rows] = await DB.query(query); + return rows[0]; + } + + // Get the total number of federation utxos + public async $getFederationUtxosNumber(): Promise { + const query = `SELECT COUNT(*) AS utxo_count FROM federation_txos WHERE unspent = 1 AND expiredAt = 0;`; + const [rows] = await DB.query(query); + return rows[0]; + } + + // Get the total number of emergency spent utxos and their total amount + public async $getEmergencySpentUtxosStats(): Promise { + const query = `SELECT COUNT(*) AS utxo_count, SUM(amount) AS total_amount FROM federation_txos WHERE emergencyKey = 1;`; + const [rows] = await DB.query(query); + return rows[0]; + } + + // Get recent pegs in / out + public async $getPegsList(count: number = 0): Promise { + const query = `SELECT txid, txindex, amount, bitcoinaddress, bitcointxid, bitcoinindex, datetime AS blocktime FROM elements_pegs ORDER BY block DESC LIMIT 15 OFFSET ?;`; + const [rows] = await DB.query(query, [count]); + return rows; + } + + // Get all peg in / out from the last month + public async $getPegsVolumeDaily(): Promise { + const pegInQuery = await DB.query(`SELECT SUM(amount) AS volume, COUNT(*) AS number FROM elements_pegs WHERE amount > 0 and datetime > UNIX_TIMESTAMP(TIMESTAMPADD(DAY, -1, CURRENT_TIMESTAMP()));`); + const pegOutQuery = await DB.query(`SELECT SUM(amount) AS volume, COUNT(*) AS number FROM elements_pegs WHERE amount < 0 and datetime > UNIX_TIMESTAMP(TIMESTAMPADD(DAY, -1, CURRENT_TIMESTAMP()));`); + return [ + pegInQuery[0][0], + pegOutQuery[0][0] + ]; + } + + // Get the total pegs number + public async $getPegsCount(): Promise { + const [rows] = await DB.query(`SELECT COUNT(*) AS pegs_count FROM elements_pegs;`); + return rows[0]; + } } export default new ElementsParser(); diff --git a/backend/src/api/liquid/liquid.routes.ts b/backend/src/api/liquid/liquid.routes.ts index b130373e1..9ea61ca31 100644 --- a/backend/src/api/liquid/liquid.routes.ts +++ b/backend/src/api/liquid/liquid.routes.ts @@ -15,7 +15,21 @@ class LiquidRoutes { if (config.DATABASE.ENABLED) { app + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs', this.$getElementsPegs) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/month', this.$getElementsPegsByMonth) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/list/:count', this.$getPegsList) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/volume', this.$getPegsVolumeDaily) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/count', this.$getPegsCount) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves', this.$getFederationReserves) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/month', this.$getFederationReservesByMonth) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/addresses', this.$getFederationAddresses) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/addresses/total', this.$getFederationAddressesNumber) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/utxos', this.$getFederationUtxos) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/utxos/total', this.$getFederationUtxosNumber) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/utxos/expired', this.$getExpiredUtxos) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/utxos/emergency-spent', this.$getEmergencySpentUtxos) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/utxos/emergency-spent/stats', this.$getEmergencySpentUtxosStats) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/status', this.$getFederationAuditStatus) ; } } @@ -63,11 +77,183 @@ class LiquidRoutes { private async $getElementsPegsByMonth(req: Request, res: Response) { try { const pegs = await elementsParser.$getPegDataByMonth(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 60).toUTCString()); res.json(pegs); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); } } + + private async $getFederationReservesByMonth(req: Request, res: Response) { + try { + const reserves = await elementsParser.$getFederationReservesByMonth(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 60).toUTCString()); + res.json(reserves); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getElementsPegs(req: Request, res: Response) { + try { + const currentSupply = await elementsParser.$getCurrentLbtcSupply(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(currentSupply); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getFederationReserves(req: Request, res: Response) { + try { + const currentReserves = await elementsParser.$getCurrentFederationReserves(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(currentReserves); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getFederationAuditStatus(req: Request, res: Response) { + try { + const auditStatus = await elementsParser.$getAuditStatus(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(auditStatus); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getFederationAddresses(req: Request, res: Response) { + try { + const federationAddresses = await elementsParser.$getFederationAddresses(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(federationAddresses); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getFederationAddressesNumber(req: Request, res: Response) { + try { + const federationAddresses = await elementsParser.$getFederationAddressesNumber(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(federationAddresses); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getFederationUtxos(req: Request, res: Response) { + try { + const federationUtxos = await elementsParser.$getFederationUtxos(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(federationUtxos); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getExpiredUtxos(req: Request, res: Response) { + try { + const expiredUtxos = await elementsParser.$getExpiredUtxos(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(expiredUtxos); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getFederationUtxosNumber(req: Request, res: Response) { + try { + const federationUtxos = await elementsParser.$getFederationUtxosNumber(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(federationUtxos); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getEmergencySpentUtxos(req: Request, res: Response) { + try { + const emergencySpentUtxos = await elementsParser.$getEmergencySpentUtxos(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(emergencySpentUtxos); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getEmergencySpentUtxosStats(req: Request, res: Response) { + try { + const emergencySpentUtxos = await elementsParser.$getEmergencySpentUtxosStats(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(emergencySpentUtxos); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getPegsList(req: Request, res: Response) { + try { + const recentPegs = await elementsParser.$getPegsList(parseInt(req.params?.count)); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(recentPegs); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getPegsVolumeDaily(req: Request, res: Response) { + try { + const pegsVolume = await elementsParser.$getPegsVolumeDaily(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(pegsVolume); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getPegsCount(req: Request, res: Response) { + try { + const pegsCount = await elementsParser.$getPegsCount(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(pegsCount); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + } export default new LiquidRoutes(); diff --git a/backend/src/api/mempool-blocks.ts b/backend/src/api/mempool-blocks.ts index 58921fcfb..3af2a9967 100644 --- a/backend/src/api/mempool-blocks.ts +++ b/backend/src/api/mempool-blocks.ts @@ -1,6 +1,6 @@ import { GbtGenerator, GbtResult, ThreadTransaction as RustThreadTransaction, ThreadAcceleration as RustThreadAcceleration } from 'rust-gbt'; import logger from '../logger'; -import { MempoolBlock, MempoolTransactionExtended, MempoolBlockWithTransactions, MempoolBlockDelta, Ancestor, CompactThreadTransaction, EffectiveFeeStats, PoolTag, TransactionClassified } from '../mempool.interfaces'; +import { MempoolBlock, MempoolTransactionExtended, MempoolBlockWithTransactions, MempoolBlockDelta, Ancestor, CompactThreadTransaction, EffectiveFeeStats, TransactionClassified, TransactionCompressed, MempoolDeltaChange, GbtCandidates } from '../mempool.interfaces'; import { Common, OnlineFeeStatsCalculator } from './common'; import config from '../config'; import { Worker } from 'worker_threads'; @@ -18,6 +18,7 @@ class MempoolBlocks { private nextUid: number = 1; private uidMap: Map = new Map(); // map short numerical uids to full txids + private txidMap: Map = new Map(); // map full txids back to short numerical uids public getMempoolBlocks(): MempoolBlock[] { return this.mempoolBlocks.map((block) => { @@ -40,138 +41,12 @@ class MempoolBlocks { return this.mempoolBlockDeltas; } - public updateMempoolBlocks(memPool: { [txid: string]: MempoolTransactionExtended }, saveResults: boolean = false): MempoolBlockWithTransactions[] { - const latestMempool = memPool; - const memPoolArray: MempoolTransactionExtended[] = []; - for (const i in latestMempool) { - memPoolArray.push(latestMempool[i]); - } - const start = new Date().getTime(); - - // Clear bestDescendants & ancestors - memPoolArray.forEach((tx) => { - tx.bestDescendant = null; - tx.ancestors = []; - tx.cpfpChecked = false; - if (!tx.effectiveFeePerVsize) { - tx.effectiveFeePerVsize = tx.adjustedFeePerVsize; - } - }); - - // First sort - memPoolArray.sort((a, b) => { - if (a.adjustedFeePerVsize === b.adjustedFeePerVsize) { - // tie-break by lexicographic txid order for stability - return a.txid < b.txid ? -1 : 1; - } else { - return b.adjustedFeePerVsize - a.adjustedFeePerVsize; - } - }); - - // Loop through and traverse all ancestors and sum up all the sizes + fees - // Pass down size + fee to all unconfirmed children - let sizes = 0; - memPoolArray.forEach((tx) => { - sizes += tx.weight; - if (sizes > 4000000 * 8) { - return; - } - Common.setRelativesAndGetCpfpInfo(tx, memPool); - }); - - // Final sort, by effective fee - memPoolArray.sort((a, b) => { - if (a.effectiveFeePerVsize === b.effectiveFeePerVsize) { - // tie-break by lexicographic txid order for stability - return a.txid < b.txid ? -1 : 1; - } else { - return b.effectiveFeePerVsize - a.effectiveFeePerVsize; - } - }); - - const end = new Date().getTime(); - const time = end - start; - logger.debug('Mempool blocks calculated in ' + time / 1000 + ' seconds'); - - const blocks = this.calculateMempoolBlocks(memPoolArray); - - if (saveResults) { - const deltas = this.calculateMempoolDeltas(this.mempoolBlocks, blocks); - this.mempoolBlocks = blocks; - this.mempoolBlockDeltas = deltas; - } - - return blocks; - } - - private calculateMempoolBlocks(transactionsSorted: MempoolTransactionExtended[]): MempoolBlockWithTransactions[] { - const mempoolBlocks: MempoolBlockWithTransactions[] = []; - let feeStatsCalculator: OnlineFeeStatsCalculator = new OnlineFeeStatsCalculator(config.MEMPOOL.BLOCK_WEIGHT_UNITS); - let onlineStats = false; - let blockSize = 0; - let blockWeight = 0; - let blockVsize = 0; - let blockFees = 0; - const sizeLimit = (config.MEMPOOL.BLOCK_WEIGHT_UNITS / 4) * 1.2; - let transactionIds: string[] = []; - let transactions: MempoolTransactionExtended[] = []; - transactionsSorted.forEach((tx, index) => { - if (blockWeight + tx.weight <= config.MEMPOOL.BLOCK_WEIGHT_UNITS - || mempoolBlocks.length === config.MEMPOOL.MEMPOOL_BLOCKS_AMOUNT - 1) { - tx.position = { - block: mempoolBlocks.length, - vsize: blockVsize + (tx.vsize / 2), - }; - blockWeight += tx.weight; - blockVsize += tx.vsize; - blockSize += tx.size; - blockFees += tx.fee; - if (blockVsize <= sizeLimit) { - transactions.push(tx); - } - transactionIds.push(tx.txid); - if (onlineStats) { - feeStatsCalculator.processNext(tx); - } - } else { - mempoolBlocks.push(this.dataToMempoolBlocks(transactionIds, transactions, blockSize, blockWeight, blockFees)); - blockVsize = 0; - tx.position = { - block: mempoolBlocks.length, - vsize: blockVsize + (tx.vsize / 2), - }; - - if (mempoolBlocks.length === config.MEMPOOL.MEMPOOL_BLOCKS_AMOUNT - 1) { - const stackWeight = transactionsSorted.slice(index).reduce((total, tx) => total + (tx.weight || 0), 0); - if (stackWeight > config.MEMPOOL.BLOCK_WEIGHT_UNITS) { - onlineStats = true; - feeStatsCalculator = new OnlineFeeStatsCalculator(stackWeight, 0.5, [10, 20, 30, 40, 50, 60, 70, 80, 90]); - feeStatsCalculator.processNext(tx); - } - } - - blockVsize += tx.vsize; - blockWeight = tx.weight; - blockSize = tx.size; - blockFees = tx.fee; - transactionIds = [tx.txid]; - transactions = [tx]; - } - }); - if (transactions.length) { - const feeStats = onlineStats ? feeStatsCalculator.getRawFeeStats() : undefined; - mempoolBlocks.push(this.dataToMempoolBlocks(transactionIds, transactions, blockSize, blockWeight, blockFees, feeStats)); - } - - return mempoolBlocks; - } - private calculateMempoolDeltas(prevBlocks: MempoolBlockWithTransactions[], mempoolBlocks: MempoolBlockWithTransactions[]): MempoolBlockDelta[] { const mempoolBlockDeltas: MempoolBlockDelta[] = []; for (let i = 0; i < Math.max(mempoolBlocks.length, prevBlocks.length); i++) { let added: TransactionClassified[] = []; let removed: string[] = []; - const changed: { txid: string, rate: number | undefined, acc: boolean | undefined }[] = []; + const changed: TransactionClassified[] = []; if (mempoolBlocks[i] && !prevBlocks[i]) { added = mempoolBlocks[i].transactions; } else if (!mempoolBlocks[i] && prevBlocks[i]) { @@ -194,20 +69,20 @@ class MempoolBlocks { if (!prevIds[tx.txid]) { added.push(tx); } else if (tx.rate !== prevIds[tx.txid].rate || tx.acc !== prevIds[tx.txid].acc) { - changed.push({ txid: tx.txid, rate: tx.rate, acc: tx.acc }); + changed.push(tx); } }); } mempoolBlockDeltas.push({ - added, + added: added.map(this.compressTx), removed, - changed, + changed: changed.map(this.compressDeltaChange), }); } return mempoolBlockDeltas; } - public async $makeBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, saveResults: boolean = false, useAccelerations: boolean = false, accelerationPool?: number): Promise { + public async $makeBlockTemplates(transactions: string[], newMempool: { [txid: string]: MempoolTransactionExtended }, candidates: GbtCandidates | undefined, saveResults: boolean = false, useAccelerations: boolean = false, accelerationPool?: number): Promise { const start = Date.now(); // reset mempool short ids @@ -215,7 +90,8 @@ class MempoolBlocks { this.resetUids(); } // set missing short ids - for (const tx of Object.values(newMempool)) { + for (const txid of transactions) { + const tx = newMempool[txid]; this.setUid(tx, !saveResults); } @@ -224,7 +100,8 @@ class MempoolBlocks { // prepare a stripped down version of the mempool with only the minimum necessary data // to reduce the overhead of passing this data to the worker thread const strippedMempool: Map = new Map(); - Object.values(newMempool).forEach(entry => { + for (const txid of transactions) { + const entry = newMempool[txid]; if (entry.uid !== null && entry.uid !== undefined) { const stripped = { uid: entry.uid, @@ -237,7 +114,7 @@ class MempoolBlocks { }; strippedMempool.set(entry.uid, stripped); } - }); + } // (re)initialize tx selection worker thread if (!this.txSelectionWorker) { @@ -268,7 +145,7 @@ class MempoolBlocks { // clean up thread error listener this.txSelectionWorker?.removeListener('error', threadErrorListener); - const processed = this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), accelerations, accelerationPool, saveResults); + const processed = this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), candidates, accelerations, accelerationPool, saveResults); logger.debug(`makeBlockTemplates completed in ${(Date.now() - start)/1000} seconds`); @@ -279,10 +156,10 @@ class MempoolBlocks { return this.mempoolBlocks; } - public async $updateBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], accelerationDelta: string[] = [], saveResults: boolean = false, useAccelerations: boolean = false): Promise { + public async $updateBlockTemplates(transactions: string[], newMempool: { [txid: string]: MempoolTransactionExtended }, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], candidates: GbtCandidates | undefined, accelerationDelta: string[] = [], saveResults: boolean = false, useAccelerations: boolean = false): Promise { if (!this.txSelectionWorker) { // need to reset the worker - await this.$makeBlockTemplates(newMempool, saveResults, useAccelerations); + await this.$makeBlockTemplates(transactions, newMempool, candidates, saveResults, useAccelerations); return; } @@ -292,9 +169,9 @@ class MempoolBlocks { const addedAndChanged: MempoolTransactionExtended[] = useAccelerations ? accelerationDelta.map(txid => newMempool[txid]).filter(tx => tx != null).concat(added) : added; for (const tx of addedAndChanged) { - this.setUid(tx, true); + this.setUid(tx, false); } - const removedUids = removed.map(tx => this.getUid(tx)).filter(uid => uid != null) as number[]; + const removedTxs = removed.filter(tx => tx.uid != null) as MempoolTransactionExtended[]; // prepare a stripped down version of the mempool with only the minimum necessary data // to reduce the overhead of passing this data to the worker thread @@ -320,15 +197,15 @@ class MempoolBlocks { }); this.txSelectionWorker?.once('error', reject); }); - this.txSelectionWorker.postMessage({ type: 'update', added: addedStripped, removed: removedUids }); + this.txSelectionWorker.postMessage({ type: 'update', added: addedStripped, removed: removedTxs.map(tx => tx.uid) as number[] }); const { blocks, rates, clusters } = this.convertResultTxids(await workerResultPromise); - this.removeUids(removedUids); + this.removeUids(removedTxs); // clean up thread error listener this.txSelectionWorker?.removeListener('error', threadErrorListener); - this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), accelerations, null, saveResults); + this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), candidates, accelerations, null, saveResults); logger.debug(`updateBlockTemplates completed in ${(Date.now() - start) / 1000} seconds`); } catch (e) { logger.err('updateBlockTemplates failed. ' + (e instanceof Error ? e.message : e)); @@ -340,25 +217,28 @@ class MempoolBlocks { this.rustGbtGenerator = new GbtGenerator(); } - public async $rustMakeBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, saveResults: boolean = false, useAccelerations: boolean = false, accelerationPool?: number): Promise { + public async $rustMakeBlockTemplates(txids: string[], newMempool: { [txid: string]: MempoolTransactionExtended }, candidates: GbtCandidates | undefined, saveResults: boolean = false, useAccelerations: boolean = false, accelerationPool?: number): Promise { const start = Date.now(); // reset mempool short ids if (saveResults) { this.resetUids(); } + + const transactions = txids.map(txid => newMempool[txid]).filter(tx => tx != null); // set missing short ids - for (const tx of Object.values(newMempool)) { + for (const tx of transactions) { this.setUid(tx, !saveResults); } // set short ids for transaction inputs - for (const tx of Object.values(newMempool)) { + for (const tx of transactions) { tx.inputs = tx.vin.map(v => this.getUid(newMempool[v.txid])).filter(uid => (uid !== null && uid !== undefined)) as number[]; } const accelerations = useAccelerations ? mempool.getAccelerations() : {}; const acceleratedList = accelerationPool ? Object.values(accelerations).filter(acc => newMempool[acc.txid] && acc.pools.includes(accelerationPool)) : Object.values(accelerations).filter(acc => newMempool[acc.txid]); const convertedAccelerations = acceleratedList.map(acc => { + this.setUid(newMempool[acc.txid], true); return { uid: this.getUid(newMempool[acc.txid]), delta: acc.feeDelta, @@ -369,15 +249,15 @@ class MempoolBlocks { const rustGbt = saveResults ? this.rustGbtGenerator : new GbtGenerator(); try { const { blocks, blockWeights, rates, clusters, overflow } = this.convertNapiResultTxids( - await rustGbt.make(Object.values(newMempool) as RustThreadTransaction[], convertedAccelerations as RustThreadAcceleration[], this.nextUid), + await rustGbt.make(transactions as RustThreadTransaction[], convertedAccelerations as RustThreadAcceleration[], this.nextUid), ); if (saveResults) { this.rustInitialized = true; } - const mempoolSize = Object.keys(newMempool).length; + const expectedSize = transactions.length; const resultMempoolSize = blocks.reduce((total, block) => total + block.length, 0) + overflow.length; - logger.debug(`RUST updateBlockTemplates returned ${resultMempoolSize} txs out of ${mempoolSize} in the mempool, ${overflow.length} were unmineable`); - const processed = this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, accelerations, accelerationPool, saveResults); + logger.debug(`RUST updateBlockTemplates returned ${resultMempoolSize} txs out of ${expectedSize} in the mempool, ${overflow.length} were unmineable`); + const processed = this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, candidates, accelerations, accelerationPool, saveResults); logger.debug(`RUST makeBlockTemplates completed in ${(Date.now() - start)/1000} seconds`); return processed; } catch (e) { @@ -389,36 +269,37 @@ class MempoolBlocks { return this.mempoolBlocks; } - public async $oneOffRustBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, useAccelerations: boolean, accelerationPool?: number): Promise { - return this.$rustMakeBlockTemplates(newMempool, false, useAccelerations, accelerationPool); + public async $oneOffRustBlockTemplates(transactions: string[], newMempool: { [txid: string]: MempoolTransactionExtended }, candidates: GbtCandidates | undefined, useAccelerations: boolean, accelerationPool?: number): Promise { + return this.$rustMakeBlockTemplates(transactions, newMempool, candidates, false, useAccelerations, accelerationPool); } - public async $rustUpdateBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, mempoolSize: number, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], useAccelerations: boolean, accelerationPool?: number): Promise { + public async $rustUpdateBlockTemplates(transactions: string[], newMempool: { [txid: string]: MempoolTransactionExtended }, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], candidates: GbtCandidates | undefined, useAccelerations: boolean, accelerationPool?: number): Promise { // GBT optimization requires that uids never get too sparse // as a sanity check, we should also explicitly prevent uint32 uid overflow - if (this.nextUid + added.length >= Math.min(Math.max(262144, 2 * mempoolSize), MAX_UINT32)) { + if (this.nextUid + added.length >= Math.min(Math.max(262144, 2 * transactions.length), MAX_UINT32)) { this.resetRustGbt(); } if (!this.rustInitialized) { // need to reset the worker - return this.$rustMakeBlockTemplates(newMempool, true, useAccelerations, accelerationPool); + return this.$rustMakeBlockTemplates(transactions, newMempool, candidates, true, useAccelerations, accelerationPool); } const start = Date.now(); // set missing short ids for (const tx of added) { - this.setUid(tx, true); + this.setUid(tx, false); } // set short ids for transaction inputs for (const tx of added) { tx.inputs = tx.vin.map(v => this.getUid(newMempool[v.txid])).filter(uid => (uid !== null && uid !== undefined)) as number[]; } - const removedUids = removed.map(tx => this.getUid(tx)).filter(uid => (uid !== null && uid !== undefined)) as number[]; + const removedTxs = removed.filter(tx => tx.uid != null) as MempoolTransactionExtended[]; const accelerations = useAccelerations ? mempool.getAccelerations() : {}; const acceleratedList = accelerationPool ? Object.values(accelerations).filter(acc => newMempool[acc.txid] && acc.pools.includes(accelerationPool)) : Object.values(accelerations).filter(acc => newMempool[acc.txid]); const convertedAccelerations = acceleratedList.map(acc => { + this.setUid(newMempool[acc.txid], true); return { uid: this.getUid(newMempool[acc.txid]), delta: acc.feeDelta, @@ -430,18 +311,18 @@ class MempoolBlocks { const { blocks, blockWeights, rates, clusters, overflow } = this.convertNapiResultTxids( await this.rustGbtGenerator.update( added as RustThreadTransaction[], - removedUids, + removedTxs.map(tx => tx.uid) as number[], convertedAccelerations as RustThreadAcceleration[], this.nextUid, ), ); const resultMempoolSize = blocks.reduce((total, block) => total + block.length, 0) + overflow.length; - logger.debug(`RUST updateBlockTemplates returned ${resultMempoolSize} txs out of ${mempoolSize} in the mempool, ${overflow.length} were unmineable`); - if (mempoolSize !== resultMempoolSize) { - throw new Error('GBT returned wrong number of transactions , cache is probably out of sync'); + logger.debug(`RUST updateBlockTemplates returned ${resultMempoolSize} txs out of ${transactions.length} candidates, ${overflow.length} were unmineable`); + if (transactions.length !== resultMempoolSize) { + throw new Error(`GBT returned wrong number of transactions ${transactions.length} vs ${resultMempoolSize}, cache is probably out of sync`); } else { - const processed = this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, accelerations, accelerationPool, true); - this.removeUids(removedUids); + const processed = this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, candidates, accelerations, accelerationPool, true); + this.removeUids(removedTxs); logger.debug(`RUST updateBlockTemplates completed in ${(Date.now() - start)/1000} seconds`); return processed; } @@ -452,7 +333,12 @@ class MempoolBlocks { } } - private processBlockTemplates(mempool: { [txid: string]: MempoolTransactionExtended }, blocks: string[][], blockWeights: number[] | null, rates: [string, number][], clusters: string[][], accelerations, accelerationPool, saveResults): MempoolBlockWithTransactions[] { + private processBlockTemplates(mempool: { [txid: string]: MempoolTransactionExtended }, blocks: string[][], blockWeights: number[] | null, rates: [string, number][], clusters: string[][], candidates: GbtCandidates | undefined, accelerations, accelerationPool, saveResults): MempoolBlockWithTransactions[] { + for (const txid of Object.keys(candidates?.txs ?? mempool)) { + if (txid in mempool) { + mempool[txid].cpfpDirty = false; + } + } for (const [txid, rate] of rates) { if (txid in mempool) { mempool[txid].cpfpDirty = (rate !== mempool[txid].effectiveFeePerVsize); @@ -486,6 +372,9 @@ class MempoolBlocks { if (txid === memberTxid) { matched = true; } else { + if (!mempool[txid]) { + console.log('txid missing from mempool! ', txid, candidates?.txs[txid]); + } const relative = { txid: txid, fee: mempool[txid].fee, @@ -518,6 +407,16 @@ class MempoolBlocks { let totalWeight = 0; let totalFees = 0; const transactions: MempoolTransactionExtended[] = []; + + // backfill purged transactions + if (candidates?.txs && blockIndex === blocks.length - 1) { + for (const txid of Object.keys(mempool)) { + if (!candidates.txs[txid]) { + block.push(txid); + } + } + } + for (const txid of block) { if (txid) { mempoolTx = mempool[txid]; @@ -526,16 +425,6 @@ class MempoolBlocks { block: blockIndex, vsize: totalVsize + (mempoolTx.vsize / 2), }; - if (!mempoolTx.cpfpChecked) { - if (mempoolTx.ancestors?.length) { - mempoolTx.ancestors = []; - } - if (mempoolTx.descendants?.length) { - mempoolTx.descendants = []; - } - mempoolTx.bestDescendant = null; - mempoolTx.cpfpChecked = true; - } const acceleration = accelerations[txid]; if (isAccelerated[txid] || (acceleration && (!accelerationPool || acceleration.pools.includes(accelerationPool)))) { @@ -594,7 +483,7 @@ class MempoolBlocks { private dataToMempoolBlocks(transactionIds: string[], transactions: MempoolTransactionExtended[], totalSize: number, totalWeight: number, totalFees: number, feeStats?: EffectiveFeeStats ): MempoolBlockWithTransactions { if (!feeStats) { - feeStats = Common.calcEffectiveFeeStatistics(transactions.filter(tx => !tx.acceleration)); + feeStats = Common.calcEffectiveFeeStatistics(transactions); } return { blockSize: totalSize, @@ -610,30 +499,38 @@ class MempoolBlocks { private resetUids(): void { this.uidMap.clear(); + this.txidMap.clear(); this.nextUid = 1; } private setUid(tx: MempoolTransactionExtended, skipSet = false): number { - if (tx.uid === null || tx.uid === undefined || !skipSet) { + if (!this.txidMap.has(tx.txid) || !skipSet) { const uid = this.nextUid; this.nextUid++; this.uidMap.set(uid, tx.txid); + this.txidMap.set(tx.txid, uid); tx.uid = uid; return uid; } else { + tx.uid = this.txidMap.get(tx.txid) as number; return tx.uid; } } private getUid(tx: MempoolTransactionExtended): number | void { - if (tx?.uid !== null && tx?.uid !== undefined && this.uidMap.has(tx.uid)) { - return tx.uid; + if (tx) { + return this.txidMap.get(tx.txid); } } - private removeUids(uids: number[]): void { - for (const uid of uids) { - this.uidMap.delete(uid); + private removeUids(txs: MempoolTransactionExtended[]): void { + for (const tx of txs) { + const uid = this.txidMap.get(tx.txid); + if (uid != null) { + this.uidMap.delete(uid); + this.txidMap.delete(tx.txid); + } + tx.uid = undefined; } } @@ -691,6 +588,40 @@ class MempoolBlocks { }); return { blocks: convertedBlocks, blockWeights, rates: convertedRates, clusters: convertedClusters, overflow: convertedOverflow }; } + + public compressTx(tx: TransactionClassified): TransactionCompressed { + if (tx.acc) { + return [ + tx.txid, + tx.fee, + tx.vsize, + tx.value, + Math.round((tx.rate || (tx.fee / tx.vsize)) * 100) / 100, + tx.flags, + tx.time || 0, + 1, + ]; + } else { + return [ + tx.txid, + tx.fee, + tx.vsize, + tx.value, + Math.round((tx.rate || (tx.fee / tx.vsize)) * 100) / 100, + tx.flags, + tx.time || 0, + ]; + } + } + + public compressDeltaChange(tx: TransactionClassified): MempoolDeltaChange { + return [ + tx.txid, + Math.round((tx.rate || (tx.fee / tx.vsize)) * 100) / 100, + tx.flags, + tx.acc ? 1 : 0, + ]; + } } export default new MempoolBlocks(); diff --git a/backend/src/api/mempool.ts b/backend/src/api/mempool.ts index a5bc8407a..176bedddb 100644 --- a/backend/src/api/mempool.ts +++ b/backend/src/api/mempool.ts @@ -1,6 +1,6 @@ import config from '../config'; import bitcoinApi from './bitcoin/bitcoin-api-factory'; -import { MempoolTransactionExtended, TransactionExtended, VbytesPerSecond } from '../mempool.interfaces'; +import { MempoolTransactionExtended, TransactionExtended, VbytesPerSecond, GbtCandidates } from '../mempool.interfaces'; import logger from '../logger'; import { Common } from './common'; import transactionUtils from './transaction-utils'; @@ -11,18 +11,20 @@ import bitcoinSecondClient from './bitcoin/bitcoin-second-client'; import rbfCache from './rbf-cache'; import { Acceleration } from './services/acceleration'; import redisCache from './redis-cache'; +import blocks from './blocks'; class Mempool { private inSync: boolean = false; private mempoolCacheDelta: number = -1; private mempoolCache: { [txId: string]: MempoolTransactionExtended } = {}; + private mempoolCandidates: { [txid: string ]: boolean } = {}; private spendMap = new Map(); private mempoolInfo: IBitcoinApi.MempoolInfo = { loaded: false, size: 0, bytes: 0, usage: 0, total_fee: 0, maxmempool: 300000000, mempoolminfee: Common.isLiquid() ? 0.00000100 : 0.00001000, minrelaytxfee: Common.isLiquid() ? 0.00000100 : 0.00001000 }; private mempoolChangedCallback: ((newMempool: {[txId: string]: MempoolTransactionExtended; }, newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]) => void) | undefined; private $asyncMempoolChangedCallback: ((newMempool: {[txId: string]: MempoolTransactionExtended; }, mempoolSize: number, newTransactions: MempoolTransactionExtended[], - deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]) => Promise) | undefined; + deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[], candidates?: GbtCandidates) => Promise) | undefined; private accelerations: { [txId: string]: Acceleration } = {}; @@ -40,6 +42,8 @@ class Mempool { private missingTxCount = 0; private mainLoopTimeout: number = 120000; + public limitGBT = config.MEMPOOL.USE_SECOND_NODE_FOR_MINFEE && config.MEMPOOL.LIMIT_GBT; + constructor() { setInterval(this.updateTxPerSecond.bind(this), 1000); } @@ -74,7 +78,8 @@ class Mempool { } public setAsyncMempoolChangedCallback(fn: (newMempool: { [txId: string]: MempoolTransactionExtended; }, mempoolSize: number, - newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]) => Promise): void { + newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[], + candidates?: GbtCandidates) => Promise): void { this.$asyncMempoolChangedCallback = fn; } @@ -86,6 +91,10 @@ class Mempool { return this.spendMap; } + public getFromSpendMap(txid, index): MempoolTransactionExtended | void { + return this.spendMap.get(`${txid}:${index}`); + } + public async $setMempool(mempoolData: { [txId: string]: MempoolTransactionExtended }) { this.mempoolCache = mempoolData; let count = 0; @@ -107,6 +116,10 @@ class Mempool { if (config.MEMPOOL.CACHE_ENABLED && config.REDIS.ENABLED) { await redisCache.$addTransaction(this.mempoolCache[txid]); } + this.mempoolCache[txid].flags = Common.getTransactionFlags(this.mempoolCache[txid]); + this.mempoolCache[txid].cpfpChecked = false; + this.mempoolCache[txid].cpfpDirty = true; + this.mempoolCache[txid].cpfpUpdated = undefined; } if (config.MEMPOOL.CACHE_ENABLED && config.REDIS.ENABLED) { await redisCache.$flushTransactions(); @@ -116,7 +129,7 @@ class Mempool { this.mempoolChangedCallback(this.mempoolCache, [], [], []); } if (this.$asyncMempoolChangedCallback) { - await this.$asyncMempoolChangedCallback(this.mempoolCache, count, [], [], []); + await this.$asyncMempoolChangedCallback(this.mempoolCache, count, [], [], [], this.limitGBT ? { txs: {}, added: [], removed: [] } : undefined); } this.addToSpendMap(Object.values(this.mempoolCache)); } @@ -159,6 +172,10 @@ class Mempool { return newTransactions; } + public getMempoolCandidates(): { [txid: string]: boolean } { + return this.mempoolCandidates; + } + public async $updateMemPoolInfo() { this.mempoolInfo = await this.$getMempoolInfo(); } @@ -188,7 +205,7 @@ class Mempool { return txTimes; } - public async $updateMempool(transactions: string[], accelerations: Acceleration[] | null, pollRate: number): Promise { + public async $updateMempool(transactions: string[], accelerations: Acceleration[] | null, minFeeMempool: string[], minFeeTip: number, pollRate: number): Promise { logger.debug(`Updating mempool...`); // warn if this run stalls the main loop for more than 2 minutes @@ -329,6 +346,8 @@ class Mempool { } } + const candidates = await this.getNextCandidates(minFeeMempool, minFeeTip, deletedTransactions); + const newMempoolSize = currentMempoolSize + newTransactions.length - deletedTransactions.length; const newTransactionsStripped = newTransactions.map((tx) => Common.stripTransaction(tx)); this.latestTransactions = newTransactionsStripped.concat(this.latestTransactions).slice(0, 6); @@ -340,12 +359,14 @@ class Mempool { this.mempoolCacheDelta = Math.abs(transactions.length - newMempoolSize); + const candidatesChanged = candidates?.added?.length || candidates?.removed?.length; + if (this.mempoolChangedCallback && (hasChange || deletedTransactions.length)) { this.mempoolChangedCallback(this.mempoolCache, newTransactions, deletedTransactions, accelerationDelta); } - if (this.$asyncMempoolChangedCallback && (hasChange || deletedTransactions.length)) { + if (this.$asyncMempoolChangedCallback && (hasChange || deletedTransactions.length || candidatesChanged)) { this.updateTimerProgress(timer, 'running async mempool callback'); - await this.$asyncMempoolChangedCallback(this.mempoolCache, newMempoolSize, newTransactions, deletedTransactions, accelerationDelta); + await this.$asyncMempoolChangedCallback(this.mempoolCache, newMempoolSize, newTransactions, deletedTransactions, accelerationDelta, candidates); this.updateTimerProgress(timer, 'completed async mempool callback'); } @@ -431,6 +452,64 @@ class Mempool { } } + public async getNextCandidates(minFeeTransactions: string[], blockHeight: number, deletedTransactions: MempoolTransactionExtended[]): Promise { + if (this.limitGBT) { + const deletedTxsMap = {}; + for (const tx of deletedTransactions) { + deletedTxsMap[tx.txid] = tx; + } + const newCandidateTxMap = {}; + for (const txid of minFeeTransactions) { + if (this.mempoolCache[txid]) { + newCandidateTxMap[txid] = true; + } + } + const accelerations = this.getAccelerations(); + for (const txid of Object.keys(accelerations)) { + if (this.mempoolCache[txid]) { + newCandidateTxMap[txid] = true; + } + } + const removed: MempoolTransactionExtended[] = []; + const added: MempoolTransactionExtended[] = []; + // don't prematurely remove txs included in a new block + if (blockHeight > blocks.getCurrentBlockHeight()) { + for (const txid of Object.keys(this.mempoolCandidates)) { + newCandidateTxMap[txid] = true; + } + } else { + for (const txid of Object.keys(this.mempoolCandidates)) { + if (!newCandidateTxMap[txid]) { + if (this.mempoolCache[txid]) { + removed.push(this.mempoolCache[txid]); + this.mempoolCache[txid].effectiveFeePerVsize = this.mempoolCache[txid].adjustedFeePerVsize; + this.mempoolCache[txid].ancestors = []; + this.mempoolCache[txid].descendants = []; + this.mempoolCache[txid].bestDescendant = null; + this.mempoolCache[txid].cpfpChecked = false; + this.mempoolCache[txid].cpfpUpdated = undefined; + } else if (deletedTxsMap[txid]) { + removed.push(deletedTxsMap[txid]); + } + } + } + } + + for (const txid of Object.keys(newCandidateTxMap)) { + if (!this.mempoolCandidates[txid]) { + added.push(this.mempoolCache[txid]); + } + } + + this.mempoolCandidates = newCandidateTxMap; + return { + txs: this.mempoolCandidates, + added, + removed + }; + } + } + private startTimer() { const state: any = { start: Date.now(), diff --git a/backend/src/api/mining/mining-routes.ts b/backend/src/api/mining/mining-routes.ts index bb78de44a..3492114b5 100644 --- a/backend/src/api/mining/mining-routes.ts +++ b/backend/src/api/mining/mining-routes.ts @@ -8,6 +8,7 @@ import HashratesRepository from '../../repositories/HashratesRepository'; import bitcoinClient from '../bitcoin/bitcoin-client'; import mining from "./mining"; import PricesRepository from '../../repositories/PricesRepository'; +import AccelerationRepository from '../../repositories/AccelerationRepository'; class MiningRoutes { public initRoutes(app: Application) { @@ -34,6 +35,11 @@ class MiningRoutes { .get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/audit/:hash', this.$getBlockAudit) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/timestamp/:timestamp', this.$getHeightFromTimestamp) .get(config.MEMPOOL.API_URL_PREFIX + 'historical-price', this.$getHistoricalPrice) + + .get(config.MEMPOOL.API_URL_PREFIX + 'accelerations/pool/:slug', this.$getAccelerationsByPool) + .get(config.MEMPOOL.API_URL_PREFIX + 'accelerations/block/:height', this.$getAccelerationsByHeight) + .get(config.MEMPOOL.API_URL_PREFIX + 'accelerations/recent/:interval', this.$getRecentAccelerations) + .get(config.MEMPOOL.API_URL_PREFIX + 'accelerations/total', this.$getAccelerationTotals) ; } @@ -46,13 +52,20 @@ class MiningRoutes { res.status(400).send('Prices are not available on testnets.'); return; } - if (req.query.timestamp) { - res.status(200).send(await PricesRepository.$getNearestHistoricalPrice( - parseInt(req.query.timestamp ?? 0, 10) - )); + const timestamp = parseInt(req.query.timestamp as string, 10) || 0; + const currency = req.query.currency as string; + + let response; + if (timestamp && currency) { + response = await PricesRepository.$getNearestHistoricalPrice(timestamp, currency); + } else if (timestamp) { + response = await PricesRepository.$getNearestHistoricalPrice(timestamp); + } else if (currency) { + response = await PricesRepository.$getHistoricalPrices(currency); } else { - res.status(200).send(await PricesRepository.$getHistoricalPrices()); + response = await PricesRepository.$getHistoricalPrices(); } + res.status(200).send(response); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); } @@ -352,6 +365,67 @@ class MiningRoutes { res.status(500).send(e instanceof Error ? e.message : e); } } + + private async $getAccelerationsByPool(req: Request, res: Response): Promise { + try { + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); + if (!config.MEMPOOL_SERVICES.ACCELERATIONS || ['testnet', 'signet', 'liquidtestnet', 'liquid'].includes(config.MEMPOOL.NETWORK)) { + res.status(400).send('Acceleration data is not available.'); + return; + } + res.status(200).send(await AccelerationRepository.$getAccelerationInfo(req.params.slug)); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getAccelerationsByHeight(req: Request, res: Response): Promise { + try { + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24).toUTCString()); + if (!config.MEMPOOL_SERVICES.ACCELERATIONS || ['testnet', 'signet', 'liquidtestnet', 'liquid'].includes(config.MEMPOOL.NETWORK)) { + res.status(400).send('Acceleration data is not available.'); + return; + } + const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10); + res.status(200).send(await AccelerationRepository.$getAccelerationInfo(null, height)); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getRecentAccelerations(req: Request, res: Response): Promise { + try { + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); + if (!config.MEMPOOL_SERVICES.ACCELERATIONS || ['testnet', 'signet', 'liquidtestnet', 'liquid'].includes(config.MEMPOOL.NETWORK)) { + res.status(400).send('Acceleration data is not available.'); + return; + } + res.status(200).send(await AccelerationRepository.$getAccelerationInfo(null, null, req.params.interval)); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + + private async $getAccelerationTotals(req: Request, res: Response): Promise { + try { + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); + if (!config.MEMPOOL_SERVICES.ACCELERATIONS || ['testnet', 'signet', 'liquidtestnet', 'liquid'].includes(config.MEMPOOL.NETWORK)) { + res.status(400).send('Acceleration data is not available.'); + return; + } + res.status(200).send(await AccelerationRepository.$getAccelerationTotals(req.query.pool, req.query.interval)); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } } export default new MiningRoutes(); diff --git a/backend/src/api/mining/mining.ts b/backend/src/api/mining/mining.ts index b23ad04c5..85554db2d 100644 --- a/backend/src/api/mining/mining.ts +++ b/backend/src/api/mining/mining.ts @@ -142,7 +142,7 @@ class Mining { public async $getPoolStat(slug: string): Promise { const pool = await PoolsRepository.$getPool(slug); if (!pool) { - throw new Error('This mining pool does not exist ' + escape(slug)); + throw new Error('This mining pool does not exist'); } const blockCount: number = await BlocksRepository.$blockCount(pool.id); diff --git a/backend/src/api/redis-cache.ts b/backend/src/api/redis-cache.ts index edfd2142b..d19d73a7f 100644 --- a/backend/src/api/redis-cache.ts +++ b/backend/src/api/redis-cache.ts @@ -19,45 +19,90 @@ class RedisCache { private client; private connected = false; private schemaVersion = 1; + private redisConfig: any; + private pauseFlush: boolean = false; private cacheQueue: MempoolTransactionExtended[] = []; + private removeQueue: string[] = []; + private rbfCacheQueue: { type: string, txid: string, value: any }[] = []; + private rbfRemoveQueue: { type: string, txid: string }[] = []; private txFlushLimit: number = 10000; constructor() { if (config.REDIS.ENABLED) { - const redisConfig = { + this.redisConfig = { socket: { path: config.REDIS.UNIX_SOCKET_PATH }, database: NetworkDB[config.MEMPOOL.NETWORK], }; - this.client = createClient(redisConfig); - this.client.on('error', (e) => { - logger.err(`Error in Redis client: ${e instanceof Error ? e.message : e}`); - }); this.$ensureConnected(); + setInterval(() => { this.$ensureConnected(); }, 10000); } } - private async $ensureConnected(): Promise { + private async $ensureConnected(): Promise { if (!this.connected && config.REDIS.ENABLED) { - return this.client.connect().then(async () => { - this.connected = true; - logger.info(`Redis client connected`); - const version = await this.client.get('schema_version'); - if (version !== this.schemaVersion) { - // schema changed - // perform migrations or flush DB if necessary - logger.info(`Redis schema version changed from ${version} to ${this.schemaVersion}`); - await this.client.set('schema_version', this.schemaVersion); - } - }); + try { + this.client = createClient(this.redisConfig); + this.client.on('error', async (e) => { + logger.err(`Error in Redis client: ${e instanceof Error ? e.message : e}`); + this.connected = false; + await this.client.disconnect(); + }); + await this.client.connect().then(async () => { + try { + const version = await this.client.get('schema_version'); + this.connected = true; + if (version !== this.schemaVersion) { + // schema changed + // perform migrations or flush DB if necessary + logger.info(`Redis schema version changed from ${version} to ${this.schemaVersion}`); + await this.client.set('schema_version', this.schemaVersion); + } + logger.info(`Redis client connected`); + return true; + } catch (e) { + this.connected = false; + logger.warn('Failed to connect to Redis'); + return false; + } + }); + await this.$onConnected(); + return true; + } catch (e) { + logger.warn('Error connecting to Redis: ' + (e instanceof Error ? e.message : e)); + return false; + } + } else { + try { + // test connection + await this.client.get('schema_version'); + return true; + } catch (e) { + logger.warn('Lost connection to Redis: ' + (e instanceof Error ? e.message : e)); + logger.warn('Attempting to reconnect in 10 seconds'); + this.connected = false; + return false; + } } } - async $updateBlocks(blocks: BlockExtended[]) { + private async $onConnected(): Promise { + await this.$flushTransactions(); + await this.$removeTransactions([]); + await this.$flushRbfQueues(); + } + + async $updateBlocks(blocks: BlockExtended[]): Promise { + if (!config.REDIS.ENABLED) { + return; + } + if (!this.connected) { + logger.warn(`Failed to update blocks in Redis cache: Redis is not connected`); + return; + } try { - await this.$ensureConnected(); await this.client.set('blocks', JSON.stringify(blocks)); logger.debug(`Saved latest blocks to Redis cache`); } catch (e) { @@ -65,9 +110,15 @@ class RedisCache { } } - async $updateBlockSummaries(summaries: BlockSummary[]) { + async $updateBlockSummaries(summaries: BlockSummary[]): Promise { + if (!config.REDIS.ENABLED) { + return; + } + if (!this.connected) { + logger.warn(`Failed to update block summaries in Redis cache: Redis is not connected`); + return; + } try { - await this.$ensureConnected(); await this.client.set('block-summaries', JSON.stringify(summaries)); logger.debug(`Saved latest block summaries to Redis cache`); } catch (e) { @@ -75,30 +126,35 @@ class RedisCache { } } - async $addTransaction(tx: MempoolTransactionExtended) { + async $addTransaction(tx: MempoolTransactionExtended): Promise { + if (!config.REDIS.ENABLED) { + return; + } this.cacheQueue.push(tx); if (this.cacheQueue.length >= this.txFlushLimit) { - await this.$flushTransactions(); + if (!this.pauseFlush) { + await this.$flushTransactions(); + } } } - async $flushTransactions() { - const success = await this.$addTransactions(this.cacheQueue); - if (success) { - logger.debug(`Saved ${this.cacheQueue.length} transactions to Redis cache`); - this.cacheQueue = []; - } else { - logger.err(`Failed to save ${this.cacheQueue.length} transactions to Redis cache`); + async $flushTransactions(): Promise { + if (!config.REDIS.ENABLED) { + return; + } + if (!this.cacheQueue.length) { + return; + } + if (!this.connected) { + logger.warn(`Failed to add ${this.cacheQueue.length} transactions to Redis cache: Redis not connected`); + return; } - } - private async $addTransactions(newTransactions: MempoolTransactionExtended[]): Promise { - if (!newTransactions.length) { - return true; - } + this.pauseFlush = false; + + const toAdd = this.cacheQueue.slice(0, this.txFlushLimit); try { - await this.$ensureConnected(); - const msetData = newTransactions.map(tx => { + const msetData = toAdd.map(tx => { const minified: any = { ...tx }; delete minified.hex; for (const vin of minified.vin) { @@ -112,30 +168,53 @@ class RedisCache { return [`mempool:tx:${tx.txid}`, JSON.stringify(minified)]; }); await this.client.MSET(msetData); - return true; + // successful, remove transactions from cache queue + this.cacheQueue = this.cacheQueue.slice(toAdd.length); + logger.debug(`Saved ${toAdd.length} transactions to Redis cache, ${this.cacheQueue.length} left in queue`); } catch (e) { - logger.warn(`Failed to add ${newTransactions.length} transactions to Redis cache: ${e instanceof Error ? e.message : e}`); - return false; + logger.warn(`Failed to add ${toAdd.length} transactions to Redis cache: ${e instanceof Error ? e.message : e}`); + this.pauseFlush = true; } } - async $removeTransactions(transactions: string[]) { - try { - await this.$ensureConnected(); + async $removeTransactions(transactions: string[]): Promise { + if (!config.REDIS.ENABLED) { + return; + } + const toRemove = this.removeQueue.concat(transactions); + this.removeQueue = []; + let failed: string[] = []; + let numRemoved = 0; + if (this.connected) { const sliceLength = config.REDIS.BATCH_QUERY_BASE_SIZE; - for (let i = 0; i < Math.ceil(transactions.length / sliceLength); i++) { - const slice = transactions.slice(i * sliceLength, (i + 1) * sliceLength); - await this.client.unlink(slice.map(txid => `mempool:tx:${txid}`)); - logger.debug(`Deleted ${slice.length} transactions from the Redis cache`); + for (let i = 0; i < Math.ceil(toRemove.length / sliceLength); i++) { + const slice = toRemove.slice(i * sliceLength, (i + 1) * sliceLength); + try { + await this.client.unlink(slice.map(txid => `mempool:tx:${txid}`)); + numRemoved+= sliceLength; + logger.debug(`Deleted ${slice.length} transactions from the Redis cache`); + } catch (e) { + logger.warn(`Failed to remove ${slice.length} transactions from Redis cache: ${e instanceof Error ? e.message : e}`); + failed = failed.concat(slice); + } } - } catch (e) { - logger.warn(`Failed to remove ${transactions.length} transactions from Redis cache: ${e instanceof Error ? e.message : e}`); + // concat instead of replace, in case more txs have been added in the meantime + this.removeQueue = this.removeQueue.concat(failed); + } else { + this.removeQueue = this.removeQueue.concat(toRemove); } } async $setRbfEntry(type: string, txid: string, value: any): Promise { + if (!config.REDIS.ENABLED) { + return; + } + if (!this.connected) { + this.rbfCacheQueue.push({ type, txid, value }); + logger.warn(`Failed to set RBF ${type} in Redis cache: Redis is not connected`); + return; + } try { - await this.$ensureConnected(); await this.client.set(`rbf:${type}:${txid}`, JSON.stringify(value)); } catch (e) { logger.warn(`Failed to set RBF ${type} in Redis cache: ${e instanceof Error ? e.message : e}`); @@ -143,17 +222,55 @@ class RedisCache { } async $removeRbfEntry(type: string, txid: string): Promise { + if (!config.REDIS.ENABLED) { + return; + } + if (!this.connected) { + this.rbfRemoveQueue.push({ type, txid }); + logger.warn(`Failed to remove RBF ${type} from Redis cache: Redis is not connected`); + return; + } try { - await this.$ensureConnected(); await this.client.unlink(`rbf:${type}:${txid}`); } catch (e) { logger.warn(`Failed to remove RBF ${type} from Redis cache: ${e instanceof Error ? e.message : e}`); } } - async $getBlocks(): Promise { + private async $flushRbfQueues(): Promise { + if (!config.REDIS.ENABLED) { + return; + } + if (!this.connected) { + return; + } + try { + const toAdd = this.rbfCacheQueue; + this.rbfCacheQueue = []; + for (const { type, txid, value } of toAdd) { + await this.$setRbfEntry(type, txid, value); + } + logger.debug(`Saved ${toAdd.length} queued RBF entries to the Redis cache`); + const toRemove = this.rbfRemoveQueue; + this.rbfRemoveQueue = []; + for (const { type, txid } of toRemove) { + await this.$removeRbfEntry(type, txid); + } + logger.debug(`Removed ${toRemove.length} queued RBF entries from the Redis cache`); + } catch (e) { + logger.warn(`Failed to flush RBF cache event queues after reconnecting to Redis: ${e instanceof Error ? e.message : e}`); + } + } + + async $getBlocks(): Promise { + if (!config.REDIS.ENABLED) { + return []; + } + if (!this.connected) { + logger.warn(`Failed to retrieve blocks from Redis cache: Redis is not connected`); + return []; + } try { - await this.$ensureConnected(); const json = await this.client.get('blocks'); return JSON.parse(json); } catch (e) { @@ -163,8 +280,14 @@ class RedisCache { } async $getBlockSummaries(): Promise { + if (!config.REDIS.ENABLED) { + return []; + } + if (!this.connected) { + logger.warn(`Failed to retrieve blocks from Redis cache: Redis is not connected`); + return []; + } try { - await this.$ensureConnected(); const json = await this.client.get('block-summaries'); return JSON.parse(json); } catch (e) { @@ -174,10 +297,16 @@ class RedisCache { } async $getMempool(): Promise<{ [txid: string]: MempoolTransactionExtended }> { + if (!config.REDIS.ENABLED) { + return {}; + } + if (!this.connected) { + logger.warn(`Failed to retrieve mempool from Redis cache: Redis is not connected`); + return {}; + } const start = Date.now(); const mempool = {}; try { - await this.$ensureConnected(); const mempoolList = await this.scanKeys('mempool:tx:*'); for (const tx of mempoolList) { mempool[tx.key] = tx.value; @@ -191,8 +320,14 @@ class RedisCache { } async $getRbfEntries(type: string): Promise { + if (!config.REDIS.ENABLED) { + return []; + } + if (!this.connected) { + logger.warn(`Failed to retrieve Rbf ${type}s from Redis cache: Redis is not connected`); + return []; + } try { - await this.$ensureConnected(); const rbfEntries = await this.scanKeys(`rbf:${type}:*`); return rbfEntries; } catch (e) { @@ -201,7 +336,10 @@ class RedisCache { } } - async $loadCache() { + async $loadCache(): Promise { + if (!config.REDIS.ENABLED) { + return; + } logger.info('Restoring mempool and blocks data from Redis cache'); // Load block data const loadedBlocks = await this.$getBlocks(); @@ -226,7 +364,7 @@ class RedisCache { }); } - private inflateLoadedTxs(mempool: { [txid: string]: MempoolTransactionExtended }) { + private inflateLoadedTxs(mempool: { [txid: string]: MempoolTransactionExtended }): void { for (const tx of Object.values(mempool)) { for (const vin of tx.vin) { if (vin.scriptsig) { diff --git a/backend/src/api/services/acceleration.ts b/backend/src/api/services/acceleration.ts index 99bb963ee..f22959f3f 100644 --- a/backend/src/api/services/acceleration.ts +++ b/backend/src/api/services/acceleration.ts @@ -7,7 +7,26 @@ export interface Acceleration { txid: string, feeDelta: number, pools: number[], -} +}; + +export interface AccelerationHistory { + txid: string, + status: string, + feePaid: number, + added: number, + lastUpdated: number, + baseFee: number, + vsizeFee: number, + effectiveFee: number, + effectiveVsize: number, + feeDelta: number, + blockHash: string, + blockHeight: number, + pools: { + pool_unique_id: number, + username: string, + }[], +}; class AccelerationApi { public async $fetchAccelerations(): Promise { @@ -24,6 +43,27 @@ class AccelerationApi { } } + public async $fetchAccelerationHistory(page?: number, status?: string): Promise { + if (config.MEMPOOL_SERVICES.ACCELERATIONS) { + try { + const response = await axios.get(`${config.MEMPOOL_SERVICES.API}/accelerator/accelerations/history`, { + responseType: 'json', + timeout: 10000, + params: { + page, + status, + } + }); + return response.data as AccelerationHistory[]; + } catch (e) { + logger.warn('Failed to fetch acceleration history from the mempool services backend: ' + (e instanceof Error ? e.message : e)); + return null; + } + } else { + return []; + } + } + public isAcceleratedBlock(block: BlockExtended, accelerations: Acceleration[]): boolean { let anyAccelerated = false; for (let i = 0; i < accelerations.length && !anyAccelerated; i++) { diff --git a/backend/src/api/statistics/statistics-api.ts b/backend/src/api/statistics/statistics-api.ts index 5c6896619..c7c3f37b0 100644 --- a/backend/src/api/statistics/statistics-api.ts +++ b/backend/src/api/statistics/statistics-api.ts @@ -285,7 +285,7 @@ class StatisticsApi { public async $list2H(): Promise { try { - const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY statistics.added DESC LIMIT 120`; + const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL 2 HOUR) AND NOW() ORDER BY statistics.added DESC`; const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout }); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]); } catch (e) { @@ -296,7 +296,7 @@ class StatisticsApi { public async $list24H(): Promise { try { - const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY statistics.added DESC LIMIT 1440`; + const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL 24 HOUR) AND NOW() ORDER BY statistics.added DESC`; const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout }); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]); } catch (e) { diff --git a/backend/src/api/statistics/statistics.ts b/backend/src/api/statistics/statistics.ts index 494777aad..2926a4b17 100644 --- a/backend/src/api/statistics/statistics.ts +++ b/backend/src/api/statistics/statistics.ts @@ -6,6 +6,7 @@ import statisticsApi from './statistics-api'; class Statistics { protected intervalTimer: NodeJS.Timer | undefined; + protected lastRun: number = 0; protected newStatisticsEntryCallback: ((stats: OptimizedStatistic) => void) | undefined; public setNewStatisticsEntryCallback(fn: (stats: OptimizedStatistic) => void) { @@ -23,15 +24,21 @@ class Statistics { setTimeout(() => { this.runStatistics(); this.intervalTimer = setInterval(() => { - this.runStatistics(); + this.runStatistics(true); }, 1 * 60 * 1000); }, difference); } - private async runStatistics(): Promise { + public async runStatistics(skipIfRecent = false): Promise { if (!memPool.isInSync()) { return; } + + if (skipIfRecent && new Date().getTime() / 1000 - this.lastRun < 30) { + return; + } + + this.lastRun = new Date().getTime() / 1000; const currentMempool = memPool.getMempool(); const txPerSecond = memPool.getTxPerSecond(); const vBytesPerSecond = memPool.getVBytesPerSecond(); diff --git a/backend/src/api/transaction-utils.ts b/backend/src/api/transaction-utils.ts index 6ff1c10b7..9107f2ae7 100644 --- a/backend/src/api/transaction-utils.ts +++ b/backend/src/api/transaction-utils.ts @@ -145,6 +145,10 @@ class TransactionUtils { } public countScriptSigops(script: string, isRawScript: boolean = false, witness: boolean = false): number { + if (!script?.length) { + return 0; + } + let sigops = 0; // count OP_CHECKSIG and OP_CHECKSIGVERIFY sigops += (script.match(/OP_CHECKSIG/g)?.length || 0); diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index d0e0b7fd8..66f134b2a 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -2,7 +2,7 @@ import logger from '../logger'; import * as WebSocket from 'ws'; import { BlockExtended, TransactionExtended, MempoolTransactionExtended, WebsocketResponse, - OptimizedStatistic, ILoadingIndicators + OptimizedStatistic, ILoadingIndicators, GbtCandidates, TxTrackingInfo, } from '../mempool.interfaces'; import blocks from './blocks'; import memPool from './mempool'; @@ -18,17 +18,21 @@ import feeApi from './fee-api'; import BlocksAuditsRepository from '../repositories/BlocksAuditsRepository'; import BlocksSummariesRepository from '../repositories/BlocksSummariesRepository'; import Audit from './audit'; -import { deepClone } from '../utils/clone'; import priceUpdater from '../tasks/price-updater'; import { ApiPrice } from '../repositories/PricesRepository'; import accelerationApi from './services/acceleration'; import mempool from './mempool'; +import statistics from './statistics/statistics'; +import accelerationRepository from '../repositories/AccelerationRepository'; +import bitcoinApi from './bitcoin/bitcoin-api-factory'; interface AddressTransactions { mempool: MempoolTransactionExtended[], confirmed: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], } +import bitcoinSecondClient from './bitcoin/bitcoin-second-client'; +import { calculateCpfp } from './cpfp'; // valid 'want' subscriptions const wantable = [ @@ -36,6 +40,7 @@ const wantable = [ 'mempool-blocks', 'live-2h-chart', 'stats', + 'tomahawk', ]; class WebsocketHandler { @@ -48,7 +53,7 @@ class WebsocketHandler { private socketData: { [key: string]: string } = {}; private serializedInitData: string = '{}'; - private lastRbfSummary: ReplacementInfo | null = null; + private lastRbfSummary: ReplacementInfo[] | null = null; constructor() { } @@ -78,6 +83,7 @@ class WebsocketHandler { const _blocks = blocks.getBlocks().slice(-config.MEMPOOL.INITIAL_BLOCKS_AMOUNT); const da = difficultyAdjustment.getDifficultyAdjustment(); this.updateSocketDataFields({ + 'backend': config.MEMPOOL.BACKEND, 'mempoolInfo': memPool.getMempoolInfo(), 'vBytesPerSecond': memPool.getVBytesPerSecond(), 'blocks': _blocks, @@ -120,7 +126,7 @@ class WebsocketHandler { for (const sub of wantable) { const key = `want-${sub}`; const wants = parsedMessage.data.includes(sub); - if (wants && client['wants'] && !client[key]) { + if (wants && !client[key]) { wantNow[key] = true; } client[key] = wants; @@ -144,6 +150,10 @@ class WebsocketHandler { response['da'] = this.socketData['da']; } + if (wantNow['want-tomahawk']) { + response['tomahawk'] = JSON.stringify(bitcoinApi.getHealthStatus()); + } + if (parsedMessage && parsedMessage['track-tx']) { if (/^[a-fA-F0-9]{64}$/.test(parsedMessage['track-tx'])) { client['track-tx'] = parsedMessage['track-tx']; @@ -200,6 +210,52 @@ class WebsocketHandler { } } + if (parsedMessage && parsedMessage['track-txs']) { + const txids: string[] = []; + if (Array.isArray(parsedMessage['track-txs'])) { + for (const txid of parsedMessage['track-txs']) { + if (/^[a-fA-F0-9]{64}$/.test(txid)) { + txids.push(txid); + } + } + } + + const txs: { [txid: string]: TxTrackingInfo } = {}; + for (const txid of txids) { + const txInfo: TxTrackingInfo = { + confirmed: true, + }; + const rbfCacheTxid = rbfCache.getReplacedBy(txid); + if (rbfCacheTxid) { + txInfo.replacedBy = rbfCacheTxid; + txInfo.confirmed = false; + } + const tx = memPool.getMempool()[txid]; + if (tx && tx.position) { + txInfo.position = { + ...tx.position + }; + if (tx.acceleration) { + txInfo.accelerated = tx.acceleration; + } + } + if (tx) { + txInfo.confirmed = false; + } + txs[txid] = txInfo; + } + + if (txids.length) { + client['track-txs'] = txids; + } else { + client['track-txs'] = null; + } + + if (Object.keys(txs).length) { + response['tracked-txs'] = JSON.stringify(txs); + } + } + if (parsedMessage && parsedMessage['track-address']) { const validAddress = this.testAddress(parsedMessage['track-address']); if (validAddress) { @@ -259,7 +315,7 @@ class WebsocketHandler { const mBlocksWithTransactions = mempoolBlocks.getMempoolBlocksWithTransactions(); response['projected-block-transactions'] = JSON.stringify({ index: index, - blockTransactions: mBlocksWithTransactions[index]?.transactions || [], + blockTransactions: (mBlocksWithTransactions[index]?.transactions || []).map(mempoolBlocks.compressTx), }); } else { client['track-mempool-block'] = null; @@ -304,14 +360,6 @@ class WebsocketHandler { client['track-donation'] = parsedMessage['track-donation']; } - if (parsedMessage['track-bisq-market']) { - if (/^[a-z]{3}_[a-z]{3}$/.test(parsedMessage['track-bisq-market'])) { - client['track-bisq-market'] = parsedMessage['track-bisq-market']; - } else { - client['track-bisq-market'] = null; - } - } - if (Object.keys(response).length) { client.send(this.serializeResponse(response)); } @@ -428,21 +476,26 @@ class WebsocketHandler { } async $handleMempoolChange(newMempool: { [txid: string]: MempoolTransactionExtended }, mempoolSize: number, - newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]): Promise { + newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[], + candidates?: GbtCandidates): Promise { if (!this.wss) { throw new Error('WebSocket.Server is not set'); } this.printLogs(); - if (config.MEMPOOL.ADVANCED_GBT_MEMPOOL) { - if (config.MEMPOOL.RUST_GBT) { - await mempoolBlocks.$rustUpdateBlockTemplates(newMempool, mempoolSize, newTransactions, deletedTransactions, config.MEMPOOL_SERVICES.ACCELERATIONS); - } else { - await mempoolBlocks.$updateBlockTemplates(newMempool, newTransactions, deletedTransactions, accelerationDelta, true, config.MEMPOOL_SERVICES.ACCELERATIONS); - } + const transactionIds = (memPool.limitGBT && candidates) ? Object.keys(candidates?.txs || {}) : Object.keys(newMempool); + let added = newTransactions; + let removed = deletedTransactions; + if (memPool.limitGBT) { + added = candidates?.added || []; + removed = candidates?.removed || []; + } + + if (config.MEMPOOL.RUST_GBT) { + await mempoolBlocks.$rustUpdateBlockTemplates(transactionIds, newMempool, added, removed, candidates, config.MEMPOOL_SERVICES.ACCELERATIONS); } else { - mempoolBlocks.updateMempoolBlocks(newMempool, true); + await mempoolBlocks.$updateBlockTemplates(transactionIds, newMempool, added, removed, candidates, accelerationDelta, true, config.MEMPOOL_SERVICES.ACCELERATIONS); } const mBlocks = mempoolBlocks.getMempoolBlocks(); @@ -456,10 +509,11 @@ class WebsocketHandler { let rbfReplacements; let fullRbfReplacements; let rbfSummary; - if (Object.keys(rbfChanges.trees).length) { + if (Object.keys(rbfChanges.trees).length || !this.lastRbfSummary) { rbfReplacements = rbfCache.getRbfTrees(false); fullRbfReplacements = rbfCache.getRbfTrees(true); - rbfSummary = rbfCache.getLatestRbfSummary(); + rbfSummary = rbfCache.getLatestRbfSummary() || []; + this.lastRbfSummary = rbfSummary; } for (const deletedTx of deletedTransactions) { @@ -502,6 +556,11 @@ class WebsocketHandler { if (client['track-tx']) { trackedTxs.add(client['track-tx']); } + if (client['track-txs']) { + for (const txid of client['track-txs']) { + trackedTxs.add(txid); + } + } }); if (trackedTxs.size > 0) { for (const tx of newTransactions) { @@ -543,6 +602,10 @@ class WebsocketHandler { response['mempool-blocks'] = getCachedResponse('mempool-blocks', mBlocks); } + if (client['want-tomahawk']) { + response['tomahawk'] = getCachedResponse('tomahawk', bitcoinApi.getHealthStatus()); + } + if (client['track-mempool-tx']) { const tx = newTransactions.find((t) => t.txid === client['track-mempool-tx']); if (tx) { @@ -676,6 +739,9 @@ class WebsocketHandler { accelerated: mempoolTx.acceleration || undefined, } }; + if (!mempoolTx.cpfpChecked) { + calculateCpfp(mempoolTx, newMempool); + } if (mempoolTx.cpfpDirty) { positionData['cpfp'] = { ancestors: mempoolTx.ancestors, @@ -691,6 +757,46 @@ class WebsocketHandler { } } + if (client['track-txs']) { + const txids = client['track-txs']; + const txs: { [txid: string]: TxTrackingInfo } = {}; + for (const txid of txids) { + const txInfo: TxTrackingInfo = {}; + const outspends = outspendCache[txid]; + if (outspends && Object.keys(outspends).length) { + txInfo.utxoSpent = outspends; + } + const replacedBy = rbfChanges.map[txid] ? rbfCache.getReplacedBy(txid) : false; + if (replacedBy) { + txInfo.replacedBy = replacedBy; + } + const mempoolTx = newMempool[txid]; + if (mempoolTx && mempoolTx.position) { + txInfo.position = { + ...mempoolTx.position, + accelerated: mempoolTx.acceleration || undefined, + }; + if (!mempoolTx.cpfpChecked) { + calculateCpfp(mempoolTx, newMempool); + } + if (mempoolTx.cpfpDirty) { + txInfo.cpfp = { + ancestors: mempoolTx.ancestors, + bestDescendant: mempoolTx.bestDescendant || null, + descendants: mempoolTx.descendants || null, + effectiveFeePerVsize: mempoolTx.effectiveFeePerVsize || null, + sigops: mempoolTx.sigops, + adjustedVsize: mempoolTx.adjustedVsize, + }; + } + } + txs[txid] = txInfo; + } + if (Object.keys(txs).length) { + response['tracked-txs'] = JSON.stringify(txs); + } + } + if (client['track-mempool-block'] >= 0 && memPool.isInSync()) { const index = client['track-mempool-block']; if (mBlockDeltas[index]) { @@ -723,8 +829,15 @@ class WebsocketHandler { } this.printLogs(); + await statistics.runStatistics(); const _memPool = memPool.getMempool(); + const candidateTxs = await memPool.getMempoolCandidates(); + let candidates: GbtCandidates | undefined = (memPool.limitGBT && candidateTxs) ? { txs: candidateTxs, added: [], removed: [] } : undefined; + let transactionIds: string[] = (memPool.limitGBT) ? Object.keys(candidates?.txs || {}) : Object.keys(_memPool); + + const accelerations = Object.values(mempool.getAccelerations()); + await accelerationRepository.$indexAccelerationsForBlock(block, accelerations, transactions); const rbfTransactions = Common.findMinedRbfTransactions(transactions, memPool.getSpendMap()); memPool.handleMinedRbfTransactions(rbfTransactions); @@ -732,36 +845,23 @@ class WebsocketHandler { if (config.MEMPOOL.AUDIT && memPool.isInSync()) { let projectedBlocks; - let auditMempool = _memPool; + const auditMempool = _memPool; const isAccelerated = config.MEMPOOL_SERVICES.ACCELERATIONS && accelerationApi.isAcceleratedBlock(block, Object.values(mempool.getAccelerations())); - // template calculation functions have mempool side effects, so calculate audits using - // a cloned copy of the mempool if we're running a different algorithm for mempool updates - const separateAudit = config.MEMPOOL.ADVANCED_GBT_AUDIT !== config.MEMPOOL.ADVANCED_GBT_MEMPOOL; - if (separateAudit) { - auditMempool = deepClone(_memPool); - if (config.MEMPOOL.ADVANCED_GBT_AUDIT) { - if (config.MEMPOOL.RUST_GBT) { - projectedBlocks = await mempoolBlocks.$oneOffRustBlockTemplates(auditMempool, isAccelerated, block.extras.pool.id); - } else { - projectedBlocks = await mempoolBlocks.$makeBlockTemplates(auditMempool, false, isAccelerated, block.extras.pool.id); - } + + if ((config.MEMPOOL_SERVICES.ACCELERATIONS)) { + if (config.MEMPOOL.RUST_GBT) { + const added = memPool.limitGBT ? (candidates?.added || []) : []; + const removed = memPool.limitGBT ? (candidates?.removed || []) : []; + projectedBlocks = await mempoolBlocks.$rustUpdateBlockTemplates(transactionIds, auditMempool, added, removed, candidates, isAccelerated, block.extras.pool.id); } else { - projectedBlocks = mempoolBlocks.updateMempoolBlocks(auditMempool, false); + projectedBlocks = await mempoolBlocks.$makeBlockTemplates(transactionIds, auditMempool, candidates, false, isAccelerated, block.extras.pool.id); } } else { - if ((config.MEMPOOL_SERVICES.ACCELERATIONS)) { - if (config.MEMPOOL.RUST_GBT) { - projectedBlocks = await mempoolBlocks.$rustUpdateBlockTemplates(auditMempool, Object.keys(auditMempool).length, [], [], isAccelerated, block.extras.pool.id); - } else { - projectedBlocks = await mempoolBlocks.$makeBlockTemplates(auditMempool, false, isAccelerated, block.extras.pool.id); - } - } else { - projectedBlocks = mempoolBlocks.getMempoolBlocksWithTransactions(); - } + projectedBlocks = mempoolBlocks.getMempoolBlocksWithTransactions(); } if (Common.indexingEnabled()) { - const { censored, added, fresh, sigop, fullrbf, accelerated, score, similarity } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); + const { censored, added, prioritized, fresh, sigop, fullrbf, accelerated, score, similarity } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); const matchRate = Math.round(score * 100 * 100) / 100; const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions : []; @@ -787,6 +887,7 @@ class WebsocketHandler { height: block.height, hash: block.id, addedTxs: added, + prioritizedTxs: prioritized, missingTxs: censored, freshTxs: fresh, sigopTxs: sigop, @@ -820,14 +921,23 @@ class WebsocketHandler { confirmedTxids[txId] = true; } - if (config.MEMPOOL.ADVANCED_GBT_MEMPOOL) { - if (config.MEMPOOL.RUST_GBT) { - await mempoolBlocks.$rustUpdateBlockTemplates(_memPool, Object.keys(_memPool).length, [], transactions, true); - } else { - await mempoolBlocks.$makeBlockTemplates(_memPool, true, config.MEMPOOL_SERVICES.ACCELERATIONS); - } + if (memPool.limitGBT) { + const minFeeMempool = memPool.limitGBT ? await bitcoinSecondClient.getRawMemPool() : null; + const minFeeTip = memPool.limitGBT ? await bitcoinSecondClient.getBlockCount() : -1; + candidates = await memPool.getNextCandidates(minFeeMempool, minFeeTip, transactions); + transactionIds = Object.keys(candidates?.txs || {}); } else { - mempoolBlocks.updateMempoolBlocks(_memPool, true); + candidates = undefined; + transactionIds = Object.keys(memPool.getMempool()); + } + + + if (config.MEMPOOL.RUST_GBT) { + const added = memPool.limitGBT ? (candidates?.added || []) : []; + const removed = memPool.limitGBT ? (candidates?.removed || []) : transactions; + await mempoolBlocks.$rustUpdateBlockTemplates(transactionIds, _memPool, added, removed, candidates, true); + } else { + await mempoolBlocks.$makeBlockTemplates(transactionIds, _memPool, candidates, true, config.MEMPOOL_SERVICES.ACCELERATIONS); } const mBlocks = mempoolBlocks.getMempoolBlocks(); const mBlockDeltas = mempoolBlocks.getMempoolBlockDeltas(); @@ -884,6 +994,10 @@ class WebsocketHandler { response['mempool-blocks'] = getCachedResponse('mempool-blocks', mBlocks); } + if (client['want-tomahawk']) { + response['tomahawk'] = getCachedResponse('tomahawk', bitcoinApi.getHealthStatus()); + } + if (client['track-tx']) { const trackTxid = client['track-tx']; if (trackTxid && confirmedTxids[trackTxid]) { @@ -902,6 +1016,28 @@ class WebsocketHandler { } } + if (client['track-txs']) { + const txs: { [txid: string]: TxTrackingInfo } = {}; + for (const txid of client['track-txs']) { + if (confirmedTxids[txid]) { + txs[txid] = { confirmed: true }; + } else { + const mempoolTx = _memPool[txid]; + if (mempoolTx && mempoolTx.position) { + txs[txid] = { + position: { + ...mempoolTx.position, + }, + accelerated: mempoolTx.acceleration || undefined, + }; + } + } + } + if (Object.keys(txs).length) { + response['tracked-txs'] = JSON.stringify(txs); + } + } + if (client['track-address']) { const foundTransactions: TransactionExtended[] = Array.from(addressCache[client['track-address']]?.values() || []); @@ -999,7 +1135,7 @@ class WebsocketHandler { if (mBlockDeltas[index].added.length > (mBlocksWithTransactions[index]?.transactions.length / 2)) { response['projected-block-transactions'] = getCachedResponse(`projected-block-transactions-full-${index}`, { index: index, - blockTransactions: mBlocksWithTransactions[index].transactions, + blockTransactions: mBlocksWithTransactions[index].transactions.map(mempoolBlocks.compressTx), }); } else { response['projected-block-transactions'] = getCachedResponse(`projected-block-transactions-delta-${index}`, { @@ -1014,6 +1150,8 @@ class WebsocketHandler { client.send(this.serializeResponse(response)); } }); + + await statistics.runStatistics(); } // takes a dictionary of JSON serialized values @@ -1095,6 +1233,7 @@ class WebsocketHandler { private printLogs(): void { if (this.wss) { let numTxSubs = 0; + let numTxsSubs = 0; let numProjectedSubs = 0; let numRbfSubs = 0; @@ -1102,6 +1241,9 @@ class WebsocketHandler { if (client['track-tx']) { numTxSubs++; } + if (client['track-txs']) { + numTxsSubs++; + } if (client['track-mempool-block'] != null && client['track-mempool-block'] >= 0) { numProjectedSubs++; } @@ -1114,7 +1256,7 @@ class WebsocketHandler { const diff = count - this.numClients; this.numClients = count; logger.debug(`${count} websocket clients | ${this.numConnected} connected | ${this.numDisconnected} disconnected | (${diff >= 0 ? '+' : ''}${diff})`); - logger.debug(`websocket subscriptions: track-tx: ${numTxSubs}, track-mempool-block: ${numProjectedSubs} track-rbf: ${numRbfSubs}`); + logger.debug(`websocket subscriptions: track-tx: ${numTxSubs}, track-txs: ${numTxsSubs}, track-mempool-block: ${numProjectedSubs} track-rbf: ${numRbfSubs}`); this.numConnected = 0; this.numDisconnected = 0; } diff --git a/backend/src/config.ts b/backend/src/config.ts index df1022a67..93ac90834 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -5,6 +5,7 @@ const configFromFile = require( interface IConfig { MEMPOOL: { ENABLED: boolean; + OFFICIAL: boolean; NETWORK: 'mainnet' | 'testnet' | 'signet' | 'liquid' | 'liquidtestnet'; BACKEND: 'esplora' | 'electrum' | 'none'; HTTP_PORT: number; @@ -20,6 +21,7 @@ interface IConfig { MEMPOOL_BLOCKS_AMOUNT: number; INDEXING_BLOCKS_AMOUNT: number; BLOCKS_SUMMARIES_INDEXING: boolean; + GOGGLES_INDEXING: boolean; USE_SECOND_NODE_FOR_MINFEE: boolean; EXTERNAL_ASSETS: string[]; EXTERNAL_MAX_RETRY: number; @@ -30,9 +32,8 @@ interface IConfig { POOLS_JSON_URL: string, POOLS_JSON_TREE_URL: string, AUDIT: boolean; - ADVANCED_GBT_AUDIT: boolean; - ADVANCED_GBT_MEMPOOL: boolean; RUST_GBT: boolean; + LIMIT_GBT: boolean; CPFP_INDEXING: boolean; MAX_BLOCKS_BULK_QUERY: number; DISK_CACHE_BLOCK_INTERVAL: number; @@ -102,6 +103,7 @@ interface IConfig { PASSWORD: string; TIMEOUT: number; PID_DIR: string; + POOL_SIZE: number; }; SYSLOG: { ENABLED: boolean; @@ -114,10 +116,6 @@ interface IConfig { ENABLED: boolean; TX_PER_SECOND_SAMPLE_PERIOD: number; }; - BISQ: { - ENABLED: boolean; - DATA_PATH: string; - }; SOCKS5PROXY: { ENABLED: boolean; USE_ONION: boolean; @@ -131,8 +129,6 @@ interface IConfig { MEMPOOL_ONION: string; LIQUID_API: string; LIQUID_ONION: string; - BISQ_URL: string; - BISQ_ONION: string; }; MAXMIND: { ENABLED: boolean; @@ -155,11 +151,16 @@ interface IConfig { UNIX_SOCKET_PATH: string; BATCH_QUERY_BASE_SIZE: number; }, + FIAT_PRICE: { + ENABLED: boolean; + API_KEY: string; + }, } const defaults: IConfig = { 'MEMPOOL': { 'ENABLED': true, + 'OFFICIAL': false, 'NETWORK': 'mainnet', 'BACKEND': 'none', 'HTTP_PORT': 8999, @@ -175,6 +176,7 @@ const defaults: IConfig = { 'MEMPOOL_BLOCKS_AMOUNT': 8, 'INDEXING_BLOCKS_AMOUNT': 11000, // 0 = disable indexing, -1 = index all blocks 'BLOCKS_SUMMARIES_INDEXING': false, + 'GOGGLES_INDEXING': false, 'USE_SECOND_NODE_FOR_MINFEE': false, 'EXTERNAL_ASSETS': [], 'EXTERNAL_MAX_RETRY': 1, @@ -185,9 +187,8 @@ const defaults: IConfig = { 'POOLS_JSON_URL': 'https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json', 'POOLS_JSON_TREE_URL': 'https://api.github.com/repos/mempool/mining-pools/git/trees/master', 'AUDIT': false, - 'ADVANCED_GBT_AUDIT': false, - 'ADVANCED_GBT_MEMPOOL': false, 'RUST_GBT': false, + 'LIMIT_GBT': false, 'CPFP_INDEXING': false, 'MAX_BLOCKS_BULK_QUERY': 0, 'DISK_CACHE_BLOCK_INTERVAL': 6, @@ -238,6 +239,7 @@ const defaults: IConfig = { 'PASSWORD': 'mempool', 'TIMEOUT': 180000, 'PID_DIR': '', + 'POOL_SIZE': 100, }, 'SYSLOG': { 'ENABLED': true, @@ -250,10 +252,6 @@ const defaults: IConfig = { 'ENABLED': true, 'TX_PER_SECOND_SAMPLE_PERIOD': 150 }, - 'BISQ': { - 'ENABLED': false, - 'DATA_PATH': '/bisq/statsnode-data/btc_mainnet/db' - }, 'LIGHTNING': { 'ENABLED': false, 'BACKEND': 'lnd', @@ -285,9 +283,7 @@ const defaults: IConfig = { 'MEMPOOL_API': 'https://mempool.space/api/v1', 'MEMPOOL_ONION': 'http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/api/v1', 'LIQUID_API': 'https://liquid.network/api/v1', - 'LIQUID_ONION': 'http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1', - 'BISQ_URL': 'https://bisq.markets/api', - 'BISQ_ONION': 'http://bisqmktse2cabavbr2xjq7xw3h6g5ottemo5rolfcwt6aly6tp5fdryd.onion/api' + 'LIQUID_ONION': 'http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1' }, 'MAXMIND': { 'ENABLED': false, @@ -310,6 +306,10 @@ const defaults: IConfig = { 'UNIX_SOCKET_PATH': '', 'BATCH_QUERY_BASE_SIZE': 5000, }, + 'FIAT_PRICE': { + 'ENABLED': true, + 'API_KEY': '', + }, }; class Config implements IConfig { @@ -321,7 +321,6 @@ class Config implements IConfig { DATABASE: IConfig['DATABASE']; SYSLOG: IConfig['SYSLOG']; STATISTICS: IConfig['STATISTICS']; - BISQ: IConfig['BISQ']; LIGHTNING: IConfig['LIGHTNING']; LND: IConfig['LND']; CLIGHTNING: IConfig['CLIGHTNING']; @@ -331,6 +330,7 @@ class Config implements IConfig { REPLICATION: IConfig['REPLICATION']; MEMPOOL_SERVICES: IConfig['MEMPOOL_SERVICES']; REDIS: IConfig['REDIS']; + FIAT_PRICE: IConfig['FIAT_PRICE']; constructor() { const configs = this.merge(configFromFile, defaults); @@ -342,7 +342,6 @@ class Config implements IConfig { this.DATABASE = configs.DATABASE; this.SYSLOG = configs.SYSLOG; this.STATISTICS = configs.STATISTICS; - this.BISQ = configs.BISQ; this.LIGHTNING = configs.LIGHTNING; this.LND = configs.LND; this.CLIGHTNING = configs.CLIGHTNING; @@ -352,6 +351,7 @@ class Config implements IConfig { this.REPLICATION = configs.REPLICATION; this.MEMPOOL_SERVICES = configs.MEMPOOL_SERVICES; this.REDIS = configs.REDIS; + this.FIAT_PRICE = configs.FIAT_PRICE; } merge = (...objects: object[]): IConfig => { diff --git a/backend/src/database.ts b/backend/src/database.ts index dc543bbbc..05f624ff4 100644 --- a/backend/src/database.ts +++ b/backend/src/database.ts @@ -21,7 +21,7 @@ import { execSync } from 'child_process'; database: config.DATABASE.DATABASE, user: config.DATABASE.USERNAME, password: config.DATABASE.PASSWORD, - connectionLimit: 10, + connectionLimit: config.DATABASE.POOL_SIZE, supportBigNumbers: true, timezone: '+00:00', }; diff --git a/backend/src/index.ts b/backend/src/index.ts index a7b2ad4df..b28e9ccbf 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -11,8 +11,6 @@ import memPool from './api/mempool'; import diskCache from './api/disk-cache'; import statistics from './api/statistics/statistics'; import websocketHandler from './api/websocket-handler'; -import bisq from './api/bisq/bisq'; -import bisqMarkets from './api/bisq/markets'; import logger from './logger'; import backendInfo from './api/backend-info'; import loadingIndicators from './api/loading-indicators'; @@ -32,7 +30,6 @@ import networkSyncService from './tasks/lightning/network-sync.service'; import statisticsRoutes from './api/statistics/statistics.routes'; import pricesRoutes from './api/prices/prices.routes'; import miningRoutes from './api/mining/mining-routes'; -import bisqRoutes from './api/bisq/bisq.routes'; import liquidRoutes from './api/liquid/liquid.routes'; import bitcoinRoutes from './api/bitcoin/bitcoin.routes'; import fundingTxFetcher from './tasks/lightning/sync-tasks/funding-tx-fetcher'; @@ -45,6 +42,7 @@ import { formatBytes, getBytesUnit } from './utils/format'; import redisCache from './api/redis-cache'; import accelerationApi from './api/services/acceleration'; import bitcoinCoreRoutes from './api/bitcoin/bitcoin-core.routes'; +import bitcoinSecondClient from './api/bitcoin/bitcoin-second-client'; class Server { private wss: WebSocket.Server | undefined; @@ -131,7 +129,7 @@ class Server { .use(express.text({ type: ['text/plain', 'application/base64'] })) ; - if (config.DATABASE.ENABLED) { + if (config.DATABASE.ENABLED && config.FIAT_PRICE.ENABLED) { await priceUpdater.$initializeLatestPriceWithDb(); } @@ -155,14 +153,22 @@ class Server { } if (Common.isLiquid()) { - try { - icons.loadIcons(); - } catch (e) { - logger.err('Cannot load liquid icons. Ignoring. Reason: ' + (e instanceof Error ? e.message : e)); - } + const refreshIcons = () => { + try { + icons.loadIcons(); + } catch (e) { + logger.err('Cannot load liquid icons. Ignoring. Reason: ' + (e instanceof Error ? e.message : e)); + } + }; + // Run once on startup. + refreshIcons(); + // Matches crontab refresh interval for asset db. + setInterval(refreshIcons, 3600_000); } - priceUpdater.$run(); + if (config.FIAT_PRICE.ENABLED) { + priceUpdater.$run(); + } await chainTips.updateOrphanedBlocks(); this.setUpHttpApiRoutes(); @@ -173,13 +179,6 @@ class Server { setInterval(() => { this.healthCheck(); }, 2500); - if (config.BISQ.ENABLED) { - bisq.startBisqService(); - bisq.setPriceCallbackFunction((price) => websocketHandler.setExtraInitData('bsq-price', price)); - blocks.setNewBlockCallback(bisq.handleNewBitcoinBlock.bind(bisq)); - bisqMarkets.startBisqService(); - } - if (config.LIGHTNING.ENABLED) { this.$runLightningBackend(); } @@ -207,14 +206,18 @@ class Server { } } const newMempool = await bitcoinApi.$getRawMempool(); + const minFeeMempool = memPool.limitGBT ? await bitcoinSecondClient.getRawMemPool() : null; + const minFeeTip = memPool.limitGBT ? await bitcoinSecondClient.getBlockCount() : -1; const newAccelerations = await accelerationApi.$fetchAccelerations(); const numHandledBlocks = await blocks.$updateBlocks(); const pollRate = config.MEMPOOL.POLL_RATE_MS * (indexer.indexerIsRunning() ? 10 : 1); if (numHandledBlocks === 0) { - await memPool.$updateMempool(newMempool, newAccelerations, pollRate); + await memPool.$updateMempool(newMempool, newAccelerations, minFeeMempool, minFeeTip, pollRate); } indexer.$run(); - priceUpdater.$run(); + if (config.FIAT_PRICE.ENABLED) { + priceUpdater.$run(); + } // rerun immediately if we skipped the mempool update, otherwise wait POLL_RATE_MS const elapsed = Date.now() - start; @@ -266,6 +269,7 @@ class Server { blocks.setNewBlockCallback(async () => { try { await elementsParser.$parse(); + await elementsParser.$updateFederationUtxos(); } catch (e) { logger.warn('Elements parsing error: ' + (e instanceof Error ? e.message : e)); } @@ -277,7 +281,9 @@ class Server { memPool.setAsyncMempoolChangedCallback(websocketHandler.$handleMempoolChange.bind(websocketHandler)); blocks.setNewAsyncBlockCallback(websocketHandler.handleNewBlock.bind(websocketHandler)); } - priceUpdater.setRatesChangedCallback(websocketHandler.handleNewConversionRates.bind(websocketHandler)); + if (config.FIAT_PRICE.ENABLED) { + priceUpdater.setRatesChangedCallback(websocketHandler.handleNewConversionRates.bind(websocketHandler)); + } loadingIndicators.setProgressChangedCallback(websocketHandler.handleLoadingChanged.bind(websocketHandler)); } @@ -291,9 +297,6 @@ class Server { if (Common.indexingEnabled() && config.MEMPOOL.ENABLED) { miningRoutes.initRoutes(this.app); } - if (config.BISQ.ENABLED) { - bisqRoutes.initRoutes(this.app); - } if (Common.isLiquid()) { liquidRoutes.initRoutes(this.app); } diff --git a/backend/src/indexer.ts b/backend/src/indexer.ts index 90b4a59e6..bc169630f 100644 --- a/backend/src/indexer.ts +++ b/backend/src/indexer.ts @@ -8,6 +8,7 @@ import priceUpdater from './tasks/price-updater'; import PricesRepository from './repositories/PricesRepository'; import config from './config'; import auditReplicator from './replication/AuditReplication'; +import AccelerationRepository from './repositories/AccelerationRepository'; export interface CoreIndex { name: string; @@ -116,7 +117,7 @@ class Indexer { switch (task) { case 'blocksPrices': { - if (!['testnet', 'signet'].includes(config.MEMPOOL.NETWORK)) { + if (!['testnet', 'signet'].includes(config.MEMPOOL.NETWORK) && config.FIAT_PRICE.ENABLED) { let lastestPriceId; try { lastestPriceId = await PricesRepository.$getLatestPriceId(); @@ -148,10 +149,12 @@ class Indexer { return; } - try { - await priceUpdater.$run(); - } catch (e) { - logger.err(`Running priceUpdater failed. Reason: ` + (e instanceof Error ? e.message : e)); + if (config.FIAT_PRICE.ENABLED) { + try { + await priceUpdater.$run(); + } catch (e) { + logger.err(`Running priceUpdater failed. Reason: ` + (e instanceof Error ? e.message : e)); + } } // Do not attempt to index anything unless Bitcoin Core is fully synced @@ -185,7 +188,9 @@ class Indexer { await blocks.$generateCPFPDatabase(); await blocks.$generateAuditStats(); await auditReplicator.$sync(); - await blocks.$classifyBlocks(); + await AccelerationRepository.$indexPastAccelerations(); + // do not wait for classify blocks to finish + blocks.$classifyBlocks(); } catch (e) { this.indexerRunning = false; logger.err(`Indexer failed, trying again in 10 seconds. Reason: ` + (e instanceof Error ? e.message : e)); diff --git a/backend/src/logger.ts b/backend/src/logger.ts index bbd781df6..bce77c63f 100644 --- a/backend/src/logger.ts +++ b/backend/src/logger.ts @@ -86,9 +86,6 @@ class Logger { if (config.LIGHTNING.ENABLED) { return config.MEMPOOL.NETWORK === 'mainnet' ? 'lightning' : `${config.MEMPOOL.NETWORK}-lightning`; } - if (config.BISQ.ENABLED) { - return 'bisq'; - } if (config.MEMPOOL.NETWORK && config.MEMPOOL.NETWORK !== 'mainnet') { return config.MEMPOOL.NETWORK; } diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index ead0a84ad..0b4b20e02 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -37,6 +37,7 @@ export interface BlockAudit { sigopTxs: string[], fullrbfTxs: string[], addedTxs: string[], + prioritizedTxs: string[], acceleratedTxs: string[], matchRate: number, expectedFees?: number, @@ -65,9 +66,9 @@ export interface MempoolBlockWithTransactions extends MempoolBlock { } export interface MempoolBlockDelta { - added: TransactionClassified[]; + added: TransactionCompressed[]; removed: string[]; - changed: { txid: string, rate: number | undefined, flags?: number }[]; + changed: MempoolDeltaChange[]; } interface VinStrippedToScriptsig { @@ -107,6 +108,7 @@ export interface MempoolTransactionExtended extends TransactionExtended { inputs?: number[]; lastBoosted?: number; cpfpDirty?: boolean; + cpfpUpdated?: number; } export interface AuditTransaction { @@ -143,6 +145,12 @@ export interface CompactThreadTransaction { dirty?: boolean; } +export interface GbtCandidates { + txs: { [txid: string ]: boolean }, + added: MempoolTransactionExtended[]; + removed: MempoolTransactionExtended[]; +} + export interface ThreadTransaction { txid: string; fee: number; @@ -181,6 +189,9 @@ export interface CpfpInfo { bestDescendant?: BestDescendant | null; descendants?: Ancestor[]; effectiveFeePerVsize?: number; + sigops?: number; + adjustedVsize?: number, + acceleration?: boolean, } export interface TransactionStripped { @@ -190,12 +201,18 @@ export interface TransactionStripped { value: number; acc?: boolean; rate?: number; // effective fee rate + time?: number; } export interface TransactionClassified extends TransactionStripped { flags: number; } +// [txid, fee, vsize, value, rate, flags, acceleration?] +export type TransactionCompressed = [string, number, number, number, number, number, number, 1?]; +// [txid, rate, flags, acceleration?] +export type MempoolDeltaChange = [string, number, number, (1|0)]; + // binary flags for transaction classification export const TransactionFlags = { // features @@ -203,6 +220,8 @@ export const TransactionFlags = { no_rbf: 0b00000010n, v1: 0b00000100n, v2: 0b00001000n, + v3: 0b00010000n, + nonstandard: 0b00100000n, // address types p2pk: 0b00000001_00000000n, p2ms: 0b00000010_00000000n, @@ -219,6 +238,7 @@ export const TransactionFlags = { op_return: 0b00000001_00000000_00000000_00000000n, fake_pubkey: 0b00000010_00000000_00000000_00000000n, inscription: 0b00000100_00000000_00000000_00000000n, + fake_scripthash: 0b00001000_00000000_00000000_00000000n, // heuristics coinjoin: 0b00000001_00000000_00000000_00000000_00000000n, consolidation: 0b00000010_00000000_00000000_00000000_00000000n, @@ -393,13 +413,28 @@ export interface OptimizedStatistic { vsizes: number[]; } +export interface TxTrackingInfo { + replacedBy?: string, + position?: { block: number, vsize: number, accelerated?: boolean }, + cpfp?: { + ancestors?: Ancestor[], + bestDescendant?: Ancestor | null, + descendants?: Ancestor[] | null, + effectiveFeePerVsize?: number | null, + sigops: number, + adjustedVsize: number, + }, + utxoSpent?: { [vout: number]: { vin: number, txid: string } }, + accelerated?: boolean, + confirmed?: boolean +} + export interface WebsocketResponse { action: string; data: string[]; 'track-tx': string; 'track-address': string; 'watch-mempool': boolean; - 'track-bisq-market': string; } export interface VbytesPerSecond { @@ -421,6 +456,7 @@ export interface IBackendInfo { gitCommit: string; version: string; lightning: boolean; + backend: 'esplora' | 'electrum' | 'none'; } export interface IDifficultyAdjustment { diff --git a/backend/src/replication/AuditReplication.ts b/backend/src/replication/AuditReplication.ts index 503c61613..4ea629839 100644 --- a/backend/src/replication/AuditReplication.ts +++ b/backend/src/replication/AuditReplication.ts @@ -114,6 +114,7 @@ class AuditReplication { time: auditSummary.timestamp || auditSummary.time, missingTxs: auditSummary.missingTxs || [], addedTxs: auditSummary.addedTxs || [], + prioritizedTxs: auditSummary.prioritizedTxs || [], freshTxs: auditSummary.freshTxs || [], sigopTxs: auditSummary.sigopTxs || [], fullrbfTxs: auditSummary.fullrbfTxs || [], diff --git a/backend/src/repositories/AccelerationRepository.ts b/backend/src/repositories/AccelerationRepository.ts new file mode 100644 index 000000000..1c91df050 --- /dev/null +++ b/backend/src/repositories/AccelerationRepository.ts @@ -0,0 +1,322 @@ +import { AccelerationInfo, makeBlockTemplate } from '../api/acceleration'; +import { RowDataPacket } from 'mysql2'; +import DB from '../database'; +import logger from '../logger'; +import { IEsploraApi } from '../api/bitcoin/esplora-api.interface'; +import { Common } from '../api/common'; +import config from '../config'; +import blocks from '../api/blocks'; +import accelerationApi, { Acceleration } from '../api/services/acceleration'; +import accelerationCosts from '../api/acceleration'; +import bitcoinApi from '../api/bitcoin/bitcoin-api-factory'; +import transactionUtils from '../api/transaction-utils'; +import { BlockExtended, MempoolTransactionExtended } from '../mempool.interfaces'; + +export interface PublicAcceleration { + txid: string, + height: number, + pool: { + id: number, + slug: string, + name: string, + }, + effective_vsize: number, + effective_fee: number, + boost_rate: number, + boost_cost: number, +} + +class AccelerationRepository { + private bidBoostV2Activated = 831580; + + public async $saveAcceleration(acceleration: AccelerationInfo, block: IEsploraApi.Block, pool_id: number): Promise { + try { + await DB.query(` + INSERT INTO accelerations(txid, added, height, pool, effective_vsize, effective_fee, boost_rate, boost_cost) + VALUE (?, FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + height = ? + `, [ + acceleration.txSummary.txid, + block.timestamp, + block.height, + pool_id, + acceleration.txSummary.effectiveVsize, + acceleration.txSummary.effectiveFee, + acceleration.targetFeeRate, + acceleration.cost, + block.height, + ]); + } catch (e: any) { + logger.err(`Cannot save acceleration (${acceleration.txSummary.txid}) into db. Reason: ` + (e instanceof Error ? e.message : e)); + // We don't throw, not a critical issue if we miss some accelerations + } + } + + public async $getAccelerationInfo(poolSlug: string | null = null, height: number | null = null, interval: string | null = null): Promise { + if (!interval || !['24h', '3d', '1w', '1m'].includes(interval)) { + interval = '1m'; + } + interval = Common.getSqlInterval(interval); + + if (!config.MEMPOOL_SERVICES.ACCELERATIONS || (interval == null && poolSlug == null && height == null)) { + return []; + } + + let query = ` + SELECT * FROM accelerations + JOIN pools on pools.unique_id = accelerations.pool + `; + let params: any[] = []; + let hasFilter = false; + + if (interval && height === null) { + query += ` WHERE accelerations.added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() `; + hasFilter = true; + } + + if (height != null) { + if (hasFilter) { + query += ` AND accelerations.height = ? `; + } else { + query += ` WHERE accelerations.height = ? `; + } + params.push(height); + } else if (poolSlug != null) { + if (hasFilter) { + query += ` AND pools.slug = ? `; + } else { + query += ` WHERE pools.slug = ? `; + } + params.push(poolSlug); + } + + query += ` ORDER BY accelerations.added DESC `; + + try { + const [rows] = await DB.query(query, params) as RowDataPacket[][]; + if (rows?.length) { + return rows.map(row => ({ + txid: row.txid, + height: row.height, + pool: { + id: row.id, + slug: row.slug, + name: row.name, + }, + effective_vsize: row.effective_vsize, + effective_fee: row.effective_fee, + boost_rate: row.boost_rate, + boost_cost: row.boost_cost, + })); + } else { + return []; + } + } catch (e) { + logger.err(`Cannot query acceleration info. Reason: ` + (e instanceof Error ? e.message : e)); + throw e; + } + } + + public async $getAccelerationTotals(poolSlug: string | null = null, interval: string | null = null): Promise<{ cost: number, count: number }> { + interval = Common.getSqlInterval(interval); + + if (!config.MEMPOOL_SERVICES.ACCELERATIONS) { + return { cost: 0, count: 0 }; + } + + let query = ` + SELECT SUM(boost_cost) as total_cost, COUNT(txid) as count FROM accelerations + JOIN pools on pools.unique_id = accelerations.pool + `; + let params: any[] = []; + let hasFilter = false; + + if (interval) { + query += ` WHERE accelerations.added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() `; + hasFilter = true; + } + if (poolSlug != null) { + if (hasFilter) { + query += ` AND pools.slug = ? `; + } else { + query += ` WHERE pools.slug = ? `; + } + params.push(poolSlug); + } + + try { + const [rows] = await DB.query(query, params) as RowDataPacket[][]; + return { + cost: rows[0]?.total_cost || 0, + count: rows[0]?.count || 0, + }; + } catch (e) { + logger.err(`Cannot query acceleration totals. Reason: ` + (e instanceof Error ? e.message : e)); + throw e; + } + } + + public async $getLastSyncedHeight(): Promise { + try { + const [rows] = await DB.query(` + SELECT * FROM state + WHERE name = 'last_acceleration_block' + `); + if (rows?.['length']) { + return rows[0].number; + } + } catch (e: any) { + logger.err(`Cannot find last acceleration sync height. Reason: ` + (e instanceof Error ? e.message : e)); + } + return 0; + } + + private async $setLastSyncedHeight(height: number): Promise { + try { + await DB.query(` + UPDATE state + SET number = ? + WHERE name = 'last_acceleration_block' + `, [height]); + } catch (e: any) { + logger.err(`Cannot update last acceleration sync height. Reason: ` + (e instanceof Error ? e.message : e)); + } + } + + public async $indexAccelerationsForBlock(block: BlockExtended, accelerations: Acceleration[], transactions: MempoolTransactionExtended[]): Promise { + const blockTxs: { [txid: string]: MempoolTransactionExtended } = {}; + for (const tx of transactions) { + blockTxs[tx.txid] = tx; + } + const successfulAccelerations = accelerations.filter(acc => acc.pools.includes(block.extras.pool.id)); + let boostRate: number | null = null; + for (const acc of successfulAccelerations) { + if (boostRate === null) { + boostRate = accelerationCosts.calculateBoostRate( + accelerations.map(acc => ({ txid: acc.txid, max_bid: acc.feeDelta })), + transactions + ); + } + if (blockTxs[acc.txid]) { + const tx = blockTxs[acc.txid]; + const accelerationInfo = accelerationCosts.getAccelerationInfo(tx, boostRate, transactions); + accelerationInfo.cost = Math.max(0, Math.min(acc.feeDelta, accelerationInfo.cost)); + this.$saveAcceleration(accelerationInfo, block, block.extras.pool.id); + } + } + const lastSyncedHeight = await this.$getLastSyncedHeight(); + // if we've missed any blocks, let the indexer catch up from the last synced height on the next run + if (block.height === lastSyncedHeight + 1) { + await this.$setLastSyncedHeight(block.height); + } + } + + /** + * [INDEXING] Backfill missing acceleration data + */ + async $indexPastAccelerations(): Promise { + if (config.MEMPOOL.NETWORK !== 'mainnet' || !config.MEMPOOL_SERVICES.ACCELERATIONS) { + // acceleration history disabled + return; + } + const lastSyncedHeight = await this.$getLastSyncedHeight(); + const currentHeight = blocks.getCurrentBlockHeight(); + if (currentHeight <= lastSyncedHeight) { + // already in sync + return; + } + + logger.debug(`Fetching accelerations between block ${lastSyncedHeight} and ${currentHeight}`); + + // Fetch accelerations from mempool.space since the last synced block; + const accelerationsByBlock = {}; + const blockHashes = {}; + let done = false; + let page = 1; + let count = 0; + try { + while (!done) { + const accelerations = await accelerationApi.$fetchAccelerationHistory(page); + page++; + if (!accelerations?.length) { + done = true; + break; + } + for (const acc of accelerations) { + if (acc.status !== 'completed_provisional' && acc.status !== 'completed') { + continue; + } + if (!lastSyncedHeight || acc.blockHeight > lastSyncedHeight) { + if (!accelerationsByBlock[acc.blockHeight]) { + accelerationsByBlock[acc.blockHeight] = []; + blockHashes[acc.blockHeight] = acc.blockHash; + } + accelerationsByBlock[acc.blockHeight].push(acc); + count++; + } else { + done = true; + } + } + } + } catch (e) { + logger.err(`Failed to fetch full acceleration history. Reason: ` + (e instanceof Error ? e.message : e)); + } + + logger.debug(`Indexing ${count} accelerations between block ${lastSyncedHeight} and ${currentHeight}`); + + // process accelerated blocks in order + const heights = Object.keys(accelerationsByBlock).map(key => parseInt(key)).sort((a,b) => a - b); + for (const height of heights) { + const accelerations = accelerationsByBlock[height]; + try { + const block = await blocks.$getBlock(blockHashes[height]) as BlockExtended; + const transactions = (await bitcoinApi.$getTxsForBlock(blockHashes[height])).map(tx => transactionUtils.extendMempoolTransaction(tx)); + + const blockTxs = {}; + for (const tx of transactions) { + blockTxs[tx.txid] = tx; + } + + let boostRate = 0; + // use Bid Boost V2 if active + if (height > this.bidBoostV2Activated) { + boostRate = accelerationCosts.calculateBoostRate( + accelerations.map(acc => ({ txid: acc.txid, max_bid: acc.feeDelta })), + transactions + ); + } else { + // default to Bid Boost V1 (median block fee rate) + const template = makeBlockTemplate( + transactions, + accelerations.map(acc => ({ txid: acc.txid, max_bid: acc.feeDelta })), + 1, + Infinity, + Infinity + ); + const feeStats = Common.calcEffectiveFeeStatistics(template); + boostRate = feeStats.medianFee; + } + for (const acc of accelerations) { + if (blockTxs[acc.txid]) { + const tx = blockTxs[acc.txid]; + const accelerationInfo = accelerationCosts.getAccelerationInfo(tx, boostRate, transactions); + accelerationInfo.cost = Math.max(0, Math.min(acc.feeDelta, accelerationInfo.cost)); + await this.$saveAcceleration(accelerationInfo, block, block.extras.pool.id); + } + } + await this.$setLastSyncedHeight(height); + } catch (e) { + logger.err(`Failed to process accelerations for block ${height}. Reason: ` + (e instanceof Error ? e.message : e)); + return; + } + logger.debug(`Indexed ${accelerations.length} accelerations in block ${height}`); + } + + await this.$setLastSyncedHeight(currentHeight); + + logger.debug(`Indexing accelerations completed`); + } +} + +export default new AccelerationRepository(); diff --git a/backend/src/repositories/BlocksAuditsRepository.ts b/backend/src/repositories/BlocksAuditsRepository.ts index c17958d2b..daf1ba52d 100644 --- a/backend/src/repositories/BlocksAuditsRepository.ts +++ b/backend/src/repositories/BlocksAuditsRepository.ts @@ -6,9 +6,9 @@ import { BlockAudit, AuditScore } from '../mempool.interfaces'; class BlocksAuditRepositories { public async $saveAudit(audit: BlockAudit): Promise { try { - await DB.query(`INSERT INTO blocks_audits(time, height, hash, missing_txs, added_txs, fresh_txs, sigop_txs, fullrbf_txs, accelerated_txs, match_rate, expected_fees, expected_weight) - VALUE (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [audit.time, audit.height, audit.hash, JSON.stringify(audit.missingTxs), - JSON.stringify(audit.addedTxs), JSON.stringify(audit.freshTxs), JSON.stringify(audit.sigopTxs), JSON.stringify(audit.fullrbfTxs), JSON.stringify(audit.acceleratedTxs), audit.matchRate, audit.expectedFees, audit.expectedWeight]); + await DB.query(`INSERT INTO blocks_audits(time, height, hash, missing_txs, added_txs, prioritized_txs, fresh_txs, sigop_txs, fullrbf_txs, accelerated_txs, match_rate, expected_fees, expected_weight) + VALUE (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [audit.time, audit.height, audit.hash, JSON.stringify(audit.missingTxs), + JSON.stringify(audit.addedTxs), JSON.stringify(audit.prioritizedTxs), JSON.stringify(audit.freshTxs), JSON.stringify(audit.sigopTxs), JSON.stringify(audit.fullrbfTxs), JSON.stringify(audit.acceleratedTxs), audit.matchRate, audit.expectedFees, audit.expectedWeight]); } catch (e: any) { if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart logger.debug(`Cannot save block audit for block ${audit.hash} because it has already been indexed, ignoring`); @@ -59,13 +59,14 @@ class BlocksAuditRepositories { } } - public async $getBlockAudit(hash: string): Promise { + public async $getBlockAudit(hash: string): Promise { try { const [rows]: any[] = await DB.query( `SELECT blocks_audits.height, blocks_audits.hash as id, UNIX_TIMESTAMP(blocks_audits.time) as timestamp, template, missing_txs as missingTxs, added_txs as addedTxs, + prioritized_txs as prioritizedTxs, fresh_txs as freshTxs, sigop_txs as sigopTxs, fullrbf_txs as fullrbfTxs, @@ -75,12 +76,13 @@ class BlocksAuditRepositories { expected_weight as expectedWeight FROM blocks_audits JOIN blocks_templates ON blocks_templates.id = blocks_audits.hash - WHERE blocks_audits.hash = "${hash}" - `); + WHERE blocks_audits.hash = ? + `, [hash]); if (rows.length) { rows[0].missingTxs = JSON.parse(rows[0].missingTxs); rows[0].addedTxs = JSON.parse(rows[0].addedTxs); + rows[0].prioritizedTxs = JSON.parse(rows[0].prioritizedTxs); rows[0].freshTxs = JSON.parse(rows[0].freshTxs); rows[0].sigopTxs = JSON.parse(rows[0].sigopTxs); rows[0].fullrbfTxs = JSON.parse(rows[0].fullrbfTxs); @@ -101,8 +103,8 @@ class BlocksAuditRepositories { const [rows]: any[] = await DB.query( `SELECT hash, match_rate as matchRate, expected_fees as expectedFees, expected_weight as expectedWeight FROM blocks_audits - WHERE blocks_audits.hash = "${hash}" - `); + WHERE blocks_audits.hash = ? + `, [hash]); return rows[0]; } catch (e: any) { logger.err(`Cannot fetch block audit from db. Reason: ` + (e instanceof Error ? e.message : e)); diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index a2a084265..e6e92d60f 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -5,7 +5,7 @@ import logger from '../logger'; import { Common } from '../api/common'; import PoolsRepository from './PoolsRepository'; import HashratesRepository from './HashratesRepository'; -import { escape } from 'mysql2'; +import { RowDataPacket, escape } from 'mysql2'; import BlocksSummariesRepository from './BlocksSummariesRepository'; import DifficultyAdjustmentsRepository from './DifficultyAdjustmentsRepository'; import bitcoinClient from '../api/bitcoin/bitcoin-client'; @@ -478,7 +478,7 @@ class BlocksRepository { public async $getBlocksByPool(slug: string, startHeight?: number): Promise { const pool = await PoolsRepository.$getPool(slug); if (!pool) { - throw new Error('This mining pool does not exist ' + escape(slug)); + throw new Error('This mining pool does not exist'); } const params: any[] = []; @@ -802,10 +802,10 @@ class BlocksRepository { /** * Get a list of blocks that have been indexed */ - public async $getIndexedBlocks(): Promise { + public async $getIndexedBlocks(): Promise<{ height: number, hash: string }[]> { try { - const [rows]: any = await DB.query(`SELECT height, hash FROM blocks ORDER BY height DESC`); - return rows; + const [rows] = await DB.query(`SELECT height, hash FROM blocks ORDER BY height DESC`) as RowDataPacket[][]; + return rows as { height: number, hash: string }[]; } catch (e) { logger.err('Cannot generate block size and weight history. Reason: ' + (e instanceof Error ? e.message : e)); throw e; @@ -815,7 +815,7 @@ class BlocksRepository { /** * Get a list of blocks that have not had CPFP data indexed */ - public async $getCPFPUnindexedBlocks(): Promise { + public async $getCPFPUnindexedBlocks(): Promise { try { const blockchainInfo = await bitcoinClient.getBlockchainInfo(); const currentBlockHeight = blockchainInfo.blocks; @@ -825,13 +825,13 @@ class BlocksRepository { } const minHeight = Math.max(0, currentBlockHeight - indexingBlockAmount + 1); - const [rows]: any[] = await DB.query(` + const [rows] = await DB.query(` SELECT height FROM compact_cpfp_clusters WHERE height <= ? AND height >= ? GROUP BY height ORDER BY height DESC; - `, [currentBlockHeight, minHeight]); + `, [currentBlockHeight, minHeight]) as RowDataPacket[][]; const indexedHeights = {}; rows.forEach((row) => { indexedHeights[row.height] = true; }); diff --git a/backend/src/repositories/BlocksSummariesRepository.ts b/backend/src/repositories/BlocksSummariesRepository.ts index f85914e31..63ad5ddf2 100644 --- a/backend/src/repositories/BlocksSummariesRepository.ts +++ b/backend/src/repositories/BlocksSummariesRepository.ts @@ -1,3 +1,4 @@ +import { RowDataPacket } from 'mysql2'; import DB from '../database'; import logger from '../logger'; import { BlockSummary, TransactionClassified } from '../mempool.interfaces'; @@ -69,7 +70,7 @@ class BlocksSummariesRepository { public async $getIndexedSummariesId(): Promise { try { - const [rows]: any[] = await DB.query(`SELECT id from blocks_summaries`); + const [rows] = await DB.query(`SELECT id from blocks_summaries`) as RowDataPacket[][]; return rows.map(row => row.id); } catch (e) { logger.err(`Cannot get block summaries id list. Reason: ` + (e instanceof Error ? e.message : e)); diff --git a/backend/src/repositories/HashratesRepository.ts b/backend/src/repositories/HashratesRepository.ts index 96cbf6f75..ec44afebe 100644 --- a/backend/src/repositories/HashratesRepository.ts +++ b/backend/src/repositories/HashratesRepository.ts @@ -139,7 +139,7 @@ class HashratesRepository { public async $getPoolWeeklyHashrate(slug: string): Promise { const pool = await PoolsRepository.$getPool(slug); if (!pool) { - throw new Error('This mining pool does not exist ' + escape(slug)); + throw new Error('This mining pool does not exist'); } // Find hashrate boundaries diff --git a/backend/src/repositories/PricesRepository.ts b/backend/src/repositories/PricesRepository.ts index ed9d1fd72..13392f0cf 100644 --- a/backend/src/repositories/PricesRepository.ts +++ b/backend/src/repositories/PricesRepository.ts @@ -1,5 +1,6 @@ import DB from '../database'; import logger from '../logger'; +import config from '../config'; import priceUpdater from '../tasks/price-updater'; export interface ApiPrice { @@ -11,17 +12,81 @@ export interface ApiPrice { CHF: number, AUD: number, JPY: number, + BGN: number, + BRL: number, + CNY: number, + CZK: number, + DKK: number, + HKD: number, + HRK: number, + HUF: number, + IDR: number, + ILS: number, + INR: number, + ISK: number, + KRW: number, + MXN: number, + MYR: number, + NOK: number, + NZD: number, + PHP: number, + PLN: number, + RON: number, + RUB: number, + SEK: number, + SGD: number, + THB: number, + TRY: number, + ZAR: number, } -const ApiPriceFields = ` - UNIX_TIMESTAMP(time) as time, - USD, - EUR, - GBP, - CAD, - CHF, - AUD, - JPY -`; + +const ApiPriceFields = config.FIAT_PRICE.API_KEY ? + ` + UNIX_TIMESTAMP(time) as time, + USD, + EUR, + GBP, + CAD, + CHF, + AUD, + JPY, + BGN, + BRL, + CNY, + CZK, + DKK, + HKD, + HRK, + HUF, + IDR, + ILS, + INR, + ISK, + KRW, + MXN, + MYR, + NOK, + NZD, + PHP, + PLN, + RON, + RUB, + SEK, + SGD, + THB, + TRY, + ZAR + `: + ` + UNIX_TIMESTAMP(time) as time, + USD, + EUR, + GBP, + CAD, + CHF, + AUD, + JPY + `; export interface ExchangeRates { USDEUR: number, @@ -30,6 +95,32 @@ export interface ExchangeRates { USDCHF: number, USDAUD: number, USDJPY: number, + USDBGN?: number, + USDBRL?: number, + USDCNY?: number, + USDCZK?: number, + USDDKK?: number, + USDHKD?: number, + USDHRK?: number, + USDHUF?: number, + USDIDR?: number, + USDILS?: number, + USDINR?: number, + USDISK?: number, + USDKRW?: number, + USDMXN?: number, + USDMYR?: number, + USDNOK?: number, + USDNZD?: number, + USDPHP?: number, + USDPLN?: number, + USDRON?: number, + USDRUB?: number, + USDSEK?: number, + USDSGD?: number, + USDTHB?: number, + USDTRY?: number, + USDZAR?: number, } export interface Conversion { @@ -45,6 +136,32 @@ export const MAX_PRICES = { CHF: 100000000, AUD: 100000000, JPY: 10000000000, + BGN: 1000000000, + BRL: 1000000000, + CNY: 1000000000, + CZK: 10000000000, + DKK: 1000000000, + HKD: 1000000000, + HRK: 1000000000, + HUF: 10000000000, + IDR: 100000000000, + ILS: 1000000000, + INR: 10000000000, + ISK: 10000000000, + KRW: 100000000000, + MXN: 1000000000, + MYR: 1000000000, + NOK: 1000000000, + NZD: 1000000000, + PHP: 10000000000, + PLN: 1000000000, + RON: 1000000000, + RUB: 10000000000, + SEK: 1000000000, + SGD: 100000000, + THB: 10000000000, + TRY: 10000000000, + ZAR: 10000000000, }; class PricesRepository { @@ -64,17 +181,49 @@ class PricesRepository { } try { - await DB.query(` - INSERT INTO prices(time, USD, EUR, GBP, CAD, CHF, AUD, JPY) - VALUE (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ? )`, - [time, prices.USD, prices.EUR, prices.GBP, prices.CAD, prices.CHF, prices.AUD, prices.JPY] - ); + if (!config.FIAT_PRICE.API_KEY) { // Store only the 7 main currencies + await DB.query(` + INSERT INTO prices(time, USD, EUR, GBP, CAD, CHF, AUD, JPY) + VALUE (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ? )`, + [time, prices.USD, prices.EUR, prices.GBP, prices.CAD, prices.CHF, prices.AUD, prices.JPY] + ); + } else { // Store all 7 main currencies + all the currencies obtained with the external API + await DB.query(` + INSERT INTO prices(time, USD, EUR, GBP, CAD, CHF, AUD, JPY, BGN, BRL, CNY, CZK, DKK, HKD, HRK, HUF, IDR, ILS, INR, ISK, KRW, MXN, MYR, NOK, NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, TRY, ZAR) + VALUE (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ? , ?, ?, ?, ?, ?, ?, ?, ? , ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? , ? )`, + [time, prices.USD, prices.EUR, prices.GBP, prices.CAD, prices.CHF, prices.AUD, prices.JPY, prices.BGN, prices.BRL, prices.CNY, prices.CZK, prices.DKK, + prices.HKD, prices.HRK, prices.HUF, prices.IDR, prices.ILS, prices.INR, prices.ISK, prices.KRW, prices.MXN, prices.MYR, prices.NOK, prices.NZD, + prices.PHP, prices.PLN, prices.RON, prices.RUB, prices.SEK, prices.SGD, prices.THB, prices.TRY, prices.ZAR] + ); + } } catch (e) { logger.err(`Cannot save exchange rate into db. Reason: ` + (e instanceof Error ? e.message : e)); throw e; } } + public async $saveAdditionalCurrencyPrices(time: number, prices: ApiPrice, legacyCurrencies: string[]): Promise { + try { + await DB.query(` + UPDATE prices + SET BGN = ?, BRL = ?, CNY = ?, CZK = ?, DKK = ?, HKD = ?, HRK = ?, HUF = ?, IDR = ?, ILS = ?, INR = ?, ISK = ?, KRW = ?, MXN = ?, MYR = ?, NOK = ?, NZD = ?, PHP = ?, PLN = ?, RON = ?, RUB = ?, SEK = ?, SGD = ?, THB = ?, TRY = ?, ZAR = ? + WHERE UNIX_TIMESTAMP(time) = ?`, + [prices.BGN, prices.BRL, prices.CNY, prices.CZK, prices.DKK, prices.HKD, prices.HRK, prices.HUF, prices.IDR, prices.ILS, prices.INR, prices.ISK, prices.KRW, prices.MXN, prices.MYR, prices.NOK, prices.NZD, prices.PHP, prices.PLN, prices.RON, prices.RUB, prices.SEK, prices.SGD, prices.THB, prices.TRY, prices.ZAR, time] + ); + for (const currency of legacyCurrencies) { + await DB.query(` + UPDATE prices + SET ${currency} = ? + WHERE UNIX_TIMESTAMP(time) = ?`, + [prices[currency], time] + ); + } + } catch (e) { + logger.err(`Cannot update exchange rate into db. Reason: ` + (e instanceof Error ? e.message : e)); + throw e; + } + } + public async $getOldestPriceTime(): Promise { const [oldestRow] = await DB.query(` SELECT UNIX_TIMESTAMP(time) AS time @@ -118,6 +267,28 @@ class PricesRepository { return times.map(time => time.time); } + public async $getPricesTimesWithMissingFields(): Promise<{time: number, USD: number, eur_missing: boolean, gbp_missing: boolean, cad_missing: boolean, chf_missing: boolean, aud_missing: boolean, jpy_missing: boolean}[]> { + const [times] = await DB.query(` + SELECT UNIX_TIMESTAMP(time) AS time, + USD, + CASE WHEN EUR = -1 THEN TRUE ELSE FALSE END AS eur_missing, + CASE WHEN GBP = -1 THEN TRUE ELSE FALSE END AS gbp_missing, + CASE WHEN CAD = -1 THEN TRUE ELSE FALSE END AS cad_missing, + CASE WHEN CHF = -1 THEN TRUE ELSE FALSE END AS chf_missing, + CASE WHEN AUD = -1 THEN TRUE ELSE FALSE END AS aud_missing, + CASE WHEN JPY = -1 THEN TRUE ELSE FALSE END AS jpy_missing + FROM prices + WHERE USD != -1 + AND -1 IN (EUR, GBP, CAD, CHF, AUD, JPY, BGN, BRL, CNY, CZK, DKK, HKD, HRK, HUF, IDR, ILS, INR, ISK, KRW, + MXN, MYR, NOK, NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, TRY, ZAR) + ORDER BY time DESC + `); + if (!Array.isArray(times)) { + return []; + } + return times as {time: number, USD: number, eur_missing: boolean, gbp_missing: boolean, cad_missing: boolean, chf_missing: boolean, aud_missing: boolean, jpy_missing: boolean}[]; + } + public async $getPricesTimesAndId(): Promise<{time: number, id: number, USD: number}[]> { const [times] = await DB.query(` SELECT @@ -144,7 +315,7 @@ class PricesRepository { return rates[0] as ApiPrice; } - public async $getNearestHistoricalPrice(timestamp: number | undefined): Promise { + public async $getNearestHistoricalPrice(timestamp: number | undefined, currency?: string): Promise { try { const [rates] = await DB.query(` SELECT ${ApiPriceFields} @@ -158,24 +329,91 @@ class PricesRepository { throw Error(`Cannot get single historical price from the database`); } + const [latestPrices] = await DB.query(` + SELECT ${ApiPriceFields} + FROM prices + ORDER BY time DESC + LIMIT 1 + `); + if (!Array.isArray(latestPrices)) { + throw Error(`Cannot get single historical price from the database`); + } + // Compute fiat exchange rates - let latestPrice = rates[0] as ApiPrice; + let latestPrice = latestPrices[0] as ApiPrice; if (!latestPrice || latestPrice.USD === -1) { latestPrice = priceUpdater.getEmptyPricesObj(); } - const computeFx = (usd: number, other: number): number => - Math.round(Math.max(other, 0) / Math.max(usd, 1) * 100) / 100; + const computeFx = (usd: number, other: number): number => usd <= 0.05 ? 0 : Math.round(Math.max(other, 0) / usd * 100) / 100; - const exchangeRates: ExchangeRates = { - USDEUR: computeFx(latestPrice.USD, latestPrice.EUR), - USDGBP: computeFx(latestPrice.USD, latestPrice.GBP), - USDCAD: computeFx(latestPrice.USD, latestPrice.CAD), - USDCHF: computeFx(latestPrice.USD, latestPrice.CHF), - USDAUD: computeFx(latestPrice.USD, latestPrice.AUD), - USDJPY: computeFx(latestPrice.USD, latestPrice.JPY), + const exchangeRates: ExchangeRates = config.FIAT_PRICE.API_KEY ? + { + USDEUR: computeFx(latestPrice.USD, latestPrice.EUR), + USDGBP: computeFx(latestPrice.USD, latestPrice.GBP), + USDCAD: computeFx(latestPrice.USD, latestPrice.CAD), + USDCHF: computeFx(latestPrice.USD, latestPrice.CHF), + USDAUD: computeFx(latestPrice.USD, latestPrice.AUD), + USDJPY: computeFx(latestPrice.USD, latestPrice.JPY), + USDBGN: computeFx(latestPrice.USD, latestPrice.BGN), + USDBRL: computeFx(latestPrice.USD, latestPrice.BRL), + USDCNY: computeFx(latestPrice.USD, latestPrice.CNY), + USDCZK: computeFx(latestPrice.USD, latestPrice.CZK), + USDDKK: computeFx(latestPrice.USD, latestPrice.DKK), + USDHKD: computeFx(latestPrice.USD, latestPrice.HKD), + USDHRK: computeFx(latestPrice.USD, latestPrice.HRK), + USDHUF: computeFx(latestPrice.USD, latestPrice.HUF), + USDIDR: computeFx(latestPrice.USD, latestPrice.IDR), + USDILS: computeFx(latestPrice.USD, latestPrice.ILS), + USDINR: computeFx(latestPrice.USD, latestPrice.INR), + USDISK: computeFx(latestPrice.USD, latestPrice.ISK), + USDKRW: computeFx(latestPrice.USD, latestPrice.KRW), + USDMXN: computeFx(latestPrice.USD, latestPrice.MXN), + USDMYR: computeFx(latestPrice.USD, latestPrice.MYR), + USDNOK: computeFx(latestPrice.USD, latestPrice.NOK), + USDNZD: computeFx(latestPrice.USD, latestPrice.NZD), + USDPHP: computeFx(latestPrice.USD, latestPrice.PHP), + USDPLN: computeFx(latestPrice.USD, latestPrice.PLN), + USDRON: computeFx(latestPrice.USD, latestPrice.RON), + USDRUB: computeFx(latestPrice.USD, latestPrice.RUB), + USDSEK: computeFx(latestPrice.USD, latestPrice.SEK), + USDSGD: computeFx(latestPrice.USD, latestPrice.SGD), + USDTHB: computeFx(latestPrice.USD, latestPrice.THB), + USDTRY: computeFx(latestPrice.USD, latestPrice.TRY), + USDZAR: computeFx(latestPrice.USD, latestPrice.ZAR), + } : { + USDEUR: computeFx(latestPrice.USD, latestPrice.EUR), + USDGBP: computeFx(latestPrice.USD, latestPrice.GBP), + USDCAD: computeFx(latestPrice.USD, latestPrice.CAD), + USDCHF: computeFx(latestPrice.USD, latestPrice.CHF), + USDAUD: computeFx(latestPrice.USD, latestPrice.AUD), + USDJPY: computeFx(latestPrice.USD, latestPrice.JPY), }; + if (currency) { + if (!latestPrice[currency]) { + return null; + } + const filteredRates = rates.map((rate: any) => { + return { + time: rate.time, + [currency]: rate[currency], + ['USD']: rate['USD'] + }; + }); + if (filteredRates.length === 0) { // No price data before 2010-07-19: add a fake entry + filteredRates.push({ + time: 1279497600, + [currency]: 0, + ['USD']: 0 + }); + } + return { + prices: filteredRates as ApiPrice[], + exchangeRates: exchangeRates + }; + } + return { prices: rates as ApiPrice[], exchangeRates: exchangeRates @@ -186,7 +424,7 @@ class PricesRepository { } } - public async $getHistoricalPrices(): Promise { + public async $getHistoricalPrices(currency?: string): Promise { try { const [rates] = await DB.query(` SELECT ${ApiPriceFields} @@ -203,18 +441,69 @@ class PricesRepository { latestPrice = priceUpdater.getEmptyPricesObj(); } - const computeFx = (usd: number, other: number): number => - Math.round(Math.max(other, 0) / Math.max(usd, 1) * 100) / 100; + const computeFx = (usd: number, other: number): number => + usd <= 0 ? 0 : Math.round(Math.max(other, 0) / usd * 100) / 100; - const exchangeRates: ExchangeRates = { - USDEUR: computeFx(latestPrice.USD, latestPrice.EUR), - USDGBP: computeFx(latestPrice.USD, latestPrice.GBP), - USDCAD: computeFx(latestPrice.USD, latestPrice.CAD), - USDCHF: computeFx(latestPrice.USD, latestPrice.CHF), - USDAUD: computeFx(latestPrice.USD, latestPrice.AUD), - USDJPY: computeFx(latestPrice.USD, latestPrice.JPY), + const exchangeRates: ExchangeRates = config.FIAT_PRICE.API_KEY ? + { + USDEUR: computeFx(latestPrice.USD, latestPrice.EUR), + USDGBP: computeFx(latestPrice.USD, latestPrice.GBP), + USDCAD: computeFx(latestPrice.USD, latestPrice.CAD), + USDCHF: computeFx(latestPrice.USD, latestPrice.CHF), + USDAUD: computeFx(latestPrice.USD, latestPrice.AUD), + USDJPY: computeFx(latestPrice.USD, latestPrice.JPY), + USDBGN: computeFx(latestPrice.USD, latestPrice.BGN), + USDBRL: computeFx(latestPrice.USD, latestPrice.BRL), + USDCNY: computeFx(latestPrice.USD, latestPrice.CNY), + USDCZK: computeFx(latestPrice.USD, latestPrice.CZK), + USDDKK: computeFx(latestPrice.USD, latestPrice.DKK), + USDHKD: computeFx(latestPrice.USD, latestPrice.HKD), + USDHRK: computeFx(latestPrice.USD, latestPrice.HRK), + USDHUF: computeFx(latestPrice.USD, latestPrice.HUF), + USDIDR: computeFx(latestPrice.USD, latestPrice.IDR), + USDILS: computeFx(latestPrice.USD, latestPrice.ILS), + USDINR: computeFx(latestPrice.USD, latestPrice.INR), + USDISK: computeFx(latestPrice.USD, latestPrice.ISK), + USDKRW: computeFx(latestPrice.USD, latestPrice.KRW), + USDMXN: computeFx(latestPrice.USD, latestPrice.MXN), + USDMYR: computeFx(latestPrice.USD, latestPrice.MYR), + USDNOK: computeFx(latestPrice.USD, latestPrice.NOK), + USDNZD: computeFx(latestPrice.USD, latestPrice.NZD), + USDPHP: computeFx(latestPrice.USD, latestPrice.PHP), + USDPLN: computeFx(latestPrice.USD, latestPrice.PLN), + USDRON: computeFx(latestPrice.USD, latestPrice.RON), + USDRUB: computeFx(latestPrice.USD, latestPrice.RUB), + USDSEK: computeFx(latestPrice.USD, latestPrice.SEK), + USDSGD: computeFx(latestPrice.USD, latestPrice.SGD), + USDTHB: computeFx(latestPrice.USD, latestPrice.THB), + USDTRY: computeFx(latestPrice.USD, latestPrice.TRY), + USDZAR: computeFx(latestPrice.USD, latestPrice.ZAR), + } : { + USDEUR: computeFx(latestPrice.USD, latestPrice.EUR), + USDGBP: computeFx(latestPrice.USD, latestPrice.GBP), + USDCAD: computeFx(latestPrice.USD, latestPrice.CAD), + USDCHF: computeFx(latestPrice.USD, latestPrice.CHF), + USDAUD: computeFx(latestPrice.USD, latestPrice.AUD), + USDJPY: computeFx(latestPrice.USD, latestPrice.JPY), }; + if (currency) { + if (!latestPrice[currency]) { + return null; + } + const filteredRates = rates.map((rate: any) => { + return { + time: rate.time, + [currency]: rate[currency], + ['USD']: rate['USD'] + }; + }); + return { + prices: filteredRates as ApiPrice[], + exchangeRates: exchangeRates + }; + } + return { prices: rates as ApiPrice[], exchangeRates: exchangeRates diff --git a/backend/src/rpc-api/commands.ts b/backend/src/rpc-api/commands.ts index ecfb2ed7c..85675230b 100644 --- a/backend/src/rpc-api/commands.ts +++ b/backend/src/rpc-api/commands.ts @@ -11,6 +11,7 @@ module.exports = { encryptWallet: 'encryptwallet', estimateFee: 'estimatefee', // bitcoind v0.10.0x estimatePriority: 'estimatepriority', // bitcoind v0.10.0+ + estimateSmartFee: 'estimatesmartfee', generate: 'generate', // bitcoind v0.11.0+ getAccount: 'getaccount', getAccountAddress: 'getaccountaddress', diff --git a/backend/src/tasks/price-feeds/free-currency-api.ts b/backend/src/tasks/price-feeds/free-currency-api.ts new file mode 100644 index 000000000..8d6175b95 --- /dev/null +++ b/backend/src/tasks/price-feeds/free-currency-api.ts @@ -0,0 +1,73 @@ +import { query } from '../../utils/axios-query'; +import { ConversionFeed, ConversionRates } from '../price-updater'; + +const emptyRates = { + AUD: -1, + BGN: -1, + BRL: -1, + CAD: -1, + CHF: -1, + CNY: -1, + CZK: -1, + DKK: -1, + EUR: -1, + GBP: -1, + HKD: -1, + HRK: -1, + HUF: -1, + IDR: -1, + ILS: -1, + INR: -1, + ISK: -1, + JPY: -1, + KRW: -1, + MXN: -1, + MYR: -1, + NOK: -1, + NZD: -1, + PHP: -1, + PLN: -1, + RON: -1, + RUB: -1, + SEK: -1, + SGD: -1, + THB: -1, + TRY: -1, + USD: -1, + ZAR: -1, +}; + +class FreeCurrencyApi implements ConversionFeed { + private API_KEY: string; + + constructor(apiKey: string) { + this.API_KEY = apiKey; + } + + public async $getQuota(): Promise { + const response = await query(`https://api.freecurrencyapi.com/v1/status?apikey=${this.API_KEY}`); + if (response && response['quotas']) { + return response['quotas']; + } + return null; + } + + public async $fetchLatestConversionRates(): Promise { + const response = await query(`https://api.freecurrencyapi.com/v1/latest?apikey=${this.API_KEY}`); + if (response && response['data']) { + return response['data']; + } + return emptyRates; + } + + public async $fetchConversionRates(date: string): Promise { + const response = await query(`https://api.freecurrencyapi.com/v1/historical?date=${date}&apikey=${this.API_KEY}`); + if (response && response['data'] && response['data'][date]) { + return response['data'][date]; + } + return emptyRates; + } + +} + +export default FreeCurrencyApi; diff --git a/backend/src/tasks/price-updater.ts b/backend/src/tasks/price-updater.ts index 0d5ca5958..7ed4cb178 100644 --- a/backend/src/tasks/price-updater.ts +++ b/backend/src/tasks/price-updater.ts @@ -8,6 +8,7 @@ import BitflyerApi from './price-feeds/bitflyer-api'; import CoinbaseApi from './price-feeds/coinbase-api'; import GeminiApi from './price-feeds/gemini-api'; import KrakenApi from './price-feeds/kraken-api'; +import FreeCurrencyApi from './price-feeds/free-currency-api'; export interface PriceFeed { name: string; @@ -23,6 +24,16 @@ export interface PriceHistory { [timestamp: number]: ApiPrice; } +export interface ConversionFeed { + $getQuota(): Promise; + $fetchLatestConversionRates(): Promise; + $fetchConversionRates(date: string): Promise; +} + +export interface ConversionRates { + [currency: string]: number +} + function getMedian(arr: number[]): number { const sortedArr = arr.slice().sort((a, b) => a - b); const mid = Math.floor(sortedArr.length / 2); @@ -33,6 +44,9 @@ function getMedian(arr: number[]): number { class PriceUpdater { public historyInserted = false; + private additionalCurrenciesHistoryInserted = false; + private additionalCurrenciesHistoryRunning = false; + private lastFailedHistoricalRun = 0; private timeBetweenUpdatesMs = 360_0000 / config.MEMPOOL.PRICE_UPDATES_PER_HOUR; private cyclePosition = -1; private firstRun = true; @@ -42,6 +56,10 @@ class PriceUpdater { private feeds: PriceFeed[] = []; private currencies: string[] = ['USD', 'EUR', 'GBP', 'CAD', 'CHF', 'AUD', 'JPY']; private latestPrices: ApiPrice; + private currencyConversionFeed: ConversionFeed | undefined; + private newCurrencies: string[] = ['BGN', 'BRL', 'CNY', 'CZK', 'DKK', 'HKD', 'HRK', 'HUF', 'IDR', 'ILS', 'INR', 'ISK', 'KRW', 'MXN', 'MYR', 'NOK', 'NZD', 'PHP', 'PLN', 'RON', 'RUB', 'SEK', 'SGD', 'THB', 'TRY', 'ZAR']; + private lastTimeConversionsRatesFetched: number = 0; + private latestConversionsRatesFromFeed: ConversionRates = {}; private ratesChangedCallback: ((rates: ApiPrice) => void) | undefined; constructor() { @@ -53,6 +71,7 @@ class PriceUpdater { this.feeds.push(new BitfinexApi()); this.feeds.push(new GeminiApi()); + this.currencyConversionFeed = new FreeCurrencyApi(config.FIAT_PRICE.API_KEY); this.setCyclePosition(); } @@ -70,6 +89,32 @@ class PriceUpdater { CHF: -1, AUD: -1, JPY: -1, + BGN: -1, + BRL: -1, + CNY: -1, + CZK: -1, + DKK: -1, + HKD: -1, + HRK: -1, + HUF: -1, + IDR: -1, + ILS: -1, + INR: -1, + ISK: -1, + KRW: -1, + MXN: -1, + MYR: -1, + NOK: -1, + NZD: -1, + PHP: -1, + PLN: -1, + RON: -1, + RUB: -1, + SEK: -1, + SGD: -1, + THB: -1, + TRY: -1, + ZAR: -1, }; } @@ -99,6 +144,23 @@ class PriceUpdater { if ((Math.round(new Date().getTime() / 1000) - this.lastHistoricalRun) > 3600 * 24) { // Once a day, look for missing prices (could happen due to network connectivity issues) this.historyInserted = false; + this.additionalCurrenciesHistoryInserted = false; + } + + if (this.lastFailedHistoricalRun > 0 && (Math.round(new Date().getTime() / 1000) - this.lastFailedHistoricalRun) > 60) { + // If the last attempt to insert missing prices failed, we try again after 60 seconds + this.additionalCurrenciesHistoryInserted = false; + } + + if (config.FIAT_PRICE.API_KEY && this.currencyConversionFeed && (Math.round(new Date().getTime() / 1000) - this.lastTimeConversionsRatesFetched) > 3600 * 24) { + // Once a day, fetch conversion rates from api: we don't need more granularity for fiat currencies and have a limited number of requests + try { + this.latestConversionsRatesFromFeed = await this.currencyConversionFeed.$fetchLatestConversionRates(); + this.lastTimeConversionsRatesFetched = Math.round(new Date().getTime() / 1000); + logger.debug(`Fetched currencies conversion rates from external API: ${JSON.stringify(this.latestConversionsRatesFromFeed)}`); + } catch (e) { + logger.err(`Cannot fetch conversion rates from the API. Reason: ${(e instanceof Error ? e.message : e)}`); + } } try { @@ -106,6 +168,10 @@ class PriceUpdater { if (this.historyInserted === false && config.DATABASE.ENABLED === true) { await this.$insertHistoricalPrices(); } + if (this.additionalCurrenciesHistoryInserted === false && config.DATABASE.ENABLED === true && config.FIAT_PRICE.API_KEY && !this.additionalCurrenciesHistoryRunning) { + await this.$insertMissingAdditionalPrices(); + } + } catch (e: any) { logger.err(`Cannot save BTC prices in db. Reason: ${e instanceof Error ? e.message : e}`, logger.tags.mining); } @@ -185,6 +251,14 @@ class PriceUpdater { } } + if (config.FIAT_PRICE.API_KEY && this.latestPrices.USD > 0 && Object.keys(this.latestConversionsRatesFromFeed).length > 0) { + for (const conversionCurrency of this.newCurrencies) { + if (this.latestConversionsRatesFromFeed[conversionCurrency] > 0 && this.latestPrices.USD * this.latestConversionsRatesFromFeed[conversionCurrency] < MAX_PRICES[conversionCurrency]) { + this.latestPrices[conversionCurrency] = Math.round(this.latestPrices.USD * this.latestConversionsRatesFromFeed[conversionCurrency]); + } + } + } + if (config.DATABASE.ENABLED === true && this.cyclePosition === 0) { // Save everything in db try { @@ -253,7 +327,7 @@ class PriceUpdater { await this.$insertMissingRecentPrices('hour'); this.historyInserted = true; - this.lastHistoricalRun = new Date().getTime(); + this.lastHistoricalRun = Math.round(new Date().getTime() / 1000); } /** @@ -320,6 +394,83 @@ class PriceUpdater { logger.debug(`Inserted ${totalInserted} ${type === 'day' ? 'dai' : 'hour'}ly historical prices into the db`, logger.tags.mining); } } + + /** + * Find missing prices for additional currencies and insert them in the database + * We calculate the additional prices from the USD price and the conversion rates + */ + private async $insertMissingAdditionalPrices(): Promise { + this.lastFailedHistoricalRun = 0; + const priceTimesToFill = await PricesRepository.$getPricesTimesWithMissingFields(); + if (priceTimesToFill.length === 0) { + return; + } + try { + const remainingQuota = await this.currencyConversionFeed?.$getQuota(); + if (remainingQuota['month']['remaining'] < 500) { // We need some calls left for the daily updates + logger.debug(`Not enough currency API credit to insert missing prices in ${priceTimesToFill.length} rows (${remainingQuota['month']['remaining']} calls left).`, logger.tags.mining); + this.additionalCurrenciesHistoryInserted = true; // Do not try again until next day + return; + } + } catch (e) { + logger.err(`Cannot fetch currency API credit, insertion of missing prices aborted. Reason: ${(e instanceof Error ? e.message : e)}`); + return; + } + + this.additionalCurrenciesHistoryRunning = true; + logger.debug(`Fetching missing conversion rates from external API to fill ${priceTimesToFill.length} rows`, logger.tags.mining); + + let conversionRates: { [timestamp: number]: ConversionRates } = {}; + let totalInserted = 0; + + for (let i = 0; i < priceTimesToFill.length; i++) { + const priceTime = priceTimesToFill[i]; + const missingLegacyCurrencies = this.getMissingLegacyCurrencies(priceTime); // In the case a legacy currency (EUR, GBP, CAD, CHF, AUD, JPY) + const year = new Date(priceTime.time * 1000).getFullYear(); // is missing, we use the same process as for the new currencies + const month = new Date(priceTime.time * 1000).getMonth(); + const yearMonthTimestamp = new Date(year, month, 1).getTime() / 1000; + if (conversionRates[yearMonthTimestamp] === undefined) { + conversionRates[yearMonthTimestamp] = await this.currencyConversionFeed?.$fetchConversionRates(`${year}-${month + 1 < 10 ? `0${month + 1}` : `${month + 1}`}-01`) || { USD: -1 }; + if (conversionRates[yearMonthTimestamp]['USD'] < 0) { + logger.err(`Cannot fetch conversion rates from the API for ${year}-${month + 1 < 10 ? `0${month + 1}` : `${month + 1}`}-01. Aborting insertion of missing prices.`, logger.tags.mining); + this.lastFailedHistoricalRun = Math.round(new Date().getTime() / 1000); + break; + } + } + + const prices: ApiPrice = this.getEmptyPricesObj(); + + let willInsert = false; + for (const conversionCurrency of this.newCurrencies.concat(missingLegacyCurrencies)) { + if (conversionRates[yearMonthTimestamp][conversionCurrency] > 0 && priceTime.USD * conversionRates[yearMonthTimestamp][conversionCurrency] < MAX_PRICES[conversionCurrency]) { + prices[conversionCurrency] = year >= 2013 ? Math.round(priceTime.USD * conversionRates[yearMonthTimestamp][conversionCurrency]) : Math.round(priceTime.USD * conversionRates[yearMonthTimestamp][conversionCurrency] * 100) / 100; + willInsert = true; + } else { + prices[conversionCurrency] = 0; + } + } + + if (willInsert) { + await PricesRepository.$saveAdditionalCurrencyPrices(priceTime.time, prices, missingLegacyCurrencies); + ++totalInserted; + } + } + + logger.debug(`Inserted ${totalInserted} missing additional currency prices into the db`, logger.tags.mining); + this.additionalCurrenciesHistoryInserted = true; + this.additionalCurrenciesHistoryRunning = false; + } + + // Helper function to get legacy missing currencies in a row (EUR, GBP, CAD, CHF, AUD, JPY) + private getMissingLegacyCurrencies(priceTime: any): string[] { + const missingCurrencies: string[] = []; + ['eur', 'gbp', 'cad', 'chf', 'aud', 'jpy'].forEach(currency => { + if (priceTime[`${currency}_missing`]) { + missingCurrencies.push(currency.toUpperCase()); + } + }); + return missingCurrencies; + } } export default new PriceUpdater(); diff --git a/backend/src/utils/bitcoin-script.ts b/backend/src/utils/bitcoin-script.ts new file mode 100644 index 000000000..3414e8269 --- /dev/null +++ b/backend/src/utils/bitcoin-script.ts @@ -0,0 +1,203 @@ +const opcodes = { + OP_FALSE: 0, + OP_0: 0, + OP_PUSHDATA1: 76, + OP_PUSHDATA2: 77, + OP_PUSHDATA4: 78, + OP_1NEGATE: 79, + OP_PUSHNUM_NEG1: 79, + OP_RESERVED: 80, + OP_TRUE: 81, + OP_1: 81, + OP_2: 82, + OP_3: 83, + OP_4: 84, + OP_5: 85, + OP_6: 86, + OP_7: 87, + OP_8: 88, + OP_9: 89, + OP_10: 90, + OP_11: 91, + OP_12: 92, + OP_13: 93, + OP_14: 94, + OP_15: 95, + OP_16: 96, + OP_PUSHNUM_1: 81, + OP_PUSHNUM_2: 82, + OP_PUSHNUM_3: 83, + OP_PUSHNUM_4: 84, + OP_PUSHNUM_5: 85, + OP_PUSHNUM_6: 86, + OP_PUSHNUM_7: 87, + OP_PUSHNUM_8: 88, + OP_PUSHNUM_9: 89, + OP_PUSHNUM_10: 90, + OP_PUSHNUM_11: 91, + OP_PUSHNUM_12: 92, + OP_PUSHNUM_13: 93, + OP_PUSHNUM_14: 94, + OP_PUSHNUM_15: 95, + OP_PUSHNUM_16: 96, + OP_NOP: 97, + OP_VER: 98, + OP_IF: 99, + OP_NOTIF: 100, + OP_VERIF: 101, + OP_VERNOTIF: 102, + OP_ELSE: 103, + OP_ENDIF: 104, + OP_VERIFY: 105, + OP_RETURN: 106, + OP_TOALTSTACK: 107, + OP_FROMALTSTACK: 108, + OP_2DROP: 109, + OP_2DUP: 110, + OP_3DUP: 111, + OP_2OVER: 112, + OP_2ROT: 113, + OP_2SWAP: 114, + OP_IFDUP: 115, + OP_DEPTH: 116, + OP_DROP: 117, + OP_DUP: 118, + OP_NIP: 119, + OP_OVER: 120, + OP_PICK: 121, + OP_ROLL: 122, + OP_ROT: 123, + OP_SWAP: 124, + OP_TUCK: 125, + OP_CAT: 126, + OP_SUBSTR: 127, + OP_LEFT: 128, + OP_RIGHT: 129, + OP_SIZE: 130, + OP_INVERT: 131, + OP_AND: 132, + OP_OR: 133, + OP_XOR: 134, + OP_EQUAL: 135, + OP_EQUALVERIFY: 136, + OP_RESERVED1: 137, + OP_RESERVED2: 138, + OP_1ADD: 139, + OP_1SUB: 140, + OP_2MUL: 141, + OP_2DIV: 142, + OP_NEGATE: 143, + OP_ABS: 144, + OP_NOT: 145, + OP_0NOTEQUAL: 146, + OP_ADD: 147, + OP_SUB: 148, + OP_MUL: 149, + OP_DIV: 150, + OP_MOD: 151, + OP_LSHIFT: 152, + OP_RSHIFT: 153, + OP_BOOLAND: 154, + OP_BOOLOR: 155, + OP_NUMEQUAL: 156, + OP_NUMEQUALVERIFY: 157, + OP_NUMNOTEQUAL: 158, + OP_LESSTHAN: 159, + OP_GREATERTHAN: 160, + OP_LESSTHANOREQUAL: 161, + OP_GREATERTHANOREQUAL: 162, + OP_MIN: 163, + OP_MAX: 164, + OP_WITHIN: 165, + OP_RIPEMD160: 166, + OP_SHA1: 167, + OP_SHA256: 168, + OP_HASH160: 169, + OP_HASH256: 170, + OP_CODESEPARATOR: 171, + OP_CHECKSIG: 172, + OP_CHECKSIGVERIFY: 173, + OP_CHECKMULTISIG: 174, + OP_CHECKMULTISIGVERIFY: 175, + OP_NOP1: 176, + OP_NOP2: 177, + OP_CHECKLOCKTIMEVERIFY: 177, + OP_CLTV: 177, + OP_NOP3: 178, + OP_CHECKSEQUENCEVERIFY: 178, + OP_CSV: 178, + OP_NOP4: 179, + OP_NOP5: 180, + OP_NOP6: 181, + OP_NOP7: 182, + OP_NOP8: 183, + OP_NOP9: 184, + OP_NOP10: 185, + OP_CHECKSIGADD: 186, + OP_PUBKEYHASH: 253, + OP_PUBKEY: 254, + OP_INVALIDOPCODE: 255, +}; +// add unused opcodes +for (let i = 187; i <= 255; i++) { + opcodes[`OP_RETURN_${i}`] = i; +} + +export { opcodes }; + +/** extracts m and n from a multisig script (asm), returns nothing if it is not a multisig script */ +export function parseMultisigScript(script: string): void | { m: number, n: number } { + if (!script) { + return; + } + const ops = script.split(' '); + if (ops.length < 3 || ops.pop() !== 'OP_CHECKMULTISIG') { + return; + } + const opN = ops.pop(); + if (!opN) { + return; + } + if (!opN.startsWith('OP_PUSHNUM_')) { + return; + } + const n = parseInt(opN.match(/[0-9]+/)?.[0] || '', 10); + if (ops.length < n * 2 + 1) { + return; + } + // pop n public keys + for (let i = 0; i < n; i++) { + if (!/^0((2|3)\w{64}|4\w{128})$/.test(ops.pop() || '')) { + return; + } + if (!/^OP_PUSHBYTES_(33|65)$/.test(ops.pop() || '')) { + return; + } + } + const opM = ops.pop(); + if (!opM) { + return; + } + if (!opM.startsWith('OP_PUSHNUM_')) { + return; + } + const m = parseInt(opM.match(/[0-9]+/)?.[0] || '', 10); + + if (ops.length) { + return; + } + + return { m, n }; +} + +export function getVarIntLength(n: number): number { + if (n < 0xfd) { + return 1; + } else if (n <= 0xffff) { + return 3; + } else if (n <= 0xffffffff) { + return 5; + } else { + return 9; + } +} \ No newline at end of file diff --git a/backend/src/utils/secp256k1.ts b/backend/src/utils/secp256k1.ts index cc731f17d..9e0f6dc3b 100644 --- a/backend/src/utils/secp256k1.ts +++ b/backend/src/utils/secp256k1.ts @@ -31,6 +31,9 @@ const curveP = BigInt(`0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF * @returns {boolean} true if the point is on the SECP256K1 curve */ export function isPoint(pointHex: string): boolean { + if (!pointHex?.length) { + return false; + } if ( !( // is uncompressed diff --git a/contributors/afahrer.txt b/contributors/afahrer.txt new file mode 100644 index 000000000..1ce6dc8e5 --- /dev/null +++ b/contributors/afahrer.txt @@ -0,0 +1,3 @@ +I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of January 23, 2024. + +Signed: afahrer \ No newline at end of file diff --git a/contributors/jamesblacklock.txt b/contributors/jamesblacklock.txt new file mode 100644 index 000000000..11591f451 --- /dev/null +++ b/contributors/jamesblacklock.txt @@ -0,0 +1,3 @@ +I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of December 20, 2023. + +Signed: jamesblacklock diff --git a/contributors/natsee.txt b/contributors/natsoni.txt similarity index 90% rename from contributors/natsee.txt rename to contributors/natsoni.txt index c391ce823..ac1007ecf 100644 --- a/contributors/natsee.txt +++ b/contributors/natsoni.txt @@ -1,3 +1,3 @@ I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of November 16, 2023. -Signed: natsee +Signed: natsoni diff --git a/contributors/russeree.txt b/contributors/russeree.txt new file mode 100644 index 000000000..0aaef47e5 --- /dev/null +++ b/contributors/russeree.txt @@ -0,0 +1,3 @@ +I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of March 8, 2024. + +Signed: PortlandHODL diff --git a/docker/README.md b/docker/README.md index 444324af8..32ed6fcde 100644 --- a/docker/README.md +++ b/docker/README.md @@ -109,8 +109,6 @@ Below we list all settings from `mempool-config.json` and the corresponding over "AUTOMATIC_BLOCK_REINDEXING": false, "POOLS_JSON_URL": "https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json", "POOLS_JSON_TREE_URL": "https://api.github.com/repos/mempool/mining-pools/git/trees/master", - "ADVANCED_GBT_AUDIT": false, - "ADVANCED_GBT_MEMPOOL": false, "CPFP_INDEXING": false, "MAX_BLOCKS_BULK_QUERY": 0, "DISK_CACHE_BLOCK_INTERVAL": 6, @@ -142,8 +140,6 @@ Corresponding `docker-compose.yml` overrides: MEMPOOL_AUTOMATIC_BLOCK_REINDEXING: "" MEMPOOL_POOLS_JSON_URL: "" MEMPOOL_POOLS_JSON_TREE_URL: "" - MEMPOOL_ADVANCED_GBT_AUDIT: "" - MEMPOOL_ADVANCED_GBT_MEMPOOL: "" MEMPOOL_CPFP_INDEXING: "" MEMPOOL_MAX_BLOCKS_BULK_QUERY: "" MEMPOOL_DISK_CACHE_BLOCK_INTERVAL: "" @@ -151,8 +147,6 @@ Corresponding `docker-compose.yml` overrides: ... ``` -`ADVANCED_GBT_AUDIT` AND `ADVANCED_GBT_MEMPOOL` enable a more accurate (but slower) block prediction algorithm for the block audit feature and the projected mempool-blocks respectively. - `CPFP_INDEXING` enables indexing CPFP (Child Pays For Parent) information for the last `INDEXING_BLOCKS_AMOUNT` blocks.
@@ -329,25 +323,6 @@ Corresponding `docker-compose.yml` overrides:
-`mempool-config.json`: -```json - "BISQ": { - "ENABLED": false, - "DATA_PATH": "/bisq/statsnode-data/btc_mainnet/db" - } -``` - -Corresponding `docker-compose.yml` overrides: -```yaml - api: - environment: - BISQ_ENABLED: "" - BISQ_DATA_PATH: "" - ... -``` - -
- `mempool-config.json`: ```json "SOCKS5PROXY": { diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index 96b1a2d5b..7ab3d605c 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.8.0-buster-slim AS builder +FROM node:20.12.0-buster-slim AS builder ARG commitHash ENV MEMPOOL_COMMIT_HASH=${commitHash} @@ -17,7 +17,7 @@ ENV PATH="/root/.cargo/bin:$PATH" RUN npm install --omit=dev --omit=optional RUN npm run package -FROM node:20.8.0-buster-slim +FROM node:20.12.0-buster-slim WORKDIR /backend diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index c68e37baa..ea3fb56a1 100644 --- a/docker/backend/mempool-config.json +++ b/docker/backend/mempool-config.json @@ -3,6 +3,7 @@ "NETWORK": "__MEMPOOL_NETWORK__", "BACKEND": "__MEMPOOL_BACKEND__", "ENABLED": __MEMPOOL_ENABLED__, + "OFFICIAL": __MEMPOOL_OFFICIAL__, "HTTP_PORT": __MEMPOOL_HTTP_PORT__, "SPAWN_CLUSTER_PROCS": __MEMPOOL_SPAWN_CLUSTER_PROCS__, "API_URL_PREFIX": "__MEMPOOL_API_URL_PREFIX__", @@ -22,11 +23,11 @@ "STDOUT_LOG_MIN_PRIORITY": "__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__", "INDEXING_BLOCKS_AMOUNT": __MEMPOOL_INDEXING_BLOCKS_AMOUNT__, "BLOCKS_SUMMARIES_INDEXING": __MEMPOOL_BLOCKS_SUMMARIES_INDEXING__, + "GOGGLES_INDEXING": __MEMPOOL_GOGGLES_INDEXING__, "AUTOMATIC_BLOCK_REINDEXING": __MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__, "AUDIT": __MEMPOOL_AUDIT__, - "ADVANCED_GBT_AUDIT": __MEMPOOL_ADVANCED_GBT_AUDIT__, - "ADVANCED_GBT_MEMPOOL": __MEMPOOL_ADVANCED_GBT_MEMPOOL__, "RUST_GBT": __MEMPOOL_RUST_GBT__, + "LIMIT_GBT": __MEMPOOL_LIMIT_GBT__, "CPFP_INDEXING": __MEMPOOL_CPFP_INDEXING__, "MAX_BLOCKS_BULK_QUERY": __MEMPOOL_MAX_BLOCKS_BULK_QUERY__, "DISK_CACHE_BLOCK_INTERVAL": __MEMPOOL_DISK_CACHE_BLOCK_INTERVAL__, @@ -34,7 +35,7 @@ "ALLOW_UNREACHABLE": __MEMPOOL_ALLOW_UNREACHABLE__, "POOLS_JSON_TREE_URL": "__MEMPOOL_POOLS_JSON_TREE_URL__", "POOLS_JSON_URL": "__MEMPOOL_POOLS_JSON_URL__", - "PRICE_UPDATES_PER_HOUR": __MEMPOOL_PRICE_UPDATES_PER_HOUR__ + "PRICE_UPDATES_PER_HOUR": __MEMPOOL_PRICE_UPDATES_PER_HOUR__, "MAX_TRACKED_ADDRESSES": __MEMPOOL_MAX_TRACKED_ADDRESSES__ }, "CORE_RPC": { @@ -78,7 +79,8 @@ "USERNAME": "__DATABASE_USERNAME__", "PASSWORD": "__DATABASE_PASSWORD__", "TIMEOUT": __DATABASE_TIMEOUT__, - "PID_DIR": "__DATABASE_PID_DIR__" + "PID_DIR": "__DATABASE_PID_DIR__", + "POOL_SIZE": __DATABASE_POOL_SIZE__ }, "SYSLOG": { "ENABLED": __SYSLOG_ENABLED__, @@ -91,10 +93,6 @@ "ENABLED": __STATISTICS_ENABLED__, "TX_PER_SECOND_SAMPLE_PERIOD": __STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD__ }, - "BISQ": { - "ENABLED": __BISQ_ENABLED__, - "DATA_PATH": "__BISQ_DATA_PATH__" - }, "LIGHTNING": { "ENABLED": __LIGHTNING_ENABLED__, "BACKEND": "__LIGHTNING_BACKEND__", @@ -126,9 +124,7 @@ "MEMPOOL_API": "__EXTERNAL_DATA_SERVER_MEMPOOL_API__", "MEMPOOL_ONION": "__EXTERNAL_DATA_SERVER_MEMPOOL_ONION__", "LIQUID_API": "__EXTERNAL_DATA_SERVER_LIQUID_API__", - "LIQUID_ONION": "__EXTERNAL_DATA_SERVER_LIQUID_ONION__", - "BISQ_URL": "__EXTERNAL_DATA_SERVER_BISQ_URL__", - "BISQ_ONION": "__EXTERNAL_DATA_SERVER_BISQ_ONION__" + "LIQUID_ONION": "__EXTERNAL_DATA_SERVER_LIQUID_ONION__" }, "MAXMIND": { "ENABLED": __MAXMIND_ENABLED__, @@ -150,5 +146,9 @@ "ENABLED": __REDIS_ENABLED__, "UNIX_SOCKET_PATH": "__REDIS_UNIX_SOCKET_PATH__", "BATCH_QUERY_BASE_SIZE": __REDIS_BATCH_QUERY_BASE_SIZE__ + }, + "FIAT_PRICE": { + "ENABLED": __FIAT_PRICE_ENABLED__, + "API_KEY": "__FIAT_PRICE_API_KEY__" } } diff --git a/docker/backend/start.sh b/docker/backend/start.sh index d73ea83fb..401199ede 100755 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -4,6 +4,7 @@ __MEMPOOL_NETWORK__=${MEMPOOL_NETWORK:=mainnet} __MEMPOOL_BACKEND__=${MEMPOOL_BACKEND:=electrum} __MEMPOOL_ENABLED__=${MEMPOOL_ENABLED:=true} +__MEMPOOL_OFFICIAL__=${MEMPOOL_OFFICIAL:=false} __MEMPOOL_HTTP_PORT__=${BACKEND_HTTP_PORT:=8999} __MEMPOOL_SPAWN_CLUSTER_PROCS__=${MEMPOOL_SPAWN_CLUSTER_PROCS:=0} __MEMPOOL_API_URL_PREFIX__=${MEMPOOL_API_URL_PREFIX:=/api/v1/} @@ -17,6 +18,7 @@ __MEMPOOL_INITIAL_BLOCKS_AMOUNT__=${MEMPOOL_INITIAL_BLOCKS_AMOUNT:=8} __MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__=${MEMPOOL_MEMPOOL_BLOCKS_AMOUNT:=8} __MEMPOOL_INDEXING_BLOCKS_AMOUNT__=${MEMPOOL_INDEXING_BLOCKS_AMOUNT:=11000} __MEMPOOL_BLOCKS_SUMMARIES_INDEXING__=${MEMPOOL_BLOCKS_SUMMARIES_INDEXING:=false} +__MEMPOOL_GOGGLES_INDEXING__=${MEMPOOL_GOGGLES_INDEXING:=false} __MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__=${MEMPOOL_USE_SECOND_NODE_FOR_MINFEE:=false} __MEMPOOL_EXTERNAL_ASSETS__=${MEMPOOL_EXTERNAL_ASSETS:=[]} __MEMPOOL_EXTERNAL_MAX_RETRY__=${MEMPOOL_EXTERNAL_MAX_RETRY:=1} @@ -27,9 +29,8 @@ __MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__=${MEMPOOL_AUTOMATIC_BLOCK_REINDEXING:=fal __MEMPOOL_POOLS_JSON_URL__=${MEMPOOL_POOLS_JSON_URL:=https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json} __MEMPOOL_POOLS_JSON_TREE_URL__=${MEMPOOL_POOLS_JSON_TREE_URL:=https://api.github.com/repos/mempool/mining-pools/git/trees/master} __MEMPOOL_AUDIT__=${MEMPOOL_AUDIT:=false} -__MEMPOOL_ADVANCED_GBT_AUDIT__=${MEMPOOL_ADVANCED_GBT_AUDIT:=false} -__MEMPOOL_ADVANCED_GBT_MEMPOOL__=${MEMPOOL_ADVANCED_GBT_MEMPOOL:=false} __MEMPOOL_RUST_GBT__=${MEMPOOL_RUST_GBT:=false} +__MEMPOOL_LIMIT_GBT__=${MEMPOOL_LIMIT_GBT:=false} __MEMPOOL_CPFP_INDEXING__=${MEMPOOL_CPFP_INDEXING:=false} __MEMPOOL_MAX_BLOCKS_BULK_QUERY__=${MEMPOOL_MAX_BLOCKS_BULK_QUERY:=0} __MEMPOOL_DISK_CACHE_BLOCK_INTERVAL__=${MEMPOOL_DISK_CACHE_BLOCK_INTERVAL:=6} @@ -54,7 +55,7 @@ __ELECTRUM_TLS_ENABLED__=${ELECTRUM_TLS_ENABLED:=false} # ESPLORA __ESPLORA_REST_API_URL__=${ESPLORA_REST_API_URL:=http://127.0.0.1:3000} -__ESPLORA_UNIX_SOCKET_PATH__=${ESPLORA_UNIX_SOCKET_PATH:="null"} +__ESPLORA_UNIX_SOCKET_PATH__=${ESPLORA_UNIX_SOCKET_PATH:=""} __ESPLORA_BATCH_QUERY_BASE_SIZE__=${ESPLORA_BATCH_QUERY_BASE_SIZE:=1000} __ESPLORA_RETRY_UNIX_SOCKET_AFTER__=${ESPLORA_RETRY_UNIX_SOCKET_AFTER:=30000} __ESPLORA_REQUEST_TIMEOUT__=${ESPLORA_REQUEST_TIMEOUT:=5000} @@ -80,6 +81,7 @@ __DATABASE_USERNAME__=${DATABASE_USERNAME:=mempool} __DATABASE_PASSWORD__=${DATABASE_PASSWORD:=mempool} __DATABASE_TIMEOUT__=${DATABASE_TIMEOUT:=180000} __DATABASE_PID_DIR__=${DATABASE_PID_DIR:=""} +__DATABASE_POOL_SIZE__=${DATABASE_POOL_SIZE:=100} # SYSLOG __SYSLOG_ENABLED__=${SYSLOG_ENABLED:=false} @@ -92,10 +94,6 @@ __SYSLOG_FACILITY__=${SYSLOG_FACILITY:=local7} __STATISTICS_ENABLED__=${STATISTICS_ENABLED:=true} __STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD__=${STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD:=150} -# BISQ -__BISQ_ENABLED__=${BISQ_ENABLED:=false} -__BISQ_DATA_PATH__=${BISQ_DATA_PATH:=/bisq/statsnode-data/btc_mainnet/db} - # SOCKS5PROXY __SOCKS5PROXY_ENABLED__=${SOCKS5PROXY_ENABLED:=false} __SOCKS5PROXY_USE_ONION__=${SOCKS5PROXY_USE_ONION:=true} @@ -109,8 +107,6 @@ __EXTERNAL_DATA_SERVER_MEMPOOL_API__=${EXTERNAL_DATA_SERVER_MEMPOOL_API:=https:/ __EXTERNAL_DATA_SERVER_MEMPOOL_ONION__=${EXTERNAL_DATA_SERVER_MEMPOOL_ONION:=http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/api/v1} __EXTERNAL_DATA_SERVER_LIQUID_API__=${EXTERNAL_DATA_SERVER_LIQUID_API:=https://liquid.network/api/v1} __EXTERNAL_DATA_SERVER_LIQUID_ONION__=${EXTERNAL_DATA_SERVER_LIQUID_ONION:=http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1} -__EXTERNAL_DATA_SERVER_BISQ_URL__=${EXTERNAL_DATA_SERVER_BISQ_URL:=https://bisq.markets/api} -__EXTERNAL_DATA_SERVER_BISQ_ONION__=${EXTERNAL_DATA_SERVER_BISQ_ONION:=http://bisqmktse2cabavbr2xjq7xw3h6g5ottemo5rolfcwt6aly6tp5fdryd.onion/api} # LIGHTNING __LIGHTNING_ENABLED__=${LIGHTNING_ENABLED:=false} @@ -152,11 +148,16 @@ __REDIS_ENABLED__=${REDIS_ENABLED:=false} __REDIS_UNIX_SOCKET_PATH__=${REDIS_UNIX_SOCKET_PATH:=true} __REDIS_BATCH_QUERY_BASE_SIZE__=${REDIS_BATCH_QUERY_BASE_SIZE:=5000} +# FIAT_PRICE +__FIAT_PRICE_ENABLED__=${FIAT_PRICE_ENABLED:=true} +__FIAT_PRICE_API_KEY__=${FIAT_PRICE_API_KEY:=""} + mkdir -p "${__MEMPOOL_CACHE_DIR__}" sed -i "s!__MEMPOOL_NETWORK__!${__MEMPOOL_NETWORK__}!g" mempool-config.json sed -i "s!__MEMPOOL_BACKEND__!${__MEMPOOL_BACKEND__}!g" mempool-config.json sed -i "s!__MEMPOOL_ENABLED__!${__MEMPOOL_ENABLED__}!g" mempool-config.json +sed -i "s!__MEMPOOL_OFFICIAL__!${__MEMPOOL_OFFICIAL__}!g" mempool-config.json sed -i "s!__MEMPOOL_HTTP_PORT__!${__MEMPOOL_HTTP_PORT__}!g" mempool-config.json sed -i "s!__MEMPOOL_SPAWN_CLUSTER_PROCS__!${__MEMPOOL_SPAWN_CLUSTER_PROCS__}!g" mempool-config.json sed -i "s!__MEMPOOL_API_URL_PREFIX__!${__MEMPOOL_API_URL_PREFIX__}!g" mempool-config.json @@ -170,6 +171,7 @@ sed -i "s!__MEMPOOL_INITIAL_BLOCKS_AMOUNT__!${__MEMPOOL_INITIAL_BLOCKS_AMOUNT__} sed -i "s!__MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__!${__MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__}!g" mempool-config.json sed -i "s!__MEMPOOL_INDEXING_BLOCKS_AMOUNT__!${__MEMPOOL_INDEXING_BLOCKS_AMOUNT__}!g" mempool-config.json sed -i "s!__MEMPOOL_BLOCKS_SUMMARIES_INDEXING__!${__MEMPOOL_BLOCKS_SUMMARIES_INDEXING__}!g" mempool-config.json +sed -i "s!__MEMPOOL_GOGGLES_INDEXING__!${__MEMPOOL_GOGGLES_INDEXING__}!g" mempool-config.json sed -i "s!__MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__!${__MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__}!g" mempool-config.json sed -i "s!__MEMPOOL_EXTERNAL_ASSETS__!${__MEMPOOL_EXTERNAL_ASSETS__}!g" mempool-config.json sed -i "s!__MEMPOOL_EXTERNAL_MAX_RETRY__!${__MEMPOOL_EXTERNAL_MAX_RETRY__}!g" mempool-config.json @@ -180,9 +182,8 @@ sed -i "s!__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__!${__MEMPOOL_AUTOMATIC_BLOCK_REI sed -i "s!__MEMPOOL_POOLS_JSON_URL__!${__MEMPOOL_POOLS_JSON_URL__}!g" mempool-config.json sed -i "s!__MEMPOOL_POOLS_JSON_TREE_URL__!${__MEMPOOL_POOLS_JSON_TREE_URL__}!g" mempool-config.json sed -i "s!__MEMPOOL_AUDIT__!${__MEMPOOL_AUDIT__}!g" mempool-config.json -sed -i "s!__MEMPOOL_ADVANCED_GBT_MEMPOOL__!${__MEMPOOL_ADVANCED_GBT_MEMPOOL__}!g" mempool-config.json sed -i "s!__MEMPOOL_RUST_GBT__!${__MEMPOOL_RUST_GBT__}!g" mempool-config.json -sed -i "s!__MEMPOOL_ADVANCED_GBT_AUDIT__!${__MEMPOOL_ADVANCED_GBT_AUDIT__}!g" mempool-config.json +sed -i "s!__MEMPOOL_LIMIT_GBT__!${__MEMPOOL_LIMIT_GBT__}!g" mempool-config.json sed -i "s!__MEMPOOL_CPFP_INDEXING__!${__MEMPOOL_CPFP_INDEXING__}!g" mempool-config.json sed -i "s!__MEMPOOL_MAX_BLOCKS_BULK_QUERY__!${__MEMPOOL_MAX_BLOCKS_BULK_QUERY__}!g" mempool-config.json sed -i "s!__MEMPOOL_DISK_CACHE_BLOCK_INTERVAL__!${__MEMPOOL_DISK_CACHE_BLOCK_INTERVAL__}!g" mempool-config.json @@ -228,6 +229,7 @@ sed -i "s!__DATABASE_USERNAME__!${__DATABASE_USERNAME__}!g" mempool-config.json sed -i "s!__DATABASE_PASSWORD__!${__DATABASE_PASSWORD__}!g" mempool-config.json sed -i "s!__DATABASE_TIMEOUT__!${__DATABASE_TIMEOUT__}!g" mempool-config.json sed -i "s!__DATABASE_PID_DIR__!${__DATABASE_PID_DIR__}!g" mempool-config.json +sed -i "s!__DATABASE_POOL_SIZE__!${__DATABASE_POOL_SIZE__}!g" mempool-config.json sed -i "s!__SYSLOG_ENABLED__!${__SYSLOG_ENABLED__}!g" mempool-config.json sed -i "s!__SYSLOG_HOST__!${__SYSLOG_HOST__}!g" mempool-config.json @@ -238,9 +240,6 @@ sed -i "s!__SYSLOG_FACILITY__!${__SYSLOG_FACILITY__}!g" mempool-config.json sed -i "s!__STATISTICS_ENABLED__!${__STATISTICS_ENABLED__}!g" mempool-config.json sed -i "s!__STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD__!${__STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD__}!g" mempool-config.json -sed -i "s!__BISQ_ENABLED__!${__BISQ_ENABLED__}!g" mempool-config.json -sed -i "s!__BISQ_DATA_PATH__!${__BISQ_DATA_PATH__}!g" mempool-config.json - sed -i "s!__SOCKS5PROXY_ENABLED__!${__SOCKS5PROXY_ENABLED__}!g" mempool-config.json sed -i "s!__SOCKS5PROXY_USE_ONION__!${__SOCKS5PROXY_USE_ONION__}!g" mempool-config.json sed -i "s!__SOCKS5PROXY_HOST__!${__SOCKS5PROXY_HOST__}!g" mempool-config.json @@ -252,8 +251,6 @@ sed -i "s!__EXTERNAL_DATA_SERVER_MEMPOOL_API__!${__EXTERNAL_DATA_SERVER_MEMPOOL_ sed -i "s!__EXTERNAL_DATA_SERVER_MEMPOOL_ONION__!${__EXTERNAL_DATA_SERVER_MEMPOOL_ONION__}!g" mempool-config.json sed -i "s!__EXTERNAL_DATA_SERVER_LIQUID_API__!${__EXTERNAL_DATA_SERVER_LIQUID_API__}!g" mempool-config.json sed -i "s!__EXTERNAL_DATA_SERVER_LIQUID_ONION__!${__EXTERNAL_DATA_SERVER_LIQUID_ONION__}!g" mempool-config.json -sed -i "s!__EXTERNAL_DATA_SERVER_BISQ_URL__!${__EXTERNAL_DATA_SERVER_BISQ_URL__}!g" mempool-config.json -sed -i "s!__EXTERNAL_DATA_SERVER_BISQ_ONION__!${__EXTERNAL_DATA_SERVER_BISQ_ONION__}!g" mempool-config.json # LIGHTNING sed -i "s!__LIGHTNING_ENABLED__!${__LIGHTNING_ENABLED__}!g" mempool-config.json @@ -295,4 +292,8 @@ sed -i "s!__REDIS_ENABLED__!${__REDIS_ENABLED__}!g" mempool-config.json sed -i "s!__REDIS_UNIX_SOCKET_PATH__!${__REDIS_UNIX_SOCKET_PATH__}!g" mempool-config.json sed -i "s!__REDIS_BATCH_QUERY_BASE_SIZE__!${__REDIS_BATCH_QUERY_BASE_SIZE__}!g" mempool-config.json +# FIAT_PRICE +sed -i "s!__FIAT_PRICE_ENABLED__!${__FIAT_PRICE_ENABLED__}!g" mempool-config.json +sed -i "s!__FIAT_PRICE_API_KEY__!${__FIAT_PRICE_API_KEY__}!g" mempool-config.json + node /backend/package/index.js diff --git a/docker/frontend/Dockerfile b/docker/frontend/Dockerfile index 4d04ae88f..3a63107bf 100644 --- a/docker/frontend/Dockerfile +++ b/docker/frontend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.8.0-buster-slim AS builder +FROM node:20.12.0-buster-slim AS builder ARG commitHash ENV DOCKER_COMMIT_HASH=${commitHash} @@ -13,7 +13,7 @@ RUN npm install --omit=dev --omit=optional RUN npm run build -FROM nginx:1.24.0-alpine +FROM nginx:1.25.4-alpine WORKDIR /patch diff --git a/docker/frontend/entrypoint.sh b/docker/frontend/entrypoint.sh index 4e14aefac..20f05e53c 100644 --- a/docker/frontend/entrypoint.sh +++ b/docker/frontend/entrypoint.sh @@ -20,8 +20,6 @@ __TESTNET_ENABLED__=${TESTNET_ENABLED:=false} __SIGNET_ENABLED__=${SIGNET_ENABLED:=false} __LIQUID_ENABLED__=${LIQUID_ENABLED:=false} __LIQUID_TESTNET_ENABLED__=${LIQUID_TESTNET_ENABLED:=false} -__BISQ_ENABLED__=${BISQ_ENABLED:=false} -__BISQ_SEPARATE_BACKEND__=${BISQ_SEPARATE_BACKEND:=false} __ITEMS_PER_PAGE__=${ITEMS_PER_PAGE:=10} __KEEP_BLOCKS_AMOUNT__=${KEEP_BLOCKS_AMOUNT:=8} __NGINX_PROTOCOL__=${NGINX_PROTOCOL:=http} @@ -32,7 +30,6 @@ __MEMPOOL_BLOCKS_AMOUNT__=${MEMPOOL_BLOCKS_AMOUNT:=8} __BASE_MODULE__=${BASE_MODULE:=mempool} __MEMPOOL_WEBSITE_URL__=${MEMPOOL_WEBSITE_URL:=https://mempool.space} __LIQUID_WEBSITE_URL__=${LIQUID_WEBSITE_URL:=https://liquid.network} -__BISQ_WEBSITE_URL__=${BISQ_WEBSITE_URL:=https://bisq.markets} __MINING_DASHBOARD__=${MINING_DASHBOARD:=true} __LIGHTNING__=${LIGHTNING:=false} __AUDIT__=${AUDIT:=false} @@ -41,14 +38,13 @@ __TESTNET_BLOCK_AUDIT_START_HEIGHT__=${TESTNET_BLOCK_AUDIT_START_HEIGHT:=0} __SIGNET_BLOCK_AUDIT_START_HEIGHT__=${SIGNET_BLOCK_AUDIT_START_HEIGHT:=0} __ACCELERATOR__=${ACCELERATOR:=false} __HISTORICAL_PRICE__=${HISTORICAL_PRICE:=true} +__ADDITIONAL_CURRENCIES__=${ADDITIONAL_CURRENCIES:=false} # Export as environment variables to be used by envsubst export __TESTNET_ENABLED__ export __SIGNET_ENABLED__ export __LIQUID_ENABLED__ export __LIQUID_TESTNET_ENABLED__ -export __BISQ_ENABLED__ -export __BISQ_SEPARATE_BACKEND__ export __ITEMS_PER_PAGE__ export __KEEP_BLOCKS_AMOUNT__ export __NGINX_PROTOCOL__ @@ -59,7 +55,6 @@ export __MEMPOOL_BLOCKS_AMOUNT__ export __BASE_MODULE__ export __MEMPOOL_WEBSITE_URL__ export __LIQUID_WEBSITE_URL__ -export __BISQ_WEBSITE_URL__ export __MINING_DASHBOARD__ export __LIGHTNING__ export __AUDIT__ @@ -68,6 +63,7 @@ export __TESTNET_BLOCK_AUDIT_START_HEIGHT__ export __SIGNET_BLOCK_AUDIT_START_HEIGHT__ export __ACCELERATOR__ export __HISTORICAL_PRICE__ +export __ADDITIONAL_CURRENCIES__ folder=$(find /var/www/mempool -name "config.js" | xargs dirname) echo ${folder} diff --git a/docker/init.sh b/docker/init.sh index ee9ac9542..3c5ec6aa3 100755 --- a/docker/init.sh +++ b/docker/init.sh @@ -1,7 +1,7 @@ #!/bin/sh #backend -cp ./docker/backend/* ./backend/ +cp -r ./docker/backend/* ./backend/ #geoip-data mkdir -p ./backend/GeoIP/ @@ -13,8 +13,8 @@ localhostIP="127.0.0.1" cp ./docker/frontend/* ./frontend cp ./nginx.conf ./frontend/ cp ./nginx-mempool.conf ./frontend/ -sed -i "s/${localhostIP}:80/0.0.0.0:__MEMPOOL_FRONTEND_HTTP_PORT__/g" ./frontend/nginx.conf -sed -i "s/${localhostIP}/0.0.0.0/g" ./frontend/nginx.conf -sed -i "s/user nobody;//g" ./frontend/nginx.conf -sed -i "s!/etc/nginx/nginx-mempool.conf!/etc/nginx/conf.d/nginx-mempool.conf!g" ./frontend/nginx.conf -sed -i "s/${localhostIP}:8999/__MEMPOOL_BACKEND_MAINNET_HTTP_HOST__:__MEMPOOL_BACKEND_MAINNET_HTTP_PORT__/g" ./frontend/nginx-mempool.conf +sed -i"" -e "s/${localhostIP}:80/0.0.0.0:__MEMPOOL_FRONTEND_HTTP_PORT__/g" ./frontend/nginx.conf +sed -i"" -e "s/${localhostIP}/0.0.0.0/g" ./frontend/nginx.conf +sed -i"" -e "s/user nobody;//g" ./frontend/nginx.conf +sed -i"" -e "s!/etc/nginx/nginx-mempool.conf!/etc/nginx/conf.d/nginx-mempool.conf!g" ./frontend/nginx.conf +sed -i"" -e "s/${localhostIP}:8999/__MEMPOOL_BACKEND_MAINNET_HTTP_HOST__:__MEMPOOL_BACKEND_MAINNET_HTTP_PORT__/g" ./frontend/nginx-mempool.conf diff --git a/frontend/.eslintrc b/frontend/.eslintrc index 9347a2050..e2652c6c8 100644 --- a/frontend/.eslintrc +++ b/frontend/.eslintrc @@ -19,6 +19,7 @@ "@typescript-eslint/no-this-alias": 1, "@typescript-eslint/no-var-requires": 1, "@typescript-eslint/explicit-function-return-type": 1, + "@typescript-eslint/no-unused-vars": 1, "no-case-declarations": 1, "no-console": 1, "no-constant-condition": 1, diff --git a/frontend/.gitignore b/frontend/.gitignore index 8159e7c7b..d2a765dda 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -6,6 +6,13 @@ /out-tsc server.run.js +# docker +Dockerfile +entrypoint.sh +nginx-mempool.conf +nginx.conf +wait-for + # Only exists if Bazel was run /bazel-out diff --git a/frontend/README.md b/frontend/README.md index 8fc77a2b4..069f1d5f0 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -22,14 +22,13 @@ cd mempool/frontend ### 2. Specify Website -The same frontend codebase is used for https://mempool.space, https://liquid.network and https://bisq.markets. +The same frontend codebase is used for https://mempool.space and https://liquid.network. Configure the frontend for the site you want by running the corresponding command: ``` $ npm run config:defaults:mempool $ npm run config:defaults:liquid -$ npm run config:defaults:bisq ``` ### 3. Run the Frontend diff --git a/frontend/angular.json b/frontend/angular.json index 792f59443..f55c59ae9 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -223,11 +223,11 @@ "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "browserTarget": "mempool:build" + "buildTarget": "mempool:build" }, "configurations": { "production": { - "browserTarget": "mempool:build:production" + "buildTarget": "mempool:build:production" }, "local": { "proxyConfig": "proxy.conf.local.js", @@ -264,7 +264,7 @@ "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { - "browserTarget": "mempool:build" + "buildTarget": "mempool:build" } }, "e2e": { @@ -280,6 +280,56 @@ } } }, + "server": { + "builder": "@angular-devkit/build-angular:server", + "options": { + "outputPath": "dist/mempool/server", + "main": "server.ts", + "tsConfig": "tsconfig.server.json" + }, + "configurations": { + "production": { + "outputHashing": "media", + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "sourceMap": false, + "localize": true, + "optimization": false + } + } + }, + "serve-ssr": { + "builder": "@angular-devkit/build-angular:ssr-dev-server", + "options": { + "browserTarget": "mempool:build", + "serverTarget": "mempool:server" + }, + "configurations": { + "production": { + "browserTarget": "mempool:build:production", + "serverTarget": "mempool:server:production", + "optimization": false, + "sourceMap": true + } + } + }, + "prerender": { + "builder": "@angular-devkit/build-angular:prerender", + "options": { + "browserTarget": "mempool:build:production", + "serverTarget": "mempool:server:production", + "routes": [ + "/" + ] + }, + "configurations": { + "production": {} + } + }, "cypress-run": { "builder": "@cypress/schematic:cypress", "options": { diff --git a/frontend/cypress/fixtures/mainnet_mempoolInfo.json b/frontend/cypress/fixtures/mainnet_mempoolInfo.json index 84a9eb304..5c41bd4dd 100644 --- a/frontend/cypress/fixtures/mainnet_mempoolInfo.json +++ b/frontend/cypress/fixtures/mainnet_mempoolInfo.json @@ -1,554 +1,771 @@ { "mempoolInfo": { - "loaded": true, - "size": 15168, - "bytes": 6070217, - "usage": 30922560, - "maxmempool": 300000000, - "mempoolminfee": 0.00001, - "minrelaytxfee": 0.00001, - "unbroadcastcount": 0 - }, - "vBytesPerSecond": 2249, - "blocks": [ - { - "extras": { - "reward": 636429476, - "coinbaseTx": { - "vin": [ - { - "scriptsig": "031e190b04a4632f622f466f756e6472792055534120506f6f6c202364726f70676f6c642f12969a355b4c000000000000" - } - ], - "vout": [ - { - "scriptpubkey_address": "19dENFt4wVwos6xtgwStA6n8bbA57WCS58", - "value": 636429476 - } - ] - }, - "medianFee": 6, - "feeRange": [ - 1, - 3, - 5, - 6, - 10, - 16, - 272 - ], - "totalFees": 11429476, - "avgFee": 4177, - "avgFeeRate": 11, - "pool": { - "id": 108, - "name": "Foundry USA" - }, - "matchRate": 98 - }, - "id": "0000000000000000000918db3142fc4ccea865edb9a9f0c05f5a1b507fd7182a", - "height": 727326, - "version": 536870912, - "timestamp": 1647272873, - "bits": 386545523, - "nonce": 105379536, - "difficulty": 27550332084343.84, - "merkle_root": "d39e7d7b8e2374be518a313b9af734a0d0d87c7e8c001b5c19472258790ebfbc", - "tx_count": 2737, - "size": 1714410, - "weight": 3993069, - "previousblockhash": "00000000000000000007b6a24a3caa6b1f320c163b6ae15f763ca4d157a304ab" - }, - { - "extras": { - "reward": 634306311, - "coinbaseTx": { - "vin": [ - { - "scriptsig": "031f190b1362696e616e63652f3832321c000b0193666a88fabe6d6de404a9ad37192b7a677b62eae09c0d60d2158b037e7250c395a0c4db62e699f20200000000000000bf370000a9600000" - } - ], - "vout": [ - { - "scriptpubkey_address": "1JvXhnHCi6XqcanvrZJ5s2Qiv4tsmm2UMy", - "value": 634306311 - } - ] - }, - "medianFee": 5, - "feeRange": [ - 1, - 2, - 3, - 5, - 9, - 15, - 316 - ], - "totalFees": 9306311, - "avgFee": 2861, - "avgFeeRate": 9, - "pool": { - "id": 102, - "name": "Binance Pool" - }, - "matchRate": 98 - }, - "id": "000000000000000000012de2fedb9f88f071ee149c03b4265cf77667b63c61fe", - "height": 727327, - "version": 536928256, - "timestamp": 1647273549, - "bits": 386545523, - "nonce": 104858074, - "difficulty": 27550332084343.84, - "merkle_root": "d2349186400acd9de2d1812bf2916834bf3299bf4a32d27da91aa2c04a434360", - "tx_count": 3253, - "size": 1610333, - "weight": 3993434, - "previousblockhash": "0000000000000000000918db3142fc4ccea865edb9a9f0c05f5a1b507fd7182a" - }, - { - "extras": { - "reward": 629478265, - "coinbaseTx": { - "vin": [ - { - "scriptsig": "0320190b215c204d41524120506f6f6c205c000000003766ea3bdd65bc206ece9c55812c0000" - } - ], - "vout": [ - { - "scriptpubkey_address": "1A32KFEX7JNPmU1PVjrtiXRrTQcesT3Nf1", - "value": 629478265 - } - ] - }, - "medianFee": 2, - "feeRange": [ - 1, - 1, - 1, - 2, - 3, - 9, - 752 - ], - "totalFees": 4478265, - "avgFee": 2179, - "avgFeeRate": 4, - "pool": { - "id": 112, - "name": "MARA Pool" - }, - "matchRate": 99 - }, - "id": "0000000000000000000699b7d37e0614c1732f4a67314b039de04e025eb7f901", - "height": 727328, - "version": 543162368, - "timestamp": 1647273774, - "bits": 386545523, - "nonce": 2592684945, - "difficulty": 27550332084343.84, - "merkle_root": "94c744153aebf492935379cc1a1e9d1f28a3fb1f3c669e55e22b34ce09e7929c", - "tx_count": 2056, - "size": 1678108, - "weight": 3992617, - "previousblockhash": "000000000000000000012de2fedb9f88f071ee149c03b4265cf77667b63c61fe" - }, - { - "extras": { - "reward": 627516396, - "coinbaseTx": { - "vin": [ - { - "scriptsig": "0321190b04be672f622f466f756e6472792055534120506f6f6c202364726f70676f6c642f063791a2b6c3000000000000" - } - ], - "vout": [ - { - "scriptpubkey_address": "19dENFt4wVwos6xtgwStA6n8bbA57WCS58", - "value": 627516396 - } - ] - }, - "medianFee": 2, - "feeRange": [ - 1, - 1, - 1, - 2, - 5, - 14, - 347 - ], - "totalFees": 2516396, - "avgFee": 3122, - "avgFeeRate": 7, - "pool": { - "id": 108, - "name": "Foundry USA" - }, - "matchRate": 100 - }, - "id": "00000000000000000008e79402333f8bb6f03147550d36fe731480cf2c9e2fb8", - "height": 727329, - "version": 536870912, - "timestamp": 1647273918, - "bits": 386545523, - "nonce": 585448784, - "difficulty": 27550332084343.84, - "merkle_root": "db0f5982d7797b74b5e92ff8495280e530c1d98f50e21f7cfe244b03c7fef6ab", - "tx_count": 807, - "size": 540195, - "weight": 1379553, - "previousblockhash": "0000000000000000000699b7d37e0614c1732f4a67314b039de04e025eb7f901" - }, - { - "extras": { - "reward": 646529546, - "coinbaseTx": { - "vin": [ - { - "scriptsig": "0322190b04b7712f622f706f6f6c696e2e636f6d2ffabe6d6dec3c68ff646d6125f669e43f1053129a5664705728c0574e7db6449db652f4ed01000000000000009287821bd9525598170e94e5beaab395110045c9120000000000" - } - ], - "vout": [ - { - "scriptpubkey_address": "1GNgwA8JfG7Kc8akJ8opdNWJUihqUztfPe", - "value": 646529546 - } - ] - }, - "medianFee": 12, - "feeRange": [ - 1, - 5, - 10, - 12, - 19, - 38, - 1815 - ], - "totalFees": 21529546, - "avgFee": 6715, - "avgFeeRate": 21, - "pool": { - "id": 91, - "name": "Poolin" - }, - "matchRate": 95 - }, - "id": "00000000000000000003ccf134cbee355e976fbcb6e8573613410c61fa7a1b57", - "height": 727330, - "version": 536870916, - "timestamp": 1647276461, - "bits": 386545523, - "nonce": 1527558879, - "difficulty": 27550332084343.84, - "merkle_root": "af3870e45030bf7775b6eb996ab4c26cbe7964f572ca149e7e58efadaaf18dd4", - "tx_count": 3207, - "size": 1463281, - "weight": 3999829, - "previousblockhash": "00000000000000000008e79402333f8bb6f03147550d36fe731480cf2c9e2fb8" - }, - { - "extras": { - "reward": 644036960, - "coinbaseTx": { - "vin": [ - { - "scriptsig": "0323190b082f5669614254432f2cfabe6d6df26d6c3e2d9050ab39b9332674dd451d68a03cb7e1db64056d4a5e7e235342fe100000000000000010fc7fd917f62bf41977ca2bb5f61d111100000000" - } - ], - "vout": [ - { - "scriptpubkey_address": "18cBEMRxXHqzWWCxZNtU91F5sbUNKhL5PX", - "value": 644036960 - } - ] - }, - "medianFee": 10, - "feeRange": [ - 1, - 5, - 8, - 10, - 17, - 30, - 724 - ], - "totalFees": 19036960, - "avgFee": 6429, - "avgFeeRate": 19, - "pool": { - "id": 70, - "name": "ViaBTC" - }, - "matchRate": 91 - }, - "id": "0000000000000000000270007691145df213b43d62e768052011b7b7d496ac15", - "height": 727331, - "version": 536870912, - "timestamp": 1647277836, - "bits": 386545523, - "nonce": 2078492739, - "difficulty": 27550332084343.84, - "merkle_root": "31df633f82c3ad60e692a9dc8f472167ba37ee00d61cf3e548792e64a6d0264f", - "tx_count": 2962, - "size": 1553257, - "weight": 3993235, - "previousblockhash": "00000000000000000003ccf134cbee355e976fbcb6e8573613410c61fa7a1b57" - }, - { - "extras": { - "reward": 636958014, - "coinbaseTx": { - "vin": [ - { - "scriptsig": "0324190b0414a005ab627463706f6f6c2f6663637367fabe6d6dfa4a2a451de1d9dd584ea9d0fc8016c5600ca1133eac2ae3fefa82ce4e8d536e020000008e9b20aa0f5ae8f8ee41040000000000" - } - ], - "vout": [ - { - "scriptpubkey_address": "1Bf9sZvBHPFGVPX71WX2njhd1NXKv5y7v5", - "value": 636958014 - } - ] - }, - "medianFee": 8, - "feeRange": [ - 1, - 6, - 7, - 8, - 10, - 15, - 503 - ], - "totalFees": 11958014, - "avgFee": 4527, - "avgFeeRate": 11, - "pool": { - "id": 3, - "name": "BTC.com" - }, - "matchRate": 94 - }, - "id": "00000000000000000008b905d911099327f0f4feb992e58d603e96df3c445aa1", - "height": 727332, - "version": 541065220, - "timestamp": 1647278491, - "bits": 386545523, - "nonce": 2560770713, - "difficulty": 27550332084343.84, - "merkle_root": "fb35644b58e7f37bcb3062dda7e385e5a2c7b9f016039e84fff2043d201ea580", - "tx_count": 2642, - "size": 1365172, - "weight": 3993028, - "previousblockhash": "0000000000000000000270007691145df213b43d62e768052011b7b7d496ac15" - }, - { - "extras": { - "reward": 641367077, - "coinbaseTx": { - "vin": [ - { - "scriptsig": "0325190b2cfabe6d6dc9a64b2a34a929eab95ca6793bb05a59a4ada9440f2caae4a291527d5fc5876610000000f09f909f092f4632506f6f6c2f65000000000000000000000000000000000000000000000000000000000000000000000005004d270000" - } - ], - "vout": [ - { - "scriptpubkey_address": "1KFHE7w8BhaENAswwryaoccDb6qcT6DbYY", - "value": 641367077 - } - ] - }, - "medianFee": 10, - "feeRange": [ - 1, - 7, - 8, - 10, - 13, - 27, - 501 - ], - "totalFees": 16367077, - "avgFee": 5359, - "avgFeeRate": 16, - "pool": { - "id": 33, - "name": "F2Pool" - }, - "matchRate": 98 - }, - "id": "000000000000000000035194e2331953ecc1c2d4783c6fb692ebd0553795b185", - "height": 727333, - "version": 1073676292, - "timestamp": 1647279951, - "bits": 386545523, - "nonce": 2216944582, - "difficulty": 27550332084343.84, - "merkle_root": "f61bcda97b7b65336bd2b3f3dbea1ba0e00d881f3abb92d364795e971f4417fd", - "tx_count": 3055, - "size": 1511571, - "weight": 3998487, - "previousblockhash": "00000000000000000008b905d911099327f0f4feb992e58d603e96df3c445aa1" - } - ], - "conversions": { - "USD": 38723.918 + "loaded": true, + "size": 112686, + "bytes": 175691391, + "usage": 856780672, + "total_fee": 5.10536864, + "maxmempool": 300000000, + "mempoolminfee": 0.00001305, + "minrelaytxfee": 0.00001, + "incrementalrelayfee": 0.00001, + "unbroadcastcount": 0, + "fullrbf": true }, + "vBytesPerSecond": 2364, "mempool-blocks": [ - { - "blockSize": 1552154, - "blockVSize": 998819, - "nTx": 2700, - "totalFees": 13645525, - "medianFee": 9.348948740137697, - "feeRange": [ - 7.1906428439860015, - 7.636363636363637, - 8.03366058906031, - 8.537102473498233, - 10.102654867256637, - 12.356010230179027, - 17.915194346289752, - 350.87719298245617 - ] - }, - { - "blockSize": 1685282, - "blockVSize": 999945.5, - "nTx": 2711, - "totalFees": 6580658, - "medianFee": 6.031858407079646, - "feeRange": [ - 5.946188340807175, - 6.021505376344086, - 6.122448979591836, - 7.190512129380054 - ] - }, - { - "blockSize": 1598963, - "blockVSize": 991915.25, - "nTx": 2055, - "totalFees": 5267286, - "medianFee": 5.132743362831858, - "feeRange": [ - 5.007085498346717, - 5.095022624434389, - 5.322493224932249, - 5.946188340807175 - ] - }, - { - "blockSize": 1553568, - "blockVSize": 999920.25, - "nTx": 3688, - "totalFees": 4366448, - "medianFee": 4.106194690265487, - "feeRange": [ - 3.075103489059728, - 3.9575971731448765, - 4.559139784946237, - 5.0065912336592335 - ] - }, - { - "blockSize": 1638100, - "blockVSize": 999883, - "nTx": 2186, - "totalFees": 2322349, - "medianFee": 2.336283185840708, - "feeRange": [ - 2.0137103684661524, - 2.111358574610245, - 2.6007067137809186, - 3.075103489059728 - ] - }, - { - "blockSize": 1552617, - "blockVSize": 940875, - "nTx": 1746, - "totalFees": 1351076, - "medianFee": 1.3983842513934581, - "feeRange": [ - 1.0035005834305717, - 1.0972279685560613, - 1.7951509153884215, - 2.0136986301369864 - ] - }, - { - "blockSize": 224482, - "blockVSize": 122290, - "nTx": 65, - "totalFees": 122659, - "medianFee": 1.0020366598778003, - "feeRange": [ - 1, - 1.0010131712259371, - 1.0029268292682927, - 1.0034970564976715 - ] - } + { + "blockSize": 2162709, + "blockVSize": 997944, + "nTx": 4888, + "totalFees": 9079705, + "medianFee": 7.036906854130053, + "feeRange": [ + 7, + 7, + 7.010590015128593, + 7.022801302931596, + 7.12430426716141, + 10.01669449081803, + 236.07305936073058 + ] + }, + { + "blockSize": 2066789, + "blockVSize": 997996.25, + "nTx": 892, + "totalFees": 6623230, + "medianFee": 6.716362627217997, + "feeRange": [ + 6.189555125725338, + 6.42654028436019, + 6.636953672027368, + 6.994764397905759, + 7.005110732538331, + 7.005110732538331, + 7.00556586270872 + ] + }, + { + "blockSize": 1844287, + "blockVSize": 997956.25, + "nTx": 27, + "totalFees": 6370984, + "medianFee": 6.400148152967939, + "feeRange": [ + 6.152492668621701, + 6.156213928434013, + 6.165703275529865, + 6.179153864334673, + 6.400128130693307, + 6.400160164168272, + 6.400176181026004 + ] + }, + { + "blockSize": 1818283, + "blockVSize": 997984.75, + "nTx": 56, + "totalFees": 6365410, + "medianFee": 6.4000840859779125, + "feeRange": [ + 6.125, + 6.125, + 6.1303214596003475, + 6.149318801089918, + 6.161055058005517, + 6.400068069430819, + 6.400116119312593 + ] + }, + { + "blockSize": 1891412, + "blockVSize": 997993.25, + "nTx": 127, + "totalFees": 6355046, + "medianFee": 6.400068069430819, + "feeRange": [ + 6.024096385542169, + 6.033333333333333, + 6.049822064056939, + 6.080719839259195, + 6.123809523809523, + 6.141470180305132, + 6.400080081082096 + ] + }, + { + "blockSize": 1939469, + "blockVSize": 997987.25, + "nTx": 56, + "totalFees": 6350364, + "medianFee": 6.400036036577125, + "feeRange": [ + 6.01063829787234, + 6.014018691588785, + 6.01996303142329, + 6.023980815347722, + 6.031815335063076, + 6.4000200202705235, + 6.400052052963891 + ] + }, + { + "blockSize": 1906245, + "blockVSize": 997956, + "nTx": 28, + "totalFees": 6348648, + "medianFee": 6.4000200202705235, + "feeRange": [ + 6, + 6.010496568429552, + 6.011583011583012, + 6.019933554817276, + 6.400004004044084, + 6.400036036577125, + 6.400052052963891 + ] + }, + { + "blockSize": 333333741, + "blockVSize": 168604440, + "nTx": 106612, + "totalFees": 463043477, + "medianFee": 1.5796055679151149, + "feeRange": [ + 1.0005271543495804, + 1.1548204755273255, + 1.3540307201497894, + 1.408026342363497, + 1.519000275099878, + 1.5796055679151149, + 3.0039108631786284, + 3.927655905571819, + 5.004098751020287, + 5.041807153997041, + 6.4000294177934 + ] + } ], "transactions": [ - { - "txid": "d9e77f48b573addece8502bd081c39e3a7cd4df426e49ffddd7a45f9ffbc7d73", - "fee": 1136, - "vsize": 141.25, - "value": 50801 - }, - { - "txid": "6c08a2bacae4dc58eef85d3319256aaf5bd80d5364ffe9fff2e21a25a71d4e4b", - "fee": 1700, - "vsize": 140.25, - "value": 1538700 - }, - { - "txid": "163ba1ec8c9568821fa787f81e483f4f8216c4caeca30396b6322918b9763868", - "fee": 1850, - "vsize": 247, - "value": 369387 - }, - { - "txid": "e4d668153578c17d8f32404dd322e46d442c8eb00d58551b2ee51bdd1cf6d338", - "fee": 1369, - "vsize": 152.25, - "value": 723647 - }, - { - "txid": "7684807a3c6b1171b312be8cacc935883210d211045b6206516728484bc56e9e", - "fee": 845, - "vsize": 141.5, - "value": 464175 - }, - { - "txid": "249c69312f72cdba3ac5420eaf23a0e504570295daf7da8d0313883350c28778", - "fee": 330, - "vsize": 163.25, - "value": 116248571 - } + { + "txid": "6942073671954381264699dffce75b99d8afd1a3ccc8b58ff0201dcfd890be39", + "fee": 1094, + "vsize": 154, + "value": 365466, + "rate": 7.103896103896104 + }, + { + "txid": "e88521600e3c8348d0534777ca6490091cb3cdd1fac8a2abe95123f7cef319e5", + "fee": 2568, + "vsize": 208.25, + "value": 29143035, + "rate": 12.331332533013205 + }, + { + "txid": "e4c25dfb025084d0bad34e2350d3a49eff7cff7ee8dddeb069c56fa1511f5ce8", + "fee": 4548, + "vsize": 535.5, + "value": 215164, + "rate": 8.492997198879552 + }, + { + "txid": "dde3a5ac76a1aa79639a1b2f7509ab5c71996de00140ada84218bce12f2516f5", + "fee": 2110, + "vsize": 211.5, + "value": 196181, + "rate": 9.976359338061465 + }, + { + "txid": "28319981d28aa77dd3c75ad31cadc78c20c292d4713ecec1d4f7f6c495f3843b", + "fee": 3000, + "vsize": 202, + "value": 546, + "rate": 14.851485148514852 + }, + { + "txid": "29a2292326cdf97bb1189ea67e1ae5094db4be15ea05ff3b0624e4ee61ec1441", + "fee": 1970, + "vsize": 197, + "value": 3875456, + "rate": 10 + } ], - "backendInfo": { - "hostname": "node206.mempool.space", - "gitCommit": "8e61720e09faf5e0ecde1768583b1be9bb32292e", - "version": "2.4.0-dev" - }, "loadingIndicators": {}, + "fees": { + "fastestFee": 8, + "halfHourFee": 8, + "hourFee": 8, + "economyFee": 4, + "minimumFee": 2 + }, + "rbfSummary": [ + { + "txid": "feae4fb145c57d48c32b11c7f9ca15f2be10f102894dceed1b67f796b520c740", + "mined": false, + "fullRbf": false, + "oldFee": 8400, + "oldVsize": 345, + "newFee": 13301, + "newVsize": 346 + }, + { + "txid": "02b30c113a096d73871ea2d7c34189aadd04a60c3477043f19e3c806f6d69960", + "mined": false, + "fullRbf": false, + "oldFee": 2450, + "oldVsize": 263.25, + "newFee": 3151, + "newVsize": 263.25 + }, + { + "txid": "1d10e449f9b38126a707b6b88089d28ef81114acd2d8f9b0bd3f3266a7496e1f", + "mined": false, + "fullRbf": false, + "oldFee": 1967, + "oldVsize": 109.5, + "newFee": 3933, + "newVsize": 109.5 + }, + { + "txid": "281b1567a1de2a5c9109470b0a523702e438e935a4aa387727506ee852486a77", + "mined": false, + "fullRbf": false, + "oldFee": 1286, + "oldVsize": 143.25, + "newFee": 1550, + "newVsize": 143.25 + }, + { + "txid": "68ce3641b7e85fe60315bc1126cb93e8c61bdc3720f5d84bb0c8254e6cd58f82", + "mined": true, + "fullRbf": false, + "oldFee": 7579, + "oldVsize": 109.25, + "newFee": 14583, + "newVsize": 109.25 + }, + { + "txid": "08cf843ac10a7243d249d13912e27e4b635464ef5819c92488f6205d50d7c874", + "mined": true, + "fullRbf": false, + "oldFee": 1136, + "oldVsize": 141.5, + "newFee": 2272, + "newVsize": 141.5 + } + ], + "blocks": [ + { + "id": "0000000000000000000200e0829f5dd72b02401660837bf67379f6a7adf4d8c9", + "height": 837043, + "version": 803872768, + "timestamp": 1711850338, + "bits": 386097875, + "nonce": 2002092656, + "difficulty": 83126997340024.61, + "merkle_root": "e328e882992bd2de39ffc0a3ac331c31e64d7f4eb33547bb8fcbc09c2a4bffa3", + "tx_count": 3191, + "size": 2117591, + "weight": 3992738, + "previousblockhash": "000000000000000000011cefb2db6b82b6ae69b4ec06eedc81fc85d16f97865d", + "mediantime": 1711847828, + "stale": false, + "extras": { + "reward": 635081690, + "coinbaseRaw": "03b3c50c0463c308662f466f756e6472792055534120506f6f6c202364726f70676f6c642f3ed1ff622486000000000000", + "orphans": [], + "medianFee": 8.020001813606207, + "feeRange": [ + 7, + 7.011686143572621, + 7.012612612612613, + 7.12430426716141, + 10.005032712632108, + 15.515151515151516, + 102.36199095022624 + ], + "totalFees": 10081690, + "avgFee": 3160, + "avgFeeRate": 10, + "utxoSetChange": 3295, + "avgTxSize": 663.52, + "totalInputs": 5670, + "totalOutputs": 8965, + "totalOutputAmt": 163033638953, + "segwitTotalTxs": 3117, + "segwitTotalSize": 2095166, + "segwitTotalWeight": 3903146, + "feePercentiles": null, + "virtualSize": 998184.5, + "coinbaseAddress": "bc1qxhmdufsvnuaaaer4ynz88fspdsxq2h9e9cetdj", + "coinbaseSignature": "OP_0 OP_PUSHBYTES_20 35f6de260c9f3bdee47524c473a6016c0c055cb9", + "coinbaseSignatureAscii": "\u0000\u0000\u0000\u0000\u0000\u0000", + "header": "0020ea2f5d86976fd185fc81dcee06ecb469aeb6826bdbb2ef1c01000000000000000000a3ff4b2a9cc0cb8fbb4735b34e7f4de6311c33aca3c0ff39ded22b9982e828e362c30866d362031770825577", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 111, + "name": "Foundry USA", + "slug": "foundryusa" + }, + "matchRate": 100, + "expectedFees": 10293045, + "expectedWeight": 3991577, + "similarity": 0.9655852166344173 + } + }, + { + "id": "00000000000000000001911ff550eeb8493e589fdd6f1fc080f30969c26cf853", + "height": 837044, + "version": 1073676288, + "timestamp": 1711850704, + "bits": 386097875, + "nonce": 1562078084, + "difficulty": 83126997340024.61, + "merkle_root": "9756f896db91a4a7573cbd0e3baa4d8905c46a60ceed30cdcb440dc66d3fc102", + "tx_count": 3502, + "size": 1968923, + "weight": 3993734, + "previousblockhash": "0000000000000000000200e0829f5dd72b02401660837bf67379f6a7adf4d8c9", + "mediantime": 1711848750, + "stale": false, + "extras": { + "reward": 636442248, + "coinbaseRaw": "03b4c50c1b4d696e656420627920416e74506f6f6c38333035010001214a5697fabe6d6d89907adbb0addfe28be89c002fbe45908b9b32c68d1802b86ff36a5dfa22c6c810000000000000000000cbd2a647000000000000", + "orphans": [], + "medianFee": 9.015025041736227, + "feeRange": [ + 7, + 7.023255813953488, + 8.014973644733765, + 9.015025041736227, + 10.01669449081803, + 15.063829787234043, + 263.89487870619945 + ], + "totalFees": 11442248, + "avgFee": 3268, + "avgFeeRate": 11, + "utxoSetChange": 1740, + "avgTxSize": 562.09, + "totalInputs": 6657, + "totalOutputs": 8397, + "totalOutputAmt": 129424754973, + "segwitTotalTxs": 3428, + "segwitTotalSize": 1944942, + "segwitTotalWeight": 3897918, + "feePercentiles": null, + "virtualSize": 998433.5, + "coinbaseAddress": "37jKPSmbEGwgfacCr2nayn1wTaqMAbA94Z", + "coinbaseSignature": "OP_HASH160 OP_PUSHBYTES_20 42402a28dd61f2718a4b27ae72a4791d5bbdade7 OP_EQUAL", + "coinbaseSignatureAscii": "Mined by AntPool8305\u0001\u0000\u0001!J\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "header": "0000ff3fc9d8f4ada7f67973f67b83601640022bd75d9f82e0000200000000000000000002c13f6dc60d44cbcd30edce606ac405894daa3b0ebd3c57a7a491db96f85697d0c40866d3620317846b1b5d", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 44, + "name": "AntPool", + "slug": "antpool" + }, + "matchRate": 100, + "expectedFees": 11588891, + "expectedWeight": 3991949, + "similarity": 0.9797301408793522 + } + }, + { + "id": "00000000000000000001ee972656708fd787b570ec4b813ae8c5feee4f200c56", + "height": 837045, + "version": 637534208, + "timestamp": 1711853470, + "bits": 386097875, + "nonce": 235193117, + "difficulty": 83126997340024.61, + "merkle_root": "7e9ec459056b973b294dbe6dbcb821eb74b773c806e067db45ec36c781e4c18b", + "tx_count": 3135, + "size": 1521892, + "weight": 3997882, + "previousblockhash": "00000000000000000001911ff550eeb8493e589fdd6f1fc080f30969c26cf853", + "mediantime": 1711849305, + "stale": false, + "extras": { + "reward": 650142949, + "coinbaseRaw": "03b5c50c2cfabe6d6d96c7f8e8e15534e9902d846875d05bc0e5494b8519a860f51802daa2f48c215910000000f09f909f092f4632506f6f6c2f6700000000000000000000000000000000000000000000000000000000000000000000000500b4861602", + "orphans": [], + "medianFee": 19.30172311348782, + "feeRange": [ + 16, + 16.623739332816136, + 18, + 20.04728132387707, + 34.51977401129943, + 40.27210884353742, + 477.45358090185675 + ], + "totalFees": 25142949, + "avgFee": 8022, + "avgFeeRate": 25, + "utxoSetChange": 6554, + "avgTxSize": 485.28000000000003, + "totalInputs": 6655, + "totalOutputs": 13209, + "totalOutputAmt": 391783255749, + "segwitTotalTxs": 2914, + "segwitTotalSize": 1432235, + "segwitTotalWeight": 3639362, + "feePercentiles": null, + "virtualSize": 999470.5, + "coinbaseAddress": "1K6KoYC69NnafWJ7YgtrpwJxBLiijWqwa6", + "coinbaseSignature": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 c6740a12d0a7d556f89782bf5faf0e12cf25a639 OP_EQUALVERIFY OP_CHECKSIG", + "coinbaseSignatureAscii": "\/F2Pool/", + "header": "0000002653f86cc26909f380c01f6fdd9f583e49b8ee50f51f91010000000000000000008bc1e481c736ec45db67e006c873b774eb21b8bc6dbe4d293b976b0559c49e7e9ecf0866d36203171dc3040e", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 36, + "name": "F2Pool", + "slug": "f2pool" + }, + "matchRate": 99.9, + "expectedFees": 25286032, + "expectedWeight": 3991754, + "similarity": 0.9851630583666668 + } + }, + { + "id": "00000000000000000000ea58c5b60311988e9553e58ac5b4e7a2f1575d07a4f0", + "height": 837046, + "version": 732078080, + "timestamp": 1711853947, + "bits": 386097875, + "nonce": 2294678239, + "difficulty": 83126997340024.61, + "merkle_root": "0ea1bed22d6e98c256d92ee8c7e959949f88188872d290abaf89b3482ab5be55", + "tx_count": 3099, + "size": 1687564, + "weight": 3993067, + "previousblockhash": "00000000000000000001ee972656708fd787b570ec4b813ae8c5feee4f200c56", + "mediantime": 1711849492, + "stale": false, + "extras": { + "reward": 641586175, + "coinbaseRaw": "03b6c50c0484d108662f4d41524120506f6f6c202876303331393234292f38c0a19ff8fe2ab8e720838f0bd6411213114d8592000d010000ffffffff", + "orphans": [], + "medianFee": 15.003962563625333, + "feeRange": [ + 12, + 12.529550827423169, + 13.077227722772276, + 15.024711696869852, + 16.0427807486631, + 22.06646525679758, + 258.6666666666667 + ], + "totalFees": 16586175, + "avgFee": 5353, + "avgFeeRate": 16, + "utxoSetChange": 2417, + "avgTxSize": 544.45, + "totalInputs": 6941, + "totalOutputs": 9358, + "totalOutputAmt": 588984462870, + "segwitTotalTxs": 2969, + "segwitTotalSize": 1563622, + "segwitTotalWeight": 3497407, + "feePercentiles": null, + "virtualSize": 998266.75, + "coinbaseAddress": "15MdAHnkxt9TMC2Rj595hsg8Hnv693pPBB", + "coinbaseSignature": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 2fc701e2049ee4957b07134b6c1d771dd5a96b21 OP_EQUALVERIFY OP_CHECKSIG", + "coinbaseSignatureAscii": "/MARA Pool (v031924)/", + "header": "00a0a22b560c204feefec5e83a814bec70b587d78f70562697ee0100000000000000000055beb52a48b389afab90d2728818889f9459e9c7e82ed956c2986e2dd2bea10e7bd10866d3620317df02c688", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 115, + "name": "MARA Pool", + "slug": "marapool" + }, + "matchRate": 100, + "expectedFees": 16712235, + "expectedWeight": 3991968, + "similarity": 0.9876955157970453 + } + }, + { + "id": "000000000000000000021164d81f6059c18066bc0a93b928fdf455d2481c4708", + "height": 837047, + "version": 536936448, + "timestamp": 1711854238, + "bits": 386097875, + "nonce": 3048189996, + "difficulty": 83126997340024.61, + "merkle_root": "dbf52683cabe20a4bb4d2512cfed294a09e0825cac98bbe8fbafb3027ac9686a", + "tx_count": 2657, + "size": 1840568, + "weight": 3997883, + "previousblockhash": "00000000000000000000ea58c5b60311988e9553e58ac5b4e7a2f1575d07a4f0", + "mediantime": 1711849912, + "stale": false, + "extras": { + "reward": 637535865, + "coinbaseRaw": "03b7c50c2cfabe6d6d97e4a56bfc94081f5de141bab7dc7607f7511394a912e9f152f5e6d6e12f4d7210000000f09f909f092f4632506f6f6c2f7300000000000000000000000000000000000000000000000000000000000000000000000500b02a0c00", + "orphans": [], + "medianFee": 10.88611182786954, + "feeRange": [ + 9.610942249240122, + 10.003260515161395, + 10.42633371169126, + 11.058823529411764, + 12.969267139479905, + 18.044444444444444, + 422.5231646471846 + ], + "totalFees": 12535865, + "avgFee": 4719, + "avgFeeRate": 12, + "utxoSetChange": 3018, + "avgTxSize": 692.52, + "totalInputs": 6835, + "totalOutputs": 9853, + "totalOutputAmt": 272522814809, + "segwitTotalTxs": 2580, + "segwitTotalSize": 1813806, + "segwitTotalWeight": 3890943, + "feePercentiles": [ + 584, + 1492, + 1650, + 2002, + 3186, + 6487, + 942300 + ], + "medianFeeAmt": 2002, + "virtualSize": 999470.75, + "coinbaseAddress": "1K6KoYC69NnafWJ7YgtrpwJxBLiijWqwa6", + "coinbaseSignature": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 c6740a12d0a7d556f89782bf5faf0e12cf25a639 OP_EQUALVERIFY OP_CHECKSIG", + "coinbaseSignatureAscii": "/F2Pool/", + "header": "00000120f0a4075d57f1a2e7b4c58ae553958e981103b6c558ea000000000000000000006a68c97a02b3affbe8bb98ac5c82e0094a29edcf12254dbba420beca8326f5db9ed20866d36203172cb0afb5", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 36, + "name": "F2Pool", + "slug": "f2pool" + }, + "matchRate": 99.96, + "expectedFees": 12610218, + "expectedWeight": 3991619, + "similarity": 0.9361101713526737 + } + }, + { + "id": "00000000000000000001ab25f774a511e1cb09462dab28a36413f7495a65da2f", + "height": 837048, + "version": 536903680, + "timestamp": 1711854388, + "bits": 386097875, + "nonce": 665023114, + "difficulty": 83126997340024.61, + "merkle_root": "ffa1ea239fb6f9ed36f263d68c9739b691788f87f31ea826dbcf9eb64aaf66d7", + "tx_count": 2252, + "size": 1640520, + "weight": 3993366, + "previousblockhash": "000000000000000000021164d81f6059c18066bc0a93b928fdf455d2481c4708", + "mediantime": 1711850338, + "stale": false, + "extras": { + "reward": 636119622, + "coinbaseRaw": "03b8c50c172f5669614254432f4d696e65642062792032373931312f2cfabe6d6d8f86142521fd5c467f05b95e96adf049f4b3716c0cbf20aa9fa92a3a0cefe5761000000000000000108a5eab06f0c0bb91c6f4919eba6e030000000000", + "orphans": [], + "medianFee": 9.02733100888539, + "feeRange": [ + 9, + 9.015025041736227, + 9.015025041736227, + 9.015025041736227, + 11.499042512447339, + 15.024744308808973, + 146.4480408858603 + ], + "totalFees": 11119622, + "avgFee": 4939, + "avgFeeRate": 11, + "utxoSetChange": 908, + "avgTxSize": 728.3000000000001, + "totalInputs": 5995, + "totalOutputs": 6903, + "totalOutputAmt": 93595027274, + "segwitTotalTxs": 2194, + "segwitTotalSize": 1345859, + "segwitTotalWeight": 2814830, + "feePercentiles": null, + "virtualSize": 998341.5, + "coinbaseAddress": "18cBEMRxXHqzWWCxZNtU91F5sbUNKhL5PX", + "coinbaseSignature": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 536ffa992491508dca0354e52f32a3a7a679a53a OP_EQUALVERIFY OP_CHECKSIG", + "coinbaseSignatureAscii": "/ViaBTC/Mined by 27911/", + "header": "0080002008471c48d255f4fd28b9930abc6680c159601fd8641102000000000000000000d766af4ab69ecfdb26a81ef3878f7891b639978cd663f236edf9b69f23eaa1ff34d30866d36203178a72a327", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 73, + "name": "ViaBTC", + "slug": "viabtc" + }, + "matchRate": 100, + "expectedFees": 11558843, + "expectedWeight": 3991937, + "similarity": 0.9606822794906695 + } + }, + { + "id": "000000000000000000012a9794fcc2fd81bd0f54df00f7589ea96ff9f3f2ef49", + "height": 837049, + "version": 536944640, + "timestamp": 1711854621, + "bits": 386097875, + "nonce": 2568084255, + "difficulty": 83126997340024.61, + "merkle_root": "5d02722d5d17fa71ec0dd4a3ac72959b84c089e527d14f6f90a6651982b212e2", + "tx_count": 3260, + "size": 2027949, + "weight": 3992970, + "previousblockhash": "00000000000000000001ab25f774a511e1cb09462dab28a36413f7495a65da2f", + "mediantime": 1711850704, + "stale": false, + "extras": { + "reward": 635976151, + "coinbaseRaw": "03b9c50c041ed408662f466f756e6472792055534120506f6f6c202364726f70676f6c642f407c3137c5140b0000000000", + "orphans": [], + "medianFee": 8.965500401011468, + "feeRange": [ + 7.980922098569158, + 8.03076923076923, + 8.753180661577607, + 9.015025041736227, + 9.044925124792014, + 12.042105263157895, + 200.85543199315654 + ], + "totalFees": 10976151, + "avgFee": 3367, + "avgFeeRate": 10, + "utxoSetChange": -176, + "avgTxSize": 621.98, + "totalInputs": 7230, + "totalOutputs": 7054, + "totalOutputAmt": 99412242005, + "segwitTotalTxs": 3216, + "segwitTotalSize": 1992038, + "segwitTotalWeight": 3849434, + "feePercentiles": null, + "virtualSize": 998242.5, + "coinbaseAddress": "bc1qxhmdufsvnuaaaer4ynz88fspdsxq2h9e9cetdj", + "coinbaseSignature": "OP_0 OP_PUSHBYTES_20 35f6de260c9f3bdee47524c473a6016c0c055cb9", + "coinbaseSignatureAscii": "/Foundry USA Pool #dropgold/", + "header": "002001202fda655a49f71364a328ab2d4609cbe111a574f725ab01000000000000000000e212b2821965a6906f4fd127e589c0849b9572aca3d40dec71fa175d2d72025d1dd40866d36203171fdb1199", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 111, + "name": "Foundry USA", + "slug": "foundryusa" + }, + "matchRate": 100, + "expectedFees": 11047051, + "expectedWeight": 3991840, + "similarity": 0.9522324867980367 + } + }, + { + "id": "00000000000000000000e7d533924ecd9e2096bdf261ae3daf3f04861150c5a8", + "height": 837050, + "version": 548552704, + "timestamp": 1711854927, + "bits": 386097875, + "nonce": 1647633217, + "difficulty": 83126997340024.61, + "merkle_root": "4b7d3685fcdefae2a34359f1b3a4181969f7d84061fca9e5b185a6a81c4cd0d1", + "tx_count": 2659, + "size": 2137871, + "weight": 3992678, + "previousblockhash": "000000000000000000012a9794fcc2fd81bd0f54df00f7589ea96ff9f3f2ef49", + "mediantime": 1711853470, + "stale": false, + "extras": { + "reward": 635929414, + "coinbaseRaw": "03bac50c044fd508662f466f756e6472792055534120506f6f6c202364726f70676f6c642f4344be31b3818e6d00000000", + "orphans": [], + "medianFee": 8.971294962196815, + "feeRange": [ + 7.12430426716141, + 7.9810874704491725, + 8.013355592654424, + 8.01920768307323, + 10.047908843713582, + 17.270506108202444, + 261.78010471204186 + ], + "totalFees": 10929414, + "avgFee": 4111, + "avgFeeRate": 10, + "utxoSetChange": 591, + "avgTxSize": 803.9, + "totalInputs": 6276, + "totalOutputs": 6867, + "totalOutputAmt": 89075789136, + "segwitTotalTxs": 2607, + "segwitTotalSize": 2082733, + "segwitTotalWeight": 3772234, + "feePercentiles": null, + "virtualSize": 998169.5, + "coinbaseAddress": "bc1qxhmdufsvnuaaaer4ynz88fspdsxq2h9e9cetdj", + "coinbaseSignature": "OP_0 OP_PUSHBYTES_20 35f6de260c9f3bdee47524c473a6016c0c055cb9", + "coinbaseSignatureAscii": "/Foundry USA Pool #dropgold/", + "header": "0040b22049eff2f3f96fa99e58f700df540fbd81fdc2fc94972a01000000000000000000d1d04c1ca8a685b1e5a9fc6140d8f7691918a4b3f15943a3e2fadefc85367d4b4fd50866d362031741e33462", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 111, + "name": "Foundry USA", + "slug": "foundryusa" + }, + "matchRate": 100, + "expectedFees": 11074070, + "expectedWeight": 3991755, + "similarity": 0.9867917444269922 + } + } + ], + "conversions": { + "time": 1711854906, + "USD": 69880, + "EUR": 64823, + "GBP": 55198, + "CAD": 95154, + "CHF": 63041, + "AUD": 107374, + "JPY": 10601000 + }, + "backendInfo": { + "hostname": "node205.tk7.mempool.space", + "version": "3.0.0-dev", + "gitCommit": "abbc8a134", + "lightning": false + }, "da": { - "progressPercent": 78.02579365079364, - "difficultyChange": -1.3182475819161987, - "estimatedRetargetDate": 1649197503643.8958, - "remainingBlocks": 443, - "remainingTime": 1647550222725.0728, - "previousRetarget": -1.490392872877445, - "nextRetargetHeight": 727776, - "timeAvg": 607909.4854914972, - "timeOffset": 0 + "progressPercent": 20.33730158730159, + "difficultyChange": 5.914788959658535, + "estimatedRetargetDate": 1712766704114, + "remainingBlocks": 1606, + "remainingTime": 911596114, + "previousRetarget": -0.9778871328980188, + "previousTime": 1711619463, + "nextRetargetHeight": 838656, + "timeAvg": 574743, + "adjustedTimeAvg": 567619, + "timeOffset": 0, + "expectedBlocks": 392.7416666666667 } -} + } \ No newline at end of file diff --git a/frontend/cypress/support/websocket.ts b/frontend/cypress/support/websocket.ts index 4a411eeb2..1356ccc76 100644 --- a/frontend/cypress/support/websocket.ts +++ b/frontend/cypress/support/websocket.ts @@ -38,7 +38,13 @@ export const mockWebSocket = () => { win.mockServer = server; win.mockServer.on('connection', (socket) => { win.mockSocket = socket; - win.mockSocket.send('{"action":"init"}'); + win.mockSocket.send('{"conversions":{"USD":32365.338815782445}}'); + cy.readFile('cypress/fixtures/mainnet_live2hchart.json', 'ascii').then((fixture) => { + win.mockSocket.send(JSON.stringify(fixture)); + }); + cy.readFile('cypress/fixtures/mainnet_mempoolInfo.json', 'ascii').then((fixture) => { + win.mockSocket.send(JSON.stringify(fixture)); + }); }); win.mockServer.on('message', (message) => { @@ -75,8 +81,6 @@ export const emitMempoolInfo = ({ switch (params.command) { case "init": { - win.mockSocket.send('{"action":"init"}'); - win.mockSocket.send('{"action":"want","data":["blocks","stats","mempool-blocks","live-2h-chart"]}'); win.mockSocket.send('{"conversions":{"USD":32365.338815782445}}'); cy.readFile('cypress/fixtures/mainnet_live2hchart.json', 'ascii').then((fixture) => { win.mockSocket.send(JSON.stringify(fixture)); diff --git a/frontend/generate-config.js b/frontend/generate-config.js index 3cc173e00..c7a81a482 100644 --- a/frontend/generate-config.js +++ b/frontend/generate-config.js @@ -71,7 +71,7 @@ const newConfig = `(function (window) { window.__env.${obj.key} = ${typeof obj.value === 'string' ? `'${obj.value}'` : obj.value};`, '')} window.__env.GIT_COMMIT_HASH = '${gitCommitHash}'; window.__env.PACKAGE_JSON_VERSION = '${packetJsonVersion}'; - }(this));`; + }((typeof global !== 'undefined') ? global : this));`; const newConfigTemplate = `(function (window) { window.__env = window.__env || {};${settings.reduce((str, obj) => `${str} diff --git a/frontend/mempool-frontend-config.sample.json b/frontend/mempool-frontend-config.sample.json index 79cce7686..31514a017 100644 --- a/frontend/mempool-frontend-config.sample.json +++ b/frontend/mempool-frontend-config.sample.json @@ -3,8 +3,6 @@ "SIGNET_ENABLED": false, "LIQUID_ENABLED": false, "LIQUID_TESTNET_ENABLED": false, - "BISQ_ENABLED": false, - "BISQ_SEPARATE_BACKEND": false, "ITEMS_PER_PAGE": 10, "KEEP_BLOCKS_AMOUNT": 8, "NGINX_PROTOCOL": "http", @@ -15,7 +13,6 @@ "BASE_MODULE": "mempool", "MEMPOOL_WEBSITE_URL": "https://mempool.space", "LIQUID_WEBSITE_URL": "https://liquid.network", - "BISQ_WEBSITE_URL": "https://bisq.markets", "MINING_DASHBOARD": true, "AUDIT": false, "MAINNET_BLOCK_AUDIT_START_HEIGHT": 0, @@ -23,5 +20,6 @@ "SIGNET_BLOCK_AUDIT_START_HEIGHT": 0, "LIGHTNING": false, "HISTORICAL_PRICE": true, + "ADDITIONAL_CURRENCIES": false, "ACCELERATOR": false } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f49fd614a..4beb4413a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,56 +9,60 @@ "version": "3.0.0-dev", "license": "GNU Affero General Public License v3.0", "dependencies": { - "@angular-devkit/build-angular": "^16.2.0", - "@angular/animations": "^16.2.2", - "@angular/cli": "^16.2.0", - "@angular/common": "^16.2.2", - "@angular/compiler": "^16.2.2", - "@angular/core": "^16.2.2", - "@angular/forms": "^16.2.2", - "@angular/localize": "^16.2.2", - "@angular/platform-browser": "^16.2.2", - "@angular/platform-browser-dynamic": "^16.2.2", - "@angular/platform-server": "^16.2.2", - "@angular/router": "^16.2.2", - "@fortawesome/angular-fontawesome": "~0.13.0", + "@angular-devkit/build-angular": "^17.3.1", + "@angular/animations": "^17.3.1", + "@angular/cli": "^17.3.1", + "@angular/common": "^17.3.1", + "@angular/compiler": "^17.3.1", + "@angular/core": "^17.3.1", + "@angular/forms": "^17.3.1", + "@angular/localize": "^17.3.1", + "@angular/platform-browser": "^17.3.1", + "@angular/platform-browser-dynamic": "^17.3.1", + "@angular/platform-server": "^17.3.1", + "@angular/router": "^17.3.1", + "@angular/ssr": "^17.3.1", + "@fortawesome/angular-fontawesome": "~0.14.1", "@fortawesome/fontawesome-common-types": "~6.5.1", "@fortawesome/fontawesome-svg-core": "~6.5.1", "@fortawesome/free-solid-svg-icons": "~6.5.1", "@mempool/mempool.js": "2.3.0", - "@ng-bootstrap/ng-bootstrap": "^15.1.0", + "@ng-bootstrap/ng-bootstrap": "^16.0.0", "@types/qrcode": "~1.5.0", "bootstrap": "~4.6.2", "browserify": "^17.0.0", "clipboard": "^2.0.11", "domino": "^2.1.6", - "echarts": "~5.4.3", + "echarts": "~5.5.0", + "esbuild": "^0.20.2", "lightweight-charts": "~3.8.0", - "ngx-echarts": "~16.2.0", - "ngx-infinite-scroll": "^16.0.0", + "ngx-echarts": "~17.1.0", + "ngx-infinite-scroll": "^17.0.0", "qrcode": "1.5.1", "rxjs": "~7.8.1", - "tinyify": "^3.1.0", + "tinyify": "^4.0.0", "tlite": "^0.1.9", "tslib": "~2.6.0", - "zone.js": "~0.13.1" + "zone.js": "~0.14.4" }, "devDependencies": { - "@angular/compiler-cli": "^16.1.5", - "@angular/language-service": "^16.1.5", + "@angular/compiler-cli": "^17.3.1", + "@angular/language-service": "^17.3.1", "@types/node": "^18.11.9", - "@typescript-eslint/eslint-plugin": "^5.48.1", - "@typescript-eslint/parser": "^5.48.1", - "eslint": "^8.31.0", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", + "browser-sync": "^3.0.0", + "eslint": "^8.57.0", "http-proxy-middleware": "~2.0.6", "prettier": "^3.0.0", + "source-map-support": "^0.5.21", "ts-node": "~10.9.1", - "typescript": "~4.9.3" + "typescript": "~5.4.3" }, "optionalDependencies": { "@cypress/schematic": "^2.5.0", "@types/cypress": "^1.1.3", - "cypress": "^13.6.2", + "cypress": "^13.7.0", "cypress-fail-on-console-error": "~5.1.0", "cypress-wait-until": "^2.0.1", "mock-socket": "~9.3.1", @@ -75,121 +79,131 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, - "node_modules/@angular-devkit/architect": { - "version": "0.1602.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.0.tgz", - "integrity": "sha512-ZRmUTBeD+uGr605eOHnsovEn6f1mOBI+kxP64DRvagNweX5TN04s3iyQ8jmLSAHQD9ush31LFxv3dVNxv3ceXQ==", + "node_modules/@ampproject/remapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dependencies": { - "@angular-devkit/core": "16.2.0", + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1703.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1703.1.tgz", + "integrity": "sha512-vkfvURv7O+3fHMTE9K+yUEiFS0v4JNYKsDP0LE1ChH5Ocy0bJXGcH2Cyz2W8qdJGDG/tKe41VzvOLpu88Xv3zQ==", + "dependencies": { + "@angular-devkit/core": "17.3.1", "rxjs": "7.8.1" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@angular-devkit/build-angular": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.2.0.tgz", - "integrity": "sha512-miylwjOqvlKmYrzS84bjRaJrecZxOXH9xsPVvQE8VBe8UKePJjRAL6yyOqXUOGtzlch2YmT98RAnuni7y0FEAw==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.3.1.tgz", + "integrity": "sha512-e+hZvLVH5AvHCFbVtKRd5oJeFsEmjg7kK1V6hsVxH4YE2f2x399TSr+AGxwV+R3jnjZ67ujIeXXd0Uuf1RwcSg==", "dependencies": { - "@ampproject/remapping": "2.2.1", - "@angular-devkit/architect": "0.1602.0", - "@angular-devkit/build-webpack": "0.1602.0", - "@angular-devkit/core": "16.2.0", - "@babel/core": "7.22.9", - "@babel/generator": "7.22.9", + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1703.1", + "@angular-devkit/build-webpack": "0.1703.1", + "@angular-devkit/core": "17.3.1", + "@babel/core": "7.24.0", + "@babel/generator": "7.23.6", "@babel/helper-annotate-as-pure": "7.22.5", "@babel/helper-split-export-declaration": "7.22.6", - "@babel/plugin-proposal-async-generator-functions": "7.20.7", - "@babel/plugin-transform-async-to-generator": "7.22.5", - "@babel/plugin-transform-runtime": "7.22.9", - "@babel/preset-env": "7.22.9", - "@babel/runtime": "7.22.6", - "@babel/template": "7.22.5", + "@babel/plugin-transform-async-generator-functions": "7.23.9", + "@babel/plugin-transform-async-to-generator": "7.23.3", + "@babel/plugin-transform-runtime": "7.24.0", + "@babel/preset-env": "7.24.0", + "@babel/runtime": "7.24.0", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "16.2.0", - "@vitejs/plugin-basic-ssl": "1.0.1", + "@ngtools/webpack": "17.3.1", + "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", - "autoprefixer": "10.4.14", + "autoprefixer": "10.4.18", "babel-loader": "9.1.3", "babel-plugin-istanbul": "6.1.1", "browserslist": "^4.21.5", - "chokidar": "3.5.3", "copy-webpack-plugin": "11.0.0", - "critters": "0.0.20", - "css-loader": "6.8.1", - "esbuild-wasm": "0.18.17", - "fast-glob": "3.3.1", - "guess-parser": "0.4.22", - "https-proxy-agent": "5.0.1", - "inquirer": "8.2.4", - "jsonc-parser": "3.2.0", + "critters": "0.0.22", + "css-loader": "6.10.0", + "esbuild-wasm": "0.20.1", + "fast-glob": "3.3.2", + "http-proxy-middleware": "2.0.6", + "https-proxy-agent": "7.0.4", + "inquirer": "9.2.15", + "jsonc-parser": "3.2.1", "karma-source-map-support": "1.4.0", - "less": "4.1.3", + "less": "4.2.0", "less-loader": "11.1.0", "license-webpack-plugin": "4.0.2", "loader-utils": "3.2.1", - "magic-string": "0.30.1", - "mini-css-extract-plugin": "2.7.6", - "mrmime": "1.0.1", + "magic-string": "0.30.8", + "mini-css-extract-plugin": "2.8.1", + "mrmime": "2.0.0", "open": "8.4.2", "ora": "5.4.1", "parse5-html-rewriting-stream": "7.0.0", - "picomatch": "2.3.1", - "piscina": "4.0.0", - "postcss": "8.4.27", - "postcss-loader": "7.3.3", + "picomatch": "4.0.1", + "piscina": "4.4.0", + "postcss": "8.4.35", + "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.64.1", - "sass-loader": "13.3.2", - "semver": "7.5.4", - "source-map-loader": "4.0.1", + "sass": "1.71.1", + "sass-loader": "14.1.1", + "semver": "7.6.0", + "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.19.2", - "text-table": "0.2.0", + "terser": "5.29.1", "tree-kill": "1.2.2", - "tslib": "2.6.1", - "vite": "4.4.7", - "webpack": "5.88.2", + "tslib": "2.6.2", + "undici": "6.7.1", + "vite": "5.1.5", + "watchpack": "2.4.0", + "webpack": "5.90.3", "webpack-dev-middleware": "6.1.1", "webpack-dev-server": "4.15.1", - "webpack-merge": "5.9.0", + "webpack-merge": "5.10.0", "webpack-subresource-integrity": "5.1.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.18.17" + "esbuild": "0.20.1" }, "peerDependencies": { - "@angular/compiler-cli": "^16.0.0", - "@angular/localize": "^16.0.0", - "@angular/platform-server": "^16.0.0", - "@angular/service-worker": "^16.0.0", + "@angular/compiler-cli": "^17.0.0", + "@angular/localize": "^17.0.0", + "@angular/platform-server": "^17.0.0", + "@angular/service-worker": "^17.0.0", + "@web/test-runner": "^0.18.0", + "browser-sync": "^3.0.2", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "karma": "^6.3.0", - "ng-packagr": "^16.0.0", + "ng-packagr": "^17.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=4.9.3 <5.2" + "typescript": ">=5.2 <5.5" }, "peerDependenciesMeta": { "@angular/localize": { @@ -201,6 +215,12 @@ "@angular/service-worker": { "optional": true }, + "@web/test-runner": { + "optional": true + }, + "browser-sync": { + "optional": true + }, "jest": { "optional": true }, @@ -222,24 +242,24 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", + "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.0", + "@babel/parser": "^7.24.0", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "^6.3.1" }, "engines": { @@ -258,10 +278,25 @@ "semver": "bin/semver.js" } }, + "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/aix-ppc64": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.1.tgz", + "integrity": "sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/android-arm": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.17.tgz", - "integrity": "sha512-wHsmJG/dnL3OkpAcwbgoBTTMHVi4Uyou3F5mf58ZtmUyIKfcdA7TROav/6tCzET4A3QW2Q2FC+eFneMU+iyOxg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.1.tgz", + "integrity": "sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==", "cpu": [ "arm" ], @@ -274,9 +309,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/android-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.17.tgz", - "integrity": "sha512-9np+YYdNDed5+Jgr1TdWBsozZ85U1Oa3xW0c7TWqH0y2aGghXtZsuT8nYRbzOMcl0bXZXjOGbksoTtVOlWrRZg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz", + "integrity": "sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==", "cpu": [ "arm64" ], @@ -289,9 +324,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/android-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.17.tgz", - "integrity": "sha512-O+FeWB/+xya0aLg23hHEM2E3hbfwZzjqumKMSIqcHbNvDa+dza2D0yLuymRBQQnC34CWrsJUXyH2MG5VnLd6uw==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.1.tgz", + "integrity": "sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==", "cpu": [ "x64" ], @@ -304,9 +339,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/darwin-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.17.tgz", - "integrity": "sha512-M9uJ9VSB1oli2BE/dJs3zVr9kcCBBsE883prage1NWz6pBS++1oNn/7soPNS3+1DGj0FrkSvnED4Bmlu1VAE9g==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz", + "integrity": "sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==", "cpu": [ "arm64" ], @@ -319,9 +354,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/darwin-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.17.tgz", - "integrity": "sha512-XDre+J5YeIJDMfp3n0279DFNrGCXlxOuGsWIkRb1NThMZ0BsrWXoTg23Jer7fEXQ9Ye5QjrvXpxnhzl3bHtk0g==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz", + "integrity": "sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==", "cpu": [ "x64" ], @@ -334,9 +369,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.17.tgz", - "integrity": "sha512-cjTzGa3QlNfERa0+ptykyxs5A6FEUQQF0MuilYXYBGdBxD3vxJcKnzDlhDCa1VAJCmAxed6mYhA2KaJIbtiNuQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz", + "integrity": "sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==", "cpu": [ "arm64" ], @@ -349,9 +384,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/freebsd-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.17.tgz", - "integrity": "sha512-sOxEvR8d7V7Kw8QqzxWc7bFfnWnGdaFBut1dRUYtu+EIRXefBc/eIsiUiShnW0hM3FmQ5Zf27suDuHsKgZ5QrA==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz", + "integrity": "sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==", "cpu": [ "x64" ], @@ -364,9 +399,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-arm": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.17.tgz", - "integrity": "sha512-2d3Lw6wkwgSLC2fIvXKoMNGVaeY8qdN0IC3rfuVxJp89CRfA3e3VqWifGDfuakPmp90+ZirmTfye1n4ncjv2lg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz", + "integrity": "sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==", "cpu": [ "arm" ], @@ -379,9 +414,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.17.tgz", - "integrity": "sha512-c9w3tE7qA3CYWjT+M3BMbwMt+0JYOp3vCMKgVBrCl1nwjAlOMYzEo+gG7QaZ9AtqZFj5MbUc885wuBBmu6aADQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz", + "integrity": "sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w==", "cpu": [ "arm64" ], @@ -394,9 +429,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-ia32": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.17.tgz", - "integrity": "sha512-1DS9F966pn5pPnqXYz16dQqWIB0dmDfAQZd6jSSpiT9eX1NzKh07J6VKR3AoXXXEk6CqZMojiVDSZi1SlmKVdg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz", + "integrity": "sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==", "cpu": [ "ia32" ], @@ -409,9 +444,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-loong64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.17.tgz", - "integrity": "sha512-EvLsxCk6ZF0fpCB6w6eOI2Fc8KW5N6sHlIovNe8uOFObL2O+Mr0bflPHyHwLT6rwMg9r77WOAWb2FqCQrVnwFg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz", + "integrity": "sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==", "cpu": [ "loong64" ], @@ -424,9 +459,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-mips64el": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.17.tgz", - "integrity": "sha512-e0bIdHA5p6l+lwqTE36NAW5hHtw2tNRmHlGBygZC14QObsA3bD4C6sXLJjvnDIjSKhW1/0S3eDy+QmX/uZWEYQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz", + "integrity": "sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==", "cpu": [ "mips64el" ], @@ -439,9 +474,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-ppc64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.17.tgz", - "integrity": "sha512-BAAilJ0M5O2uMxHYGjFKn4nJKF6fNCdP1E0o5t5fvMYYzeIqy2JdAP88Az5LHt9qBoUa4tDaRpfWt21ep5/WqQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz", + "integrity": "sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==", "cpu": [ "ppc64" ], @@ -454,9 +489,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-riscv64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.17.tgz", - "integrity": "sha512-Wh/HW2MPnC3b8BqRSIme/9Zhab36PPH+3zam5pqGRH4pE+4xTrVLx2+XdGp6fVS3L2x+DrsIcsbMleex8fbE6g==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz", + "integrity": "sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==", "cpu": [ "riscv64" ], @@ -469,9 +504,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-s390x": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.17.tgz", - "integrity": "sha512-j/34jAl3ul3PNcK3pfI0NSlBANduT2UO5kZ7FCaK33XFv3chDhICLY8wJJWIhiQ+YNdQ9dxqQctRg2bvrMlYgg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz", + "integrity": "sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==", "cpu": [ "s390x" ], @@ -484,9 +519,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.17.tgz", - "integrity": "sha512-QM50vJ/y+8I60qEmFxMoxIx4de03pGo2HwxdBeFd4nMh364X6TIBZ6VQ5UQmPbQWUVWHWws5MmJXlHAXvJEmpQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz", + "integrity": "sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==", "cpu": [ "x64" ], @@ -499,9 +534,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/netbsd-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.17.tgz", - "integrity": "sha512-/jGlhWR7Sj9JPZHzXyyMZ1RFMkNPjC6QIAan0sDOtIo2TYk3tZn5UDrkE0XgsTQCxWTTOcMPf9p6Rh2hXtl5TQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz", + "integrity": "sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==", "cpu": [ "x64" ], @@ -514,9 +549,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/openbsd-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.17.tgz", - "integrity": "sha512-rSEeYaGgyGGf4qZM2NonMhMOP/5EHp4u9ehFiBrg7stH6BYEEjlkVREuDEcQ0LfIl53OXLxNbfuIj7mr5m29TA==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz", + "integrity": "sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==", "cpu": [ "x64" ], @@ -529,9 +564,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/sunos-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.17.tgz", - "integrity": "sha512-Y7ZBbkLqlSgn4+zot4KUNYst0bFoO68tRgI6mY2FIM+b7ZbyNVtNbDP5y8qlu4/knZZ73fgJDlXID+ohY5zt5g==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz", + "integrity": "sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==", "cpu": [ "x64" ], @@ -544,9 +579,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/win32-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.17.tgz", - "integrity": "sha512-bwPmTJsEQcbZk26oYpc4c/8PvTY3J5/QK8jM19DVlEsAB41M39aWovWoHtNm78sd6ip6prilxeHosPADXtEJFw==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz", + "integrity": "sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==", "cpu": [ "arm64" ], @@ -559,9 +594,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/win32-ia32": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.17.tgz", - "integrity": "sha512-H/XaPtPKli2MhW+3CQueo6Ni3Avggi6hP/YvgkEe1aSaxw+AeO8MFjq8DlgfTd9Iz4Yih3QCZI6YLMoyccnPRg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz", + "integrity": "sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==", "cpu": [ "ia32" ], @@ -574,9 +609,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/win32-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.17.tgz", - "integrity": "sha512-fGEb8f2BSA3CW7riJVurug65ACLuQAzKq0SSqkY2b2yHHH0MzDfbLyKIGzHwOI/gkHcxM/leuSW6D5w/LMNitA==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz", + "integrity": "sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==", "cpu": [ "x64" ], @@ -588,10 +623,15 @@ "node": ">=12" } }, + "node_modules/@angular-devkit/build-angular/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "node_modules/@angular-devkit/build-angular/node_modules/esbuild": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.17.tgz", - "integrity": "sha512-1GJtYnUxsJreHYA0Y+iQz2UEykonY66HNWOb0yXYZi9/kNrORUEHVg87eQsCtqh59PEJ5YVZJO98JHznMJSWjg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz", + "integrity": "sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==", "hasInstallScript": true, "optional": true, "bin": { @@ -601,43 +641,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.18.17", - "@esbuild/android-arm64": "0.18.17", - "@esbuild/android-x64": "0.18.17", - "@esbuild/darwin-arm64": "0.18.17", - "@esbuild/darwin-x64": "0.18.17", - "@esbuild/freebsd-arm64": "0.18.17", - "@esbuild/freebsd-x64": "0.18.17", - "@esbuild/linux-arm": "0.18.17", - "@esbuild/linux-arm64": "0.18.17", - "@esbuild/linux-ia32": "0.18.17", - "@esbuild/linux-loong64": "0.18.17", - "@esbuild/linux-mips64el": "0.18.17", - "@esbuild/linux-ppc64": "0.18.17", - "@esbuild/linux-riscv64": "0.18.17", - "@esbuild/linux-s390x": "0.18.17", - "@esbuild/linux-x64": "0.18.17", - "@esbuild/netbsd-x64": "0.18.17", - "@esbuild/openbsd-x64": "0.18.17", - "@esbuild/sunos-x64": "0.18.17", - "@esbuild/win32-arm64": "0.18.17", - "@esbuild/win32-ia32": "0.18.17", - "@esbuild/win32-x64": "0.18.17" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" + "@esbuild/aix-ppc64": "0.20.1", + "@esbuild/android-arm": "0.20.1", + "@esbuild/android-arm64": "0.20.1", + "@esbuild/android-x64": "0.20.1", + "@esbuild/darwin-arm64": "0.20.1", + "@esbuild/darwin-x64": "0.20.1", + "@esbuild/freebsd-arm64": "0.20.1", + "@esbuild/freebsd-x64": "0.20.1", + "@esbuild/linux-arm": "0.20.1", + "@esbuild/linux-arm64": "0.20.1", + "@esbuild/linux-ia32": "0.20.1", + "@esbuild/linux-loong64": "0.20.1", + "@esbuild/linux-mips64el": "0.20.1", + "@esbuild/linux-ppc64": "0.20.1", + "@esbuild/linux-riscv64": "0.20.1", + "@esbuild/linux-s390x": "0.20.1", + "@esbuild/linux-x64": "0.20.1", + "@esbuild/netbsd-x64": "0.20.1", + "@esbuild/openbsd-x64": "0.20.1", + "@esbuild/sunos-x64": "0.20.1", + "@esbuild/win32-arm64": "0.20.1", + "@esbuild/win32-ia32": "0.20.1", + "@esbuild/win32-x64": "0.20.1" } }, "node_modules/@angular-devkit/build-angular/node_modules/loader-utils": { @@ -648,16 +674,41 @@ "node": ">= 12.13.0" } }, - "node_modules/@angular-devkit/build-webpack": { - "version": "0.1602.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1602.0.tgz", - "integrity": "sha512-KdSr6iAcO30i/LIGL8mYi+d1buVXuDCp2dptzEJ4vxReOMFJca90KLwb+tVHEqqnDb0WkNfWm8Ii2QYh2FrNyA==", + "node_modules/@angular-devkit/build-angular/node_modules/picomatch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { - "@angular-devkit/architect": "0.1602.0", + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1703.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1703.1.tgz", + "integrity": "sha512-nVUzewX8RCzaEPQZ1JQpE42wpsYchKQwfXUSCkoUsuCMB2c6zuEz0Jt94nzJg3UjSEEV4ZqCH8v5MDOvB49Rlw==", + "dependencies": { + "@angular-devkit/architect": "0.1703.1", "rxjs": "7.8.1" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -667,18 +718,19 @@ } }, "node_modules/@angular-devkit/core": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.0.tgz", - "integrity": "sha512-l1k6Rqm3YM16BEn3CWyQKrk9xfu+2ux7Bw3oS+h1TO4/RoxO2PgHj8LLRh/WNrYVarhaqO7QZ5ePBkXNMkzJ1g==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.1.tgz", + "integrity": "sha512-EP7zwqBEaOPuBJwzKmh2abfgNFITGX178BOyTG6zTymeMzEbrvy2OdeQXSslkJ/RGLCpx60GT+0CFW7wGlQR6Q==", "dependencies": { "ajv": "8.12.0", "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", + "jsonc-parser": "3.2.1", + "picomatch": "4.0.1", "rxjs": "7.8.1", "source-map": "0.7.4" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -711,58 +763,69 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/@angular-devkit/core/node_modules/picomatch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular-devkit/schematics": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.0.tgz", - "integrity": "sha512-QMDJXPE0+YQJ9Ap3MMzb0v7rx6ZbBEokmHgpdIjN3eILYmbAdsSGE8HTV8NjS9nKmcyE9OGzFCMb7PFrDTlTAw==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.1.tgz", + "integrity": "sha512-c3tp5zC5zp6XpK9w8wJf3d4Dyw9BNbmg/VEoXtePGivp4hzks6zuMAFknNRwdK7roOlH0HyM5No4WUZHBFpOmw==", "dependencies": { - "@angular-devkit/core": "16.2.0", - "jsonc-parser": "3.2.0", - "magic-string": "0.30.1", + "@angular-devkit/core": "17.3.1", + "jsonc-parser": "3.2.1", + "magic-string": "0.30.8", "ora": "5.4.1", "rxjs": "7.8.1" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@angular/animations": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-16.2.2.tgz", - "integrity": "sha512-p0QefudkPGXjq9inZDrtW6WJrDcSeL+Nkc8lxubjg5fLQATKWKpsUBb+u2xEVu8OvWqj8BvrZUDnXYLyTdM4vw==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.3.1.tgz", + "integrity": "sha512-2TZ0M5J0IizhHpb404DeqArlv8Ki9BFz5ZUuET2uFROpKW8IMDCht8fSrn/DKHpjB9lvzPUhNFaRxNWEY6klnA==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "16.2.2" + "@angular/core": "17.3.1" } }, "node_modules/@angular/cli": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.0.tgz", - "integrity": "sha512-xT8vJOyw6Rc2364XDW2jHagLgKu7342ktd/lt+c0u6R+AB2XVFMePR7VceLohX9N/vRUsbQ0nVSZr+ru/hA+HA==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.3.1.tgz", + "integrity": "sha512-IVnnbRi53BZvZ3LE0PCfFefoB2uHlO1sHtilZf/xCpdV4E1Mkz0/hHln5CRHwAXErdSiY57VoMsF5tffxAfaBQ==", "dependencies": { - "@angular-devkit/architect": "0.1602.0", - "@angular-devkit/core": "16.2.0", - "@angular-devkit/schematics": "16.2.0", - "@schematics/angular": "16.2.0", + "@angular-devkit/architect": "0.1703.1", + "@angular-devkit/core": "17.3.1", + "@angular-devkit/schematics": "17.3.1", + "@schematics/angular": "17.3.1", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.3", - "ini": "4.1.1", - "inquirer": "8.2.4", - "jsonc-parser": "3.2.0", - "npm-package-arg": "10.1.0", - "npm-pick-manifest": "8.0.1", + "ini": "4.1.2", + "inquirer": "9.2.15", + "jsonc-parser": "3.2.1", + "npm-package-arg": "11.0.1", + "npm-pick-manifest": "9.0.0", "open": "8.4.2", "ora": "5.4.1", - "pacote": "15.2.0", - "resolve": "1.22.2", - "semver": "7.5.4", + "pacote": "17.0.6", + "resolve": "1.22.8", + "semver": "7.6.0", "symbol-observable": "4.0.0", "yargs": "17.7.2" }, @@ -770,7 +833,7 @@ "ng": "bin/ng.js" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } @@ -818,6 +881,20 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/@angular/cli/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@angular/cli/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -868,32 +945,32 @@ } }, "node_modules/@angular/common": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-16.2.2.tgz", - "integrity": "sha512-2ww8/heDHkfJEBwjakbQeleq610ljcvytNs6ZN1xiXib060xMP+xx17Oa9I3onhi369JsKCHkMR5Qs2U5af1uA==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-17.3.1.tgz", + "integrity": "sha512-HyUTJ4RxhE3bOmFRV6Fv2y01ixbrUb8Hd4MxPm8REbNMGKsWCfXhR3FfxFL18Sc03SAF+o0Md0wwekjFKTNKfQ==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "16.2.2", + "@angular/core": "17.3.1", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-16.2.2.tgz", - "integrity": "sha512-0X9i5NsqjX++0gmFy0fy2Uc5dHJMxDq6Yu/j1L3RdbvycL1GW+P8GgPfIvD/+v/YiDqpOHQswQXLbkcHw1+svA==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.3.1.tgz", + "integrity": "sha512-8qqlWPGZEyD2FY5losOW3Aocro+lFysPDzsf0LHgQUM6Ub1b+pq4jUOjH6w0vzaxG3TfxkgzOQ9aNdWtSV67Rg==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "16.2.2" + "@angular/core": "17.3.1" }, "peerDependenciesMeta": { "@angular/core": { @@ -902,15 +979,15 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-16.2.2.tgz", - "integrity": "sha512-+4i7o0yBc6xSljO8rdYL1G9AiZr2OW5dJAHfPuO21yNhp9BjIJ/TW+Sw1+o/WH4Gnim9adtnonL18UM+vuYeXg==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.3.1.tgz", + "integrity": "sha512-xLV9KU+zOpe57/2rQ59ku21EaStNpLSlR9+qkDYf8JR09fB+W9vY3UYbpi5RjHxAFIZBM5D9SFQjjll8rch26g==", "dependencies": { - "@babel/core": "7.22.5", + "@babel/core": "7.23.9", "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^3.0.0", "convert-source-map": "^1.5.1", - "reflect-metadata": "^0.1.2", + "reflect-metadata": "^0.2.0", "semver": "^7.0.0", "tslib": "^2.3.0", "yargs": "^17.2.1" @@ -921,11 +998,11 @@ "ngcc": "bundles/ngcc/index.js" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/compiler": "16.2.2", - "typescript": ">=4.9.3 <5.2" + "@angular/compiler": "17.3.1", + "typescript": ">=5.2 <5.5" } }, "node_modules/@angular/compiler-cli/node_modules/ansi-styles": { @@ -1018,53 +1095,54 @@ } }, "node_modules/@angular/core": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-16.2.2.tgz", - "integrity": "sha512-l6nJlppguroov7eByBIpbxn/mEPcQrL//Ru1TSPzTtXOLR1p41VqPMaeJXj7xYVx7im57YLTDPAjhtLzkUT/Ow==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-17.3.1.tgz", + "integrity": "sha512-Qf3/sgkXS1LHwOTtqAVYprySrn0YpPIZqerPc0tK+hyQfwAz5BQlpcBhbH8RWKlfCY8eO0cqo/j0+e8DQOgYfg==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.13.0" + "zone.js": "~0.14.0" } }, "node_modules/@angular/forms": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-16.2.2.tgz", - "integrity": "sha512-Q3GmOCLSD5BXSjvlLkMsJLXWXb4SO0gA2Aya8JaG1y0doQT/CdGcYXrsCrCT3ot13wqp0HdGQ/ATNd0cNjmz2A==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.3.1.tgz", + "integrity": "sha512-HndsO90k67sFHzd+sII+rhAUksffBvquFuAUCc6QR9WVjILxVg2fY7oBidgS1gKNqu0mptPG0GvuORnaW/0gSg==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "16.2.2", - "@angular/core": "16.2.2", - "@angular/platform-browser": "16.2.2", + "@angular/common": "17.3.1", + "@angular/core": "17.3.1", + "@angular/platform-browser": "17.3.1", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-16.1.5.tgz", - "integrity": "sha512-Zhuoy3KB35fvD52Wg85emmmK38t2oPLHhKYV3pC1WegJunopyF4FkIE1hGqXZyk4VA29QgOT3WC1315FbUiV4Q==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-17.3.1.tgz", + "integrity": "sha512-awC+KHwIRXZ7biQz0Q7q+UZuuyeWHcxjxyQtvv0n1jwwyRpUo8WAXcduKRxl/wMOrxfZkB/tpGcd1/Eeql9CCw==", "dev": true, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" } }, "node_modules/@angular/localize": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-16.2.2.tgz", - "integrity": "sha512-6WO8icVzOGAjZd0Zm4mXisg1ljhmB1+UFSjUdHWrXd0QxAKKhHuI2P91v8J+5j1wl27JIKzTVA7+/gnNQMmGsw==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-17.3.1.tgz", + "integrity": "sha512-ma8PD+DWv68OKgvbmxw7rVohT5HvIYgbmPnVg8lyEz/YkUa9lua0zzrgA+3HUComqv16oVrIaQr00oWxn/9lXQ==", "dependencies": { - "@babel/core": "7.22.5", - "fast-glob": "3.3.0", + "@babel/core": "7.23.9", + "@types/babel__core": "7.20.5", + "fast-glob": "3.3.2", "yargs": "^17.2.1" }, "bin": { @@ -1073,11 +1151,11 @@ "localize-translate": "tools/bundles/src/translate/cli.js" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/compiler": "16.2.2", - "@angular/compiler-cli": "16.2.2" + "@angular/compiler": "17.3.1", + "@angular/compiler-cli": "17.3.1" } }, "node_modules/@angular/localize/node_modules/ansi-styles": { @@ -1170,19 +1248,19 @@ } }, "node_modules/@angular/platform-browser": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-16.2.2.tgz", - "integrity": "sha512-9RwUiHYCAmEirXqwWL/rPfXHMkU9PnpGinok6tmHF8agAmJs1kMWZedxG0GnreTzpTlBu/dI/4v6VDfR9S/D6Q==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.3.1.tgz", + "integrity": "sha512-8ABAL8PElSGzkIparVwifsU0NSu0DdqnWYw9YvLhhZQ6lOuWbG+dTUo/DXzmWhA6ezQWJGNakEZPJJytFIIy+A==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/animations": "16.2.2", - "@angular/common": "16.2.2", - "@angular/core": "16.2.2" + "@angular/animations": "17.3.1", + "@angular/common": "17.3.1", + "@angular/core": "17.3.1" }, "peerDependenciesMeta": { "@angular/animations": { @@ -1191,103 +1269,111 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-16.2.2.tgz", - "integrity": "sha512-EOGDZ+oABB/aNiBR//wxc6McycjF99/9ds74Q6WoHiNy8CYkzH3plr5pHoy4zkriSyqzoETg2tCu7jSiiMbjRg==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.3.1.tgz", + "integrity": "sha512-ACW/npNaDxUNQtEomjjv/KIBY8jHEinePff5qosnAxLE0IpA4qE9eDp36zG35xoJqrPJPYjXbZCBRqqrzM7U7Q==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "16.2.2", - "@angular/compiler": "16.2.2", - "@angular/core": "16.2.2", - "@angular/platform-browser": "16.2.2" + "@angular/common": "17.3.1", + "@angular/compiler": "17.3.1", + "@angular/core": "17.3.1", + "@angular/platform-browser": "17.3.1" } }, "node_modules/@angular/platform-server": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-16.2.2.tgz", - "integrity": "sha512-mvJsmPJMG6GzzGvOMSkjPgE9zHpuWkFfaO6HTSj0GvxyvxjrlQKsVW87gxEgqfTdhN4JbgmMA4eC9x8625VPyg==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-17.3.1.tgz", + "integrity": "sha512-yC1WgUquIac8qFCPMLjRio2ViR3XHexlXKlZpFhqpWAFPsWSHjoCHTEW+KTUFZmOPhUEFR2W8fWOChur8mjthw==", "dependencies": { "tslib": "^2.3.0", "xhr2": "^0.2.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/animations": "16.2.2", - "@angular/common": "16.2.2", - "@angular/compiler": "16.2.2", - "@angular/core": "16.2.2", - "@angular/platform-browser": "16.2.2" + "@angular/animations": "17.3.1", + "@angular/common": "17.3.1", + "@angular/compiler": "17.3.1", + "@angular/core": "17.3.1", + "@angular/platform-browser": "17.3.1" } }, "node_modules/@angular/router": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-16.2.2.tgz", - "integrity": "sha512-r4KMVUVEWqjOZK0ZUsY8jRqscseGvgcigcikvYJwfxPqtCGYY7RoVAFY7HUtmXC0GAv1aIybK5o/MKTLaecD5Q==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-17.3.1.tgz", + "integrity": "sha512-H6H7lY9i5Ppu0SFwwpeWqJbCFw8cILOj8Rd1+AGoCN5m3ivPtjD2Ltz62PI2zZkqx+WhQdk19l61Wm3oRqg70A==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "16.2.2", - "@angular/core": "16.2.2", - "@angular/platform-browser": "16.2.2", + "@angular/common": "17.3.1", + "@angular/core": "17.3.1", + "@angular/platform-browser": "17.3.1", "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@assemblyscript/loader": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", - "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==" + "node_modules/@angular/ssr": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/ssr/-/ssr-17.3.1.tgz", + "integrity": "sha512-K/2FGTSC3xJOUJEvqRNVhhhoNGMDFMXUKJqnLXe6cNE8xNkOzO52tWTc0ZZr4ZYvFSwtVMuFY4E65HUxbhGTvA==", + "dependencies": { + "critters": "0.0.22", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^17.0.0", + "@angular/core": "^17.0.0" + } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz", + "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", - "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", + "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helpers": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.9", + "@babel/parser": "^7.23.9", + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -1297,6 +1383,11 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -1306,11 +1397,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", - "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dependencies": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -1345,32 +1436,29 @@ } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.10.tgz", - "integrity": "sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", "dependencies": { - "@babel/types": "^7.22.10" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", - "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { @@ -1395,16 +1483,16 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz", - "integrity": "sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz", + "integrity": "sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-replace-supers": "^7.24.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" @@ -1425,9 +1513,9 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz", - "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", @@ -1449,9 +1537,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", - "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.1.tgz", + "integrity": "sha512-o7SDgTJuvx5vLKD6SFvkydkSMBvahDKGiNJzG22IZYXhiqoe9efY7zocICBgzHV4IRg5wdgl2nEL/tulKIEIbA==", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -1483,19 +1571,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-function-name/node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-hoist-variables": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", @@ -1508,37 +1583,37 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", - "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -1559,21 +1634,21 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", - "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-wrap-function": "^7.22.9" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -1583,12 +1658,12 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", - "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz", + "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5" }, "engines": { @@ -1632,9 +1707,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "engines": { "node": ">=6.9.0" } @@ -1648,56 +1723,57 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz", - "integrity": "sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", "dependencies": { "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", - "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz", + "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", + "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1706,11 +1782,11 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", - "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz", + "integrity": "sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1720,13 +1796,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", - "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz", + "integrity": "sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.5" + "@babel/plugin-transform-optional-chaining": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -1735,21 +1811,19 @@ "@babel/core": "^7.13.0" } }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz", + "integrity": "sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { @@ -1763,21 +1837,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -1837,11 +1896,11 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", - "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz", + "integrity": "sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1851,11 +1910,11 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", - "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz", + "integrity": "sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1996,11 +2055,11 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", - "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz", + "integrity": "sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2010,13 +2069,13 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz", - "integrity": "sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", + "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.9", + "@babel/helper-remap-async-to-generator": "^7.22.20", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { @@ -2027,13 +2086,13 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", - "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", + "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", "dependencies": { - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5" + "@babel/helper-remap-async-to-generator": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -2043,11 +2102,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", - "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz", + "integrity": "sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2057,11 +2116,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz", - "integrity": "sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.1.tgz", + "integrity": "sha512-h71T2QQvDgM2SmT29UYU6ozjMlAt7s7CSs5Hvy8f8cf/GM/Z4a2zMfN+fjVGaieeCrXR3EdQl6C4gQG+OgmbKw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2071,12 +2130,12 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", - "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz", + "integrity": "sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2086,12 +2145,12 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", - "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.1.tgz", + "integrity": "sha512-FUHlKCn6J3ERiu8Dv+4eoz7w8+kFLSyeVG4vDAikwADGjUCoHw/JHokyGtr8OR4UjpwPVivyF+h8Q5iv/JmrtA==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -2102,17 +2161,16 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", - "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz", + "integrity": "sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1", "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" }, @@ -2124,12 +2182,12 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", - "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz", + "integrity": "sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/template": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2139,11 +2197,11 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz", - "integrity": "sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz", + "integrity": "sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2153,12 +2211,12 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", - "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz", + "integrity": "sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2168,11 +2226,11 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", - "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz", + "integrity": "sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2182,11 +2240,11 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", - "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz", + "integrity": "sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { @@ -2197,12 +2255,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", - "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz", + "integrity": "sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw==", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2212,11 +2270,11 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", - "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz", + "integrity": "sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { @@ -2227,11 +2285,12 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", - "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz", + "integrity": "sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2241,13 +2300,13 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", - "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz", + "integrity": "sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==", "dependencies": { - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2257,11 +2316,11 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", - "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz", + "integrity": "sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { @@ -2272,11 +2331,11 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", - "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz", + "integrity": "sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2286,11 +2345,11 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", - "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz", + "integrity": "sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -2301,11 +2360,11 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", - "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz", + "integrity": "sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2315,12 +2374,12 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", - "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz", + "integrity": "sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ==", "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2330,12 +2389,12 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", - "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz", + "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==", "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-simple-access": "^7.22.5" }, "engines": { @@ -2346,14 +2405,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", - "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz", + "integrity": "sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA==", "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -2363,12 +2422,12 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", - "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz", + "integrity": "sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg==", "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2393,11 +2452,11 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", - "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz", + "integrity": "sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2407,11 +2466,11 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", - "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz", + "integrity": "sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { @@ -2422,11 +2481,11 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", - "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz", + "integrity": "sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { @@ -2437,15 +2496,14 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", - "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.1.tgz", + "integrity": "sha512-XjD5f0YqOtebto4HGISLNfiNMTTs6tbkFf2TOqJlYKYmbo+mN9Dnpl4SRoofiziuOWMIyq3sZEUqLo3hLITFEA==", "dependencies": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.5" + "@babel/plugin-transform-parameters": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -2455,12 +2513,12 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", - "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz", + "integrity": "sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -2470,11 +2528,11 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", - "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz", + "integrity": "sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { @@ -2485,11 +2543,11 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz", - "integrity": "sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz", + "integrity": "sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, @@ -2501,11 +2559,11 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", - "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz", + "integrity": "sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2515,12 +2573,12 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", - "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz", + "integrity": "sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2530,13 +2588,13 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", - "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.1.tgz", + "integrity": "sha512-pTHxDVa0BpUbvAgX3Gat+7cSciXqUcY9j2VZKTbSB6+VQGpNgNO9ailxTGHSXlqOnX1Hcx1Enme2+yv7VqP9bg==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -2547,11 +2605,11 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", - "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz", + "integrity": "sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2561,11 +2619,11 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", - "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz", + "integrity": "sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "regenerator-transform": "^0.15.2" }, "engines": { @@ -2576,11 +2634,11 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", - "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz", + "integrity": "sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2590,15 +2648,15 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz", - "integrity": "sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.0.tgz", + "integrity": "sha512-zc0GA5IitLKJrSfXlXmp8KDqLrnGECK7YRfQBmEKg1NmBOQ7e+KuclBEKJgzifQeUYLdNiAw4B4bjyvzWVLiSA==", "dependencies": { - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.4", - "babel-plugin-polyfill-corejs3": "^0.8.2", - "babel-plugin-polyfill-regenerator": "^0.5.1", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0", + "babel-plugin-polyfill-corejs2": "^0.4.8", + "babel-plugin-polyfill-corejs3": "^0.9.0", + "babel-plugin-polyfill-regenerator": "^0.5.5", "semver": "^6.3.1" }, "engines": { @@ -2617,11 +2675,11 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", - "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz", + "integrity": "sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2631,11 +2689,11 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", - "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz", + "integrity": "sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { @@ -2646,11 +2704,11 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", - "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz", + "integrity": "sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2660,11 +2718,11 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", - "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz", + "integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2674,11 +2732,11 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", - "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz", + "integrity": "sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2688,11 +2746,11 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", - "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz", + "integrity": "sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2702,12 +2760,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", - "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz", + "integrity": "sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2717,12 +2775,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", - "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz", + "integrity": "sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2732,12 +2790,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", - "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz", + "integrity": "sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2747,24 +2805,25 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.9.tgz", - "integrity": "sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.0.tgz", + "integrity": "sha512-ZxPEzV9IgvGn73iK0E6VB9/95Nd7aMFpbE0l8KQFDG70cOV9IxRP7Y2FUPmlK0v6ImlLqYX50iuZ3ZTVhOF2lA==", "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/compat-data": "^7.23.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.22.5", - "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-assertions": "^7.23.3", + "@babel/plugin-syntax-import-attributes": "^7.23.3", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -2776,59 +2835,58 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.7", - "@babel/plugin-transform-async-to-generator": "^7.22.5", - "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.22.5", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.5", - "@babel/plugin-transform-classes": "^7.22.6", - "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.5", - "@babel/plugin-transform-dotall-regex": "^7.22.5", - "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.5", - "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.5", - "@babel/plugin-transform-for-of": "^7.22.5", - "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.5", - "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", - "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.5", - "@babel/plugin-transform-modules-systemjs": "^7.22.5", - "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-arrow-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.9", + "@babel/plugin-transform-async-to-generator": "^7.23.3", + "@babel/plugin-transform-block-scoped-functions": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.4", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.4", + "@babel/plugin-transform-classes": "^7.23.8", + "@babel/plugin-transform-computed-properties": "^7.23.3", + "@babel/plugin-transform-destructuring": "^7.23.3", + "@babel/plugin-transform-dotall-regex": "^7.23.3", + "@babel/plugin-transform-duplicate-keys": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.4", + "@babel/plugin-transform-exponentiation-operator": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-for-of": "^7.23.6", + "@babel/plugin-transform-function-name": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.4", + "@babel/plugin-transform-literals": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-member-expression-literals": "^7.23.3", + "@babel/plugin-transform-modules-amd": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.9", + "@babel/plugin-transform-modules-umd": "^7.23.3", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", - "@babel/plugin-transform-numeric-separator": "^7.22.5", - "@babel/plugin-transform-object-rest-spread": "^7.22.5", - "@babel/plugin-transform-object-super": "^7.22.5", - "@babel/plugin-transform-optional-catch-binding": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.6", - "@babel/plugin-transform-parameters": "^7.22.5", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.5", - "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.5", - "@babel/plugin-transform-reserved-words": "^7.22.5", - "@babel/plugin-transform-shorthand-properties": "^7.22.5", - "@babel/plugin-transform-spread": "^7.22.5", - "@babel/plugin-transform-sticky-regex": "^7.22.5", - "@babel/plugin-transform-template-literals": "^7.22.5", - "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.5", - "@babel/plugin-transform-unicode-property-regex": "^7.22.5", - "@babel/plugin-transform-unicode-regex": "^7.22.5", - "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.4", - "babel-plugin-polyfill-corejs3": "^0.8.2", - "babel-plugin-polyfill-regenerator": "^0.5.1", + "@babel/plugin-transform-new-target": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-numeric-separator": "^7.23.4", + "@babel/plugin-transform-object-rest-spread": "^7.24.0", + "@babel/plugin-transform-object-super": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-parameters": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", + "@babel/plugin-transform-property-literals": "^7.23.3", + "@babel/plugin-transform-regenerator": "^7.23.3", + "@babel/plugin-transform-reserved-words": "^7.23.3", + "@babel/plugin-transform-shorthand-properties": "^7.23.3", + "@babel/plugin-transform-spread": "^7.23.3", + "@babel/plugin-transform-sticky-regex": "^7.23.3", + "@babel/plugin-transform-template-literals": "^7.23.3", + "@babel/plugin-transform-typeof-symbol": "^7.23.3", + "@babel/plugin-transform-unicode-escapes": "^7.23.3", + "@babel/plugin-transform-unicode-property-regex": "^7.23.3", + "@babel/plugin-transform-unicode-regex": "^7.23.3", + "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.8", + "babel-plugin-polyfill-corejs3": "^0.9.0", + "babel-plugin-polyfill-regenerator": "^0.5.5", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, @@ -2848,13 +2906,11 @@ } }, "node_modules/@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, @@ -2868,43 +2924,43 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", - "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", + "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -2912,13 +2968,13 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz", + "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==", "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -2926,20 +2982,20 @@ } }, "node_modules/@babel/traverse/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, @@ -2947,6 +3003,89 @@ "node": ">=6.9.0" } }, + "node_modules/@browserify/envify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@browserify/envify/-/envify-6.0.0.tgz", + "integrity": "sha512-ovxHR0KTsRCyMNwD7MGV0+VCU1sT6Ds+itC4DaQHM41eUId+w5Jd0qlhLVoDkkIVBnkY3BAAM8yb2QfpBlHkPw==", + "dependencies": { + "acorn-node": "^2.0.1", + "dash-ast": "^2.0.1", + "multisplice": "^1.0.0", + "through2": "^4.0.2" + }, + "bin": { + "envify": "bin/envify" + } + }, + "node_modules/@browserify/envify/node_modules/acorn-node": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-2.0.1.tgz", + "integrity": "sha512-VLR5sHqjk+8c5hrKeP2fWaIHb8eewsoxnZ8r2qpwRHXMHuC7KyOPflnOx9dLssVQUurzJ7rO0OzIFjHcndafWw==", + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/@browserify/envify/node_modules/dash-ast": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-2.0.1.tgz", + "integrity": "sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ==" + }, + "node_modules/@browserify/envify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@browserify/envify/node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/@browserify/uglifyify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@browserify/uglifyify/-/uglifyify-6.0.0.tgz", + "integrity": "sha512-48M2a3novsgKhUSo/B3ja10awc7unliK1HfW6aYBJdLFQj3wXDx9BBJVfj6MVYERSQVEVjNHQQ7IK89h4MpCLw==", + "dependencies": { + "convert-source-map": "^1.9.0", + "minimatch": "^3.0.2", + "terser": "^5.15.1", + "through2": "^4.0.2", + "xtend": "^4.0.1" + } + }, + "node_modules/@browserify/uglifyify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@browserify/uglifyify/node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dependencies": { + "readable-stream": "3" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -3056,10 +3195,25 @@ "node": ">=10.0.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", "cpu": [ "arm" ], @@ -3072,9 +3226,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", "cpu": [ "arm64" ], @@ -3087,9 +3241,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", "cpu": [ "x64" ], @@ -3102,9 +3256,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", "cpu": [ "arm64" ], @@ -3117,9 +3271,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", "cpu": [ "x64" ], @@ -3132,9 +3286,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", "cpu": [ "arm64" ], @@ -3147,9 +3301,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", "cpu": [ "x64" ], @@ -3162,9 +3316,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", "cpu": [ "arm" ], @@ -3177,9 +3331,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", "cpu": [ "arm64" ], @@ -3192,9 +3346,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", "cpu": [ "ia32" ], @@ -3207,9 +3361,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", "cpu": [ "loong64" ], @@ -3222,9 +3376,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", "cpu": [ "mips64el" ], @@ -3237,9 +3391,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", "cpu": [ "ppc64" ], @@ -3252,9 +3406,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", "cpu": [ "riscv64" ], @@ -3267,9 +3421,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", "cpu": [ "s390x" ], @@ -3282,9 +3436,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", "cpu": [ "x64" ], @@ -3297,9 +3451,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", "cpu": [ "x64" ], @@ -3312,9 +3466,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", "cpu": [ "x64" ], @@ -3327,9 +3481,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", "cpu": [ "x64" ], @@ -3342,9 +3496,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", "cpu": [ "arm64" ], @@ -3357,9 +3511,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", "cpu": [ "ia32" ], @@ -3372,9 +3526,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", "cpu": [ "x64" ], @@ -3386,15 +3540,39 @@ "node": ">=12" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -3416,9 +3594,9 @@ "dev": true }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3454,15 +3632,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@fortawesome/angular-fontawesome": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.13.0.tgz", - "integrity": "sha512-gzSPRdveOXNO7NIiMgTyB46aiHG0i98KinnAEqHXi8qzraM/kCcHn/0y3f4MhemX6kftwsFli0IU8RyHmtXlSQ==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.14.1.tgz", + "integrity": "sha512-Yb5HLiEOAxjSLEcaOM51CKIrzdfvoDafXVJERm9vufxfZkVZPZJgrZRgqwLVpejgq4/Ez6TqHZ6SqmJwdtRF6g==", "dependencies": { - "tslib": "^2.4.1" + "tslib": "^2.6.2" }, "peerDependencies": { - "@angular/core": "^16.0.0", + "@angular/core": "^17.0.0", "@fortawesome/fontawesome-svg-core": "~1.2.27 || ~1.3.0-beta2 || ^6.1.0" } }, @@ -3517,35 +3704,6 @@ "ms": "^2.1.1" } }, - "node_modules/@goto-bus-stop/envify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@goto-bus-stop/envify/-/envify-5.0.0.tgz", - "integrity": "sha512-xAnxuDWmwQxO8CgVuPTxKuNsKDfwyXXTyAabG4sNoK59H/ZMC7BHxTA/4ehtinsxbcH7/9L65F5VhyNdQfUyqA==", - "dependencies": { - "acorn-node": "^2.0.1", - "dash-ast": "^2.0.1", - "multisplice": "^1.0.0", - "through2": "^2.0.5" - }, - "bin": { - "envify": "bin/envify" - } - }, - "node_modules/@goto-bus-stop/envify/node_modules/acorn-node": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-2.0.1.tgz", - "integrity": "sha512-VLR5sHqjk+8c5hrKeP2fWaIHb8eewsoxnZ8r2qpwRHXMHuC7KyOPflnOx9dLssVQUurzJ7rO0OzIFjHcndafWw==", - "dependencies": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "node_modules/@goto-bus-stop/envify/node_modules/dash-ast": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-2.0.1.tgz", - "integrity": "sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ==" - }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -3562,13 +3720,13 @@ } }, "node_modules/@humanwhocodes/config-array": { - "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==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -3589,9 +3747,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -3707,18 +3865,27 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", @@ -3728,9 +3895,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "engines": { "node": ">=6.0.0" } @@ -3763,6 +3930,17 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "node_modules/@ljharb/through": { + "version": "2.3.13", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", + "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/@mempool/mempool.js": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@mempool/mempool.js/-/mempool.js-2.3.0.tgz", @@ -3793,33 +3971,33 @@ } }, "node_modules/@ng-bootstrap/ng-bootstrap": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-15.1.0.tgz", - "integrity": "sha512-4Z/sXYcAq22D15jtlnZV7qztuSnlSlOgO7EVp6rJ8dyGi3CPzX9PqMfetoM6K5sKQTiSW8IfsbdXCWN7rnzxWQ==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-16.0.0.tgz", + "integrity": "sha512-+FJ3e6cX9DW2t7021Ji3oz433rk3+4jLfqzU+Jyx6/vJz1dIOaML3EAY6lYuW4TLiXgMPOMvs6KzPFALGh4Lag==", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/common": "^16.0.0", - "@angular/core": "^16.0.0", - "@angular/forms": "^16.0.0", - "@angular/localize": "^16.0.0", - "@popperjs/core": "^2.11.6", + "@angular/common": "^17.0.0", + "@angular/core": "^17.0.0", + "@angular/forms": "^17.0.0", + "@angular/localize": "^17.0.0", + "@popperjs/core": "^2.11.8", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@ngtools/webpack": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-16.2.0.tgz", - "integrity": "sha512-c9jv4r7GnLTpnPOeF+a9yAm/3/2wwl9lMBU32i9hlY+q/Hqde4PiL95bUOLnRRL1I64DV7BFTlSZqSPgDpFXZQ==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.3.1.tgz", + "integrity": "sha512-6qRYFN6DqogZK0ZFrSlhg1OsIWm3lL3m+/Ixoj6/MLLjDBrTtHqmI93vg6P1EKYTH4fWChL7jtv7iS/LSZubgw==", "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "peerDependencies": { - "@angular/compiler-cli": "^16.0.0", - "typescript": ">=4.9.3 <5.2", + "@angular/compiler-cli": "^17.0.0", + "typescript": ">=5.2 <5.5", "webpack": "^5.54.0" } }, @@ -3855,6 +4033,29 @@ "node": ">= 8" } }, + "node_modules/@npmcli/agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.1.tgz", + "integrity": "sha512-H4FrOVtNyWC8MUwL3UfjOsAihHvT1Pe8POj3JvjXhSTJipsZMtgUALCT4mGyYZNxymkUfOw3PUj6dE4QPp6osQ==", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/@npmcli/fs": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", @@ -3867,43 +4068,51 @@ } }, "node_modules/@npmcli/git": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz", - "integrity": "sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.4.tgz", + "integrity": "sha512-nr6/WezNzuYUppzXRaYu/W4aT5rLxdXqEFupbh6e/ovlYFQ8hpu1UUPV3Ir/YTl+74iXl2ZOMlGzudh9ZPUchQ==", "dependencies": { - "@npmcli/promise-spawn": "^6.0.0", - "lru-cache": "^7.4.4", - "npm-pick-manifest": "^8.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", "proc-log": "^3.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", - "which": "^3.0.0" + "which": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "engines": { + "node": ">=16" } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "engines": { - "node": ">=12" + "node": "14 || >=16.14" } }, "node_modules/@npmcli/git/node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/@npmcli/installed-package-contents": { @@ -3929,58 +4138,142 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@npmcli/promise-spawn": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", - "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", + "node_modules/@npmcli/package-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.0.0.tgz", + "integrity": "sha512-OI2zdYBLhQ7kpNPaJxiflofYIpkNLi+lnGdzqUOfRmCF3r2l1nadcjtCYMJKv/Utm/ZtlffaUuTiAktPHbc17g==", "dependencies": { - "which": "^3.0.0" + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.5.3" }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/package-json/node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/@npmcli/package-json/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.1.tgz", + "integrity": "sha512-P4KkF9jX3y+7yFUxgcUdDtLy+t4OlDGuEBLNs57AZsfSfg+uV6MLndqGpnl4831ggaEdXwR50XFoZP4VFtHolg==", + "dependencies": { + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "engines": { + "node": ">=16" + } + }, "node_modules/@npmcli/promise-spawn/node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/@npmcli/run-script": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.2.tgz", - "integrity": "sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-7.0.4.tgz", + "integrity": "sha512-9ApYM/3+rBt9V80aYg6tZfzj3UWdiYyCt7gJUD1VJKvWF5nwKDSICXbYIQbspFTq6TOpbsEtIC0LArB8d9PFmg==", "dependencies": { "@npmcli/node-gyp": "^3.0.0", - "@npmcli/promise-spawn": "^6.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^3.0.0", - "which": "^3.0.0" + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "which": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "engines": { + "node": ">=16" } }, "node_modules/@npmcli/run-script/node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/@pkgjs/parseargs": { @@ -3993,26 +4286,182 @@ } }, "node_modules/@popperjs/core": { - "version": "2.11.6", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", - "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@schematics/angular": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.0.tgz", - "integrity": "sha512-Ib0/ZCkjWt7a5p3209JVwEWwf41v03K3ylvlxLIEo1ZGijAZAlrBj4GrA5YQ+TmPm2hRyt+owss7x91/x+i0Gw==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.3.1.tgz", + "integrity": "sha512-B3TkpjDjZhxX+tUc2ySEHU33x82Da0sssq/EMqQ1PQBHeRMa0ecyCeExjFEs2y57ZuC+QeVTaUt+TW45lLSjQw==", "dependencies": { - "@angular-devkit/core": "16.2.0", - "@angular-devkit/schematics": "16.2.0", - "jsonc-parser": "3.2.0" + "@angular-devkit/core": "17.3.1", + "@angular-devkit/schematics": "17.3.1", + "jsonc-parser": "3.2.1" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } @@ -4038,24 +4487,70 @@ "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", "optional": true }, + "node_modules/@sigstore/bundle": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.2.0.tgz", + "integrity": "sha512-5VI58qgNs76RDrwXNhpmyN/jKpq9evV/7f1XrcqcAfvxDl5SeVY/I5Rmfe96ULAV7/FK5dge9RBKGBJPhL1WsQ==", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.0.0.tgz", + "integrity": "sha512-dW2qjbWLRKGu6MIDUTBuJwXCnR8zivcSpf5inUzk7y84zqy/dji0/uahppoIgMoKeR+6pUZucrwHfkQQtiG9Rw==", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, "node_modules/@sigstore/protobuf-specs": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz", - "integrity": "sha512-a31EnjuIDSX8IXBUib3cYLDRlPMU36AWX4xS8ysLaNu4ZzUesDiPt83pgrW2X1YLMe5L2HbDyaKK5BrL4cNKaQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.0.tgz", + "integrity": "sha512-zxiQ66JFOjVvP9hbhGj/F/qNdsZfkGb/dVXSanNRNuAzMlr4MC95voPUBX8//ZNnmv3uSYzdfR/JSkrgvZTGxA==", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@sigstore/tuf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.2.tgz", - "integrity": "sha512-vjwcYePJzM01Ha6oWWZ9gNcdIgnzyFxfqfWzph483DPJTH8Tb7f7bQRRll3CYVkyH56j0AgcPAcl6Vg95DPF+Q==", + "node_modules/@sigstore/sign": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.2.3.tgz", + "integrity": "sha512-LqlA+ffyN02yC7RKszCdMTS6bldZnIodiox+IkT8B2f8oRYXCB3LQ9roXeiEL21m64CVH1wyveYAORfD65WoSw==", "dependencies": { - "@sigstore/protobuf-specs": "^0.1.0", - "tuf-js": "^1.1.7" + "@sigstore/bundle": "^2.2.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.0", + "make-fetch-happen": "^13.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.1.tgz", + "integrity": "sha512-9Iv40z652td/QbV0o5n/x25H9w6IYRt2pIGbTX55yFDYlApDQn/6YZomjz6+KBx69rXHLzHcbtTS586mDdFD+Q==", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.0", + "tuf-js": "^2.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.1.0.tgz", + "integrity": "sha512-1fTqnqyTBWvV7cftUUFtDcHPdSox0N3Ub7C0lRyReYx4zZUlNTZjCV+HPy4Lre+r45dV7Qx5JLKvqqsgxuyYfg==", + "dependencies": { + "@sigstore/bundle": "^2.2.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/@sinonjs/commons": { @@ -4106,16 +4601,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "optional": true, - "peer": true - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "engines": { - "node": ">= 10" - } + "devOptional": true }, "node_modules/@tsconfig/node10": { "version": "1.0.9", @@ -4142,23 +4628,23 @@ "dev": true }, "node_modules/@tufjs/canonical-json": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz", - "integrity": "sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/@tufjs/models": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-1.0.4.tgz", - "integrity": "sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.0.tgz", + "integrity": "sha512-c8nj8BaOExmZKO2DXhDfegyhSGcG9E/mPN3U13L+/PsoWm1uaGiHHjxqSHQiasDBQwDA3aHuw9+9spYAP1qvvg==", "dependencies": { - "@tufjs/canonical-json": "1.0.0", - "minimatch": "^9.0.0" + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/@tufjs/models/node_modules/brace-expansion": { @@ -4183,6 +4669,43 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, "node_modules/@types/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", @@ -4193,9 +4716,9 @@ } }, "node_modules/@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dependencies": { "@types/node": "*" } @@ -4209,9 +4732,9 @@ } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dependencies": { "@types/express-serve-static-core": "*", "@types/node": "*" @@ -4221,15 +4744,13 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "optional": true, - "peer": true + "devOptional": true }, "node_modules/@types/cors": { "version": "2.8.13", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "@types/node": "*" } @@ -4263,9 +4784,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "node_modules/@types/express": { "version": "4.17.13", @@ -4297,9 +4818,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/mime": { "version": "2.0.3", @@ -4311,6 +4832,14 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.18.tgz", "integrity": "sha512-/4QOuy3ZpV7Ya1GTRz5CYSz3DgkKpyUptXuQ5PPce7uuyJAOR7r9FhkmxJfvcNUXyklbC63a+YvB3jxy7s9ngw==" }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/qrcode": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.0.tgz", @@ -4335,15 +4864,15 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dependencies": { "@types/express": "*" } @@ -4370,17 +4899,17 @@ "optional": true }, "node_modules/@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/ws": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz", - "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dependencies": { "@types/node": "*" } @@ -4395,31 +4924,33 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz", - "integrity": "sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz", + "integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.48.1", - "@typescript-eslint/type-utils": "5.48.1", - "@typescript-eslint/utils": "5.48.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "7.4.0", + "@typescript-eslint/type-utils": "7.4.0", + "@typescript-eslint/utils": "7.4.0", + "@typescript-eslint/visitor-keys": "7.4.0", "debug": "^4.3.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -4428,25 +4959,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.1.tgz", - "integrity": "sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz", + "integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.48.1", - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/typescript-estree": "5.48.1", + "@typescript-eslint/scope-manager": "7.4.0", + "@typescript-eslint/types": "7.4.0", + "@typescript-eslint/typescript-estree": "7.4.0", + "@typescript-eslint/visitor-keys": "7.4.0", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -4455,16 +4987,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz", - "integrity": "sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz", + "integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/visitor-keys": "5.48.1" + "@typescript-eslint/types": "7.4.0", + "@typescript-eslint/visitor-keys": "7.4.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -4472,25 +5004,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz", - "integrity": "sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz", + "integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.48.1", - "@typescript-eslint/utils": "5.48.1", + "@typescript-eslint/typescript-estree": "7.4.0", + "@typescript-eslint/utils": "7.4.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -4499,12 +5031,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.1.tgz", - "integrity": "sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz", + "integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -4512,21 +5044,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz", - "integrity": "sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz", + "integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/visitor-keys": "5.48.1", + "@typescript-eslint/types": "7.4.0", + "@typescript-eslint/visitor-keys": "7.4.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -4538,6 +5071,15 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -4558,6 +5100,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4568,57 +5125,62 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.1.tgz", - "integrity": "sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz", + "integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.48.1", - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/typescript-estree": "5.48.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.4.0", + "@typescript-eslint/types": "7.4.0", + "@typescript-eslint/typescript-estree": "7.4.0", + "semver": "^7.5.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz", - "integrity": "sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz", + "integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.48.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "7.4.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@vitejs/plugin-basic-ssl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.0.1.tgz", - "integrity": "sha512-pcub+YbFtFhaGRTo1832FQHQSHvMrlb43974e2eS8EKleR3p1cDdkJFPci1UhwkEf1J9Bz+wKBSzqpKp7nNj2A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", + "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", "engines": { "node": ">=14.6.0" }, "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" } }, "node_modules/@webassemblyjs/ast": { @@ -4752,92 +5314,6 @@ "@xtuc/long": "4.2.2" } }, - "node_modules/@wessberg/ts-evaluator": { - "version": "0.0.27", - "resolved": "https://registry.npmjs.org/@wessberg/ts-evaluator/-/ts-evaluator-0.0.27.tgz", - "integrity": "sha512-7gOpVm3yYojUp/Yn7F4ZybJRxyqfMNf0LXK5KJiawbPfL0XTsJV+0mgrEDjOIR6Bi0OYk2Cyg4tjFu1r8MCZaA==", - "deprecated": "this package has been renamed to ts-evaluator. Please install ts-evaluator instead", - "dependencies": { - "chalk": "^4.1.0", - "jsdom": "^16.4.0", - "object-path": "^0.11.5", - "tslib": "^2.0.3" - }, - "engines": { - "node": ">=10.1.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/wessberg/ts-evaluator?sponsor=1" - }, - "peerDependencies": { - "typescript": ">=3.2.x || >= 4.x" - } - }, - "node_modules/@wessberg/ts-evaluator/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@wessberg/ts-evaluator/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@wessberg/ts-evaluator/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@wessberg/ts-evaluator/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@wessberg/ts-evaluator/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@wessberg/ts-evaluator/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -4853,15 +5329,13 @@ "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" - }, "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/accepts": { "version": "1.3.8", @@ -4886,15 +5360,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -4935,27 +5400,14 @@ } }, "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "dependencies": { - "debug": "4" + "debug": "^4.3.4" }, "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agentkeepalive": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", - "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", - "dependencies": { - "debug": "^4.1.0", - "depd": "^2.0.0", - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" + "node": ">= 14" } }, "node_modules/aggregate-error": { @@ -5046,11 +5498,11 @@ } }, "node_modules/ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dependencies": { - "type-fest": "^0.11.0" + "type-fest": "^0.21.3" }, "engines": { "node": ">=8" @@ -5101,11 +5553,6 @@ "node": ">= 8" } }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, "node_modules/arch": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", @@ -5126,31 +5573,6 @@ ], "optional": true }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -5176,9 +5598,9 @@ "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=" }, "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/array-from": { "version": "2.1.1", @@ -5274,10 +5696,20 @@ "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "optional": true }, + "node_modules/async-each-series": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", + "integrity": "sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==", + "devOptional": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "optional": true }, "node_modules/at-least-node": { "version": "1.0.0", @@ -5289,9 +5721,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.18", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", + "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", "funding": [ { "type": "opencollective", @@ -5300,12 +5732,16 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001591", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -5389,12 +5825,12 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", - "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.10.tgz", + "integrity": "sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ==", "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.2", + "@babel/helper-define-polyfill-provider": "^0.6.1", "semver": "^6.3.1" }, "peerDependencies": { @@ -5410,23 +5846,53 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz", - "integrity": "sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", + "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.2", - "core-js-compat": "^3.31.0" + "@babel/helper-define-polyfill-provider": "^0.5.0", + "core-js-compat": "^3.34.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3/node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", + "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", - "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", + "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.2" + "@babel/helper-define-polyfill-provider": "^0.5.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator/node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", + "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -5460,8 +5926,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "optional": true, - "peer": true, + "devOptional": true, "engines": { "node": "^4.5.0 || >= 5.9" } @@ -5537,12 +6002,12 @@ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -5550,7 +6015,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -5598,12 +6063,10 @@ } }, "node_modules/bonjour-service": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", - "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } @@ -5698,11 +6161,6 @@ "browser-pack-flat": "cli.js" } }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" - }, "node_modules/browser-resolve": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", @@ -5711,6 +6169,450 @@ "resolve": "^1.17.0" } }, + "node_modules/browser-sync": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-3.0.2.tgz", + "integrity": "sha512-PC9c7aWJFVR4IFySrJxOqLwB9ENn3/TaXCXtAa0SzLwocLN3qMjN+IatbjvtCX92BjNXsY6YWg9Eb7F3Wy255g==", + "devOptional": true, + "dependencies": { + "browser-sync-client": "^3.0.2", + "browser-sync-ui": "^3.0.2", + "bs-recipes": "1.3.4", + "chalk": "4.1.2", + "chokidar": "^3.5.1", + "connect": "3.6.6", + "connect-history-api-fallback": "^1", + "dev-ip": "^1.0.1", + "easy-extender": "^2.3.4", + "eazy-logger": "^4.0.1", + "etag": "^1.8.1", + "fresh": "^0.5.2", + "fs-extra": "3.0.1", + "http-proxy": "^1.18.1", + "immutable": "^3", + "micromatch": "^4.0.2", + "opn": "5.3.0", + "portscanner": "2.2.0", + "raw-body": "^2.3.2", + "resp-modifier": "6.0.2", + "rx": "4.1.0", + "send": "0.16.2", + "serve-index": "1.9.1", + "serve-static": "1.13.2", + "server-destroy": "1.0.1", + "socket.io": "^4.4.1", + "ua-parser-js": "^1.0.33", + "yargs": "^17.3.1" + }, + "bin": { + "browser-sync": "dist/bin.js" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/browser-sync-client": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-3.0.2.tgz", + "integrity": "sha512-tBWdfn9L0wd2Pjuz/NWHtNEKthVb1Y67vg8/qyGNtCqetNz5lkDkFnrsx5UhPNPYUO8vci50IWC/BhYaQskDiQ==", + "devOptional": true, + "dependencies": { + "etag": "1.8.1", + "fresh": "0.5.2", + "mitt": "^1.1.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/browser-sync-ui": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-3.0.2.tgz", + "integrity": "sha512-V3FwWAI+abVbFLTyJjXJlCMBwjc3GXf/BPGfwO2fMFACWbIGW9/4SrBOFYEOOtqzCjQE0Di+U3VIb7eES4omNA==", + "devOptional": true, + "dependencies": { + "async-each-series": "0.1.1", + "chalk": "4.1.2", + "connect-history-api-fallback": "^1", + "immutable": "^3", + "server-destroy": "1.0.1", + "socket.io-client": "^4.4.1", + "stream-throttle": "^0.1.3" + } + }, + "node_modules/browser-sync-ui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/browser-sync-ui/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "devOptional": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/browser-sync-ui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/browser-sync-ui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true + }, + "node_modules/browser-sync-ui/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-sync-ui/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "devOptional": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-sync/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/browser-sync/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "devOptional": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/browser-sync/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "devOptional": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/browser-sync/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/browser-sync/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true + }, + "node_modules/browser-sync/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "devOptional": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/browser-sync/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "devOptional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/browser-sync/node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "devOptional": true + }, + "node_modules/browser-sync/node_modules/fs-extra": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "integrity": "sha512-V3Z3WZWVUYd8hoCL5xfXJCaHWYzmtwW5XWYSlLgERi8PWd8bx1kUHUk8L1BT57e49oKnDDD180mjfrHc1yA9rg==", + "devOptional": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^3.0.0", + "universalify": "^0.1.0" + } + }, + "node_modules/browser-sync/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-sync/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "devOptional": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/browser-sync/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "devOptional": true + }, + "node_modules/browser-sync/node_modules/jsonfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==", + "devOptional": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/browser-sync/node_modules/mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "devOptional": true, + "bin": { + "mime": "cli.js" + } + }, + "node_modules/browser-sync/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "devOptional": true + }, + "node_modules/browser-sync/node_modules/send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "devOptional": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/browser-sync/node_modules/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "devOptional": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/browser-sync/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "devOptional": true + }, + "node_modules/browser-sync/node_modules/statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "devOptional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/browser-sync/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "devOptional": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-sync/node_modules/ua-parser-js": { + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.36.tgz", + "integrity": "sha512-znuyCIXzl8ciS3+y3fHJI/2OhQIXbXw9MWC/o3qwyR+RGppjZHrM27CGFSKCJXi2Kctiz537iOu2KnXs1lMQhw==", + "devOptional": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/browser-sync/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "devOptional": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/browser-sync/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "devOptional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/browser-sync/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "devOptional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/browser-sync/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "devOptional": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/browser-sync/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "devOptional": true, + "engines": { + "node": ">=12" + } + }, "node_modules/browser-unpack": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/browser-unpack/-/browser-unpack-1.4.2.tgz", @@ -5982,9 +6884,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "funding": [ { "type": "opencollective", @@ -6000,10 +6902,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -6012,6 +6914,12 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs-recipes": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz", + "integrity": "sha512-BXvDkqhDNxXEjeGM8LFkSbR+jzmP/CYpCiVKYn+soB1dDldeU15EBNDkwVXndKuX35wnNUaPd0qSoQEAkmQtMw==", + "devOptional": true + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -6092,16 +7000,16 @@ } }, "node_modules/cacache": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.3.tgz", - "integrity": "sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==", + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.2.tgz", + "integrity": "sha512-r3NU8h/P+4lVUHfeRw1dtgQYar3DZMm4/cm2bZgOvrFC/su7budSOeqh52VJIC4U4iG1WWwV6vRW0znqBvxNuw==", "dependencies": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-collect": "^1.0.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^4.0.0", @@ -6110,7 +7018,7 @@ "unique-filename": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/cacache/node_modules/brace-expansion": { @@ -6122,18 +7030,18 @@ } }, "node_modules/cacache/node_modules/glob": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", - "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" }, "bin": { - "glob": "dist/cjs/src/bin.js" + "glob": "dist/esm/bin.mjs" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -6143,11 +7051,11 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "engines": { - "node": ">=12" + "node": "14 || >=16.14" } }, "node_modules/cacache/node_modules/minimatch": { @@ -6179,27 +7087,23 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/call-matcher": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/call-matcher/-/call-matcher-2.0.0.tgz", - "integrity": "sha512-CIDC5wZZfZ2VjZu849WQckS58Z3pJXFfRaSjNjgo/q3in5zxkhTwVL83vttgtmvyLG7TuDlLlBya7SKP6CjDIA==", - "dependencies": { - "deep-equal": "^1.0.0", - "espurify": "^2.0.0", - "estraverse": "^4.0.0" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -6217,9 +7121,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001522", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz", - "integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==", + "version": "1.0.30001600", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz", + "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==", "funding": [ { "type": "opencollective", @@ -6426,11 +7330,11 @@ } }, "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "engines": { - "node": ">= 10" + "node": ">= 12" } }, "node_modules/clipboard": { @@ -6487,14 +7391,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" - } - }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -6533,6 +7429,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "optional": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -6639,24 +7536,77 @@ "typedarray": "^0.0.6" } }, + "node_modules/connect": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha512-OO7axMmPpu/2XuX1+2Yrg0ddju31B6xLZMWkJ5rYBu4YRmRVlOjvlY6kw2FJKiAzyxGwnrDUAG4s1Pf0sbBMCQ==", + "devOptional": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.0", + "parseurl": "~1.3.2", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "devOptional": true, "engines": { "node": ">=0.8" } }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "devOptional": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha512-ejnvM9ZXYzp6PUPUyQBMBf0Co5VX2gr5H2VQe2Ui2jWXNlxv+PYZo8wpAymJNJdLsG1R4p+M4aynF8KuoUEwRw==", + "devOptional": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "devOptional": true + }, + "node_modules/connect/node_modules/statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha512-wuTCPGlJONk/a1kqZ4fQM2+908lC7fa7nPYpTC1EhnvqLX/IICbeP1OZGDtA374trpSq68YubKUMo8oRhN46yg==", + "devOptional": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/console-browserify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, "node_modules/constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", @@ -6693,9 +7643,9 @@ ] }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } @@ -6706,9 +7656,9 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -6764,11 +7714,11 @@ } }, "node_modules/core-js-compat": { - "version": "3.32.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.1.tgz", - "integrity": "sha512-GSvKDv4wE0bPnQtjklV101juQ85g6H3rm5PDP20mqlS5j0kXF3pP97YvAu5hl+uFHqMictp3b2VxOHljWMAtuA==", + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", + "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==", "dependencies": { - "browserslist": "^4.21.10" + "browserslist": "^4.23.0" }, "funding": { "type": "opencollective", @@ -6784,8 +7734,7 @@ "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "object-assign": "^4", "vary": "^1" @@ -6795,20 +7744,28 @@ } }, "node_modules/cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dependencies": { - "import-fresh": "^3.2.1", + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" + "parse-json": "^5.2.0" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/cosmiconfig/node_modules/argparse": { @@ -6881,9 +7838,9 @@ "dev": true }, "node_modules/critters": { - "version": "0.0.20", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.20.tgz", - "integrity": "sha512-CImNRorKOl5d8TWcnAz5n5izQ6HFsvz29k327/ELy6UFcmbiZNOsinaKvzv16WZR0P6etfSWYzE47C4/56B3Uw==", + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.22.tgz", + "integrity": "sha512-NU7DEcQZM2Dy8XTKFHxtdnIM/drE312j2T4PCVaSUcS0oBeyT/NImpRw/Ap0zOr/1SE7SgPK9tGPg1WK/sVakw==", "dependencies": { "chalk": "^4.1.0", "css-select": "^5.1.0", @@ -6891,7 +7848,7 @@ "domhandler": "^5.0.2", "htmlparser2": "^8.0.2", "postcss": "^8.4.23", - "pretty-bytes": "^5.3.0" + "postcss-media-query-parser": "^0.2.3" } }, "node_modules/critters/node_modules/ansi-styles": { @@ -6993,18 +7950,18 @@ } }, "node_modules/css-loader": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", - "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.10.0.tgz", + "integrity": "sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw==", "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.4.21", + "postcss": "^8.4.33", "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.3", - "postcss-modules-scope": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.4", + "postcss-modules-scope": "^3.1.1", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" + "semver": "^7.5.4" }, "engines": { "node": ">= 12.13.0" @@ -7014,7 +7971,16 @@ "url": "https://opencollective.com/webpack" }, "peerDependencies": { + "@rspack/core": "0.x || 1.x", "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, "node_modules/css-select": { @@ -7054,27 +8020,6 @@ "node": ">=4" } }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" - }, "node_modules/custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", @@ -7083,21 +8028,20 @@ "peer": true }, "node_modules/cypress": { - "version": "13.6.2", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.2.tgz", - "integrity": "sha512-TW3bGdPU4BrfvMQYv1z3oMqj71YI4AlgJgnrycicmPZAXtvywVFZW9DAToshO65D97rCWfG/kqMFsYB6Kp91gQ==", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.7.0.tgz", + "integrity": "sha512-UimjRSJJYdTlvkChcdcfywKJ6tUYuwYuk/n1uMMglrvi+ZthNhoRYcxnWgTqUtkl17fXrPAsD5XT2rcQYN1xKA==", "hasInstallScript": true, "optional": true, "dependencies": { "@cypress/request": "^3.0.0", "@cypress/xvfb": "^1.2.4", - "@types/node": "^18.17.5", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", "bluebird": "^3.7.2", - "buffer": "^5.6.0", + "buffer": "^5.7.1", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", @@ -7115,7 +8059,7 @@ "figures": "^3.2.0", "fs-extra": "^9.1.0", "getos": "^3.2.1", - "is-ci": "^3.0.0", + "is-ci": "^3.0.1", "is-installed-globally": "~0.4.0", "lazy-ass": "^1.6.0", "listr2": "^3.8.3", @@ -7343,19 +8287,6 @@ "node": ">=0.10" } }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/date-format": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz", @@ -7396,11 +8327,6 @@ "node": ">=0.10.0" } }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" - }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -7418,29 +8344,11 @@ "node": ">=6" } }, - "node_modules/deep-equal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", - "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", - "dependencies": { - "is-arguments": "^1.1.1", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.5.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, "node_modules/default-gateway": { "version": "6.0.3", @@ -7462,16 +8370,19 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-lazy-prop": { @@ -7507,6 +8418,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "optional": true, "engines": { "node": ">=0.4.0" } @@ -7516,11 +8428,6 @@ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -7582,6 +8489,18 @@ "node": ">=0.8.0" } }, + "node_modules/dev-ip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz", + "integrity": "sha512-LmVkry/oDShEgSZPNgqCIp2/TlqtExeGmymru3uCELnfyjY11IzpAproLYs+1X88fXO6DBoYP3ul2Xo2yz2j6A==", + "devOptional": true, + "bin": { + "dev-ip": "lib/dev-ip.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -7629,15 +8548,10 @@ "node": ">=8" } }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" - }, "node_modules/dns-packet": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", - "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" }, @@ -7703,25 +8617,6 @@ } ] }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "engines": { - "node": ">=8" - } - }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", @@ -7772,6 +8667,100 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/easy-extender": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.4.tgz", + "integrity": "sha512-8cAwm6md1YTiPpOvDULYJL4ZS6WfM5/cTeVVh4JsvyYZAoqlRVUpHL9Gr5Fy7HA6xcSZicUia3DeAgO3Us8E+Q==", + "devOptional": true, + "dependencies": { + "lodash": "^4.17.10" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/eazy-logger": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eazy-logger/-/eazy-logger-4.0.1.tgz", + "integrity": "sha512-2GSFtnnC6U4IEKhEI7+PvdxrmjJ04mdsj3wHZTFiw0tUtG4HCWzTr13ZYTk8XOGnA1xQMaDljoBOYlk3D/MMSw==", + "devOptional": true, + "dependencies": { + "chalk": "4.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eazy-logger/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eazy-logger/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "devOptional": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eazy-logger/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eazy-logger/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true + }, + "node_modules/eazy-logger/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eazy-logger/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "devOptional": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -7783,12 +8772,12 @@ } }, "node_modules/echarts": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.4.3.tgz", - "integrity": "sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz", + "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==", "dependencies": { "tslib": "2.3.0", - "zrender": "5.4.4" + "zrender": "5.5.0" } }, "node_modules/echarts/node_modules/tslib": { @@ -7802,9 +8791,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "node_modules/electron-to-chromium": { - "version": "1.4.500", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.500.tgz", - "integrity": "sha512-P38NO8eOuWOKY1sQk5yE0crNtrjgjJj6r3NrbIKtG18KzCHmHE2Bt+aQA7/y0w3uYsHWxDa6icOohzjLJ4vJ4A==" + "version": "1.4.715", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz", + "integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -7884,8 +8873,7 @@ "version": "6.5.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.1.tgz", "integrity": "sha512-mGqhI+D7YxS9KJMppR6Iuo37Ed3abhU8NdfgSvJSDUafQutrN+sPTncJYTyM9+tkhSmWodKtVYGPPHyXJEwEQA==", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", @@ -7902,12 +8890,54 @@ "node": ">=10.0.0" } }, + "node_modules/engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "devOptional": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "devOptional": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "devOptional": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/engine.io-parser": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.1.0.tgz", "integrity": "sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==", - "optional": true, - "peer": true, + "devOptional": true, "engines": { "node": ">=10.0.0" } @@ -7916,8 +8946,7 @@ "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "optional": true, - "peer": true, + "devOptional": true, "engines": { "node": ">= 0.6" } @@ -7926,8 +8955,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "optional": true, - "peer": true, + "devOptional": true, "engines": { "node": ">=10.0.0" }, @@ -8048,6 +9076,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", @@ -8070,13 +9117,18 @@ } }, "node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" } }, "node_modules/es6-iterator": { @@ -8144,9 +9196,9 @@ } }, "node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -8155,34 +9207,35 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" } }, "node_modules/esbuild-wasm": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.18.17.tgz", - "integrity": "sha512-9OHGcuRzy+I8ziF9FzjfKLWAPbvi0e/metACVg9k6bK+SI4FFxeV6PcZsz8RIVaMD4YNehw+qj6UMR3+qj/EuQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.20.1.tgz", + "integrity": "sha512-6v/WJubRsjxBbQdz6izgvx7LsVFvVaGmSdwrFHmEzoVgfXL89hkKPoQHsnVI2ngOkcBUQT9kmAM1hVL1k/Av4A==", "bin": { "esbuild": "bin/esbuild" }, @@ -8263,49 +9316,48 @@ } }, "node_modules/eslint": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz", - "integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.4.1", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -8330,40 +9382,16 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/ansi-styles": { @@ -8434,9 +9462,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -8444,6 +9472,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { @@ -8484,9 +9515,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -8519,19 +9550,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/eslint/node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -8547,23 +9565,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint/node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", @@ -8579,15 +9580,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8600,18 +9592,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -8624,15 +9604,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -8642,9 +9641,9 @@ } }, "node_modules/espree/node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -8665,15 +9664,10 @@ "node": ">=4" } }, - "node_modules/espurify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/espurify/-/espurify-2.1.1.tgz", - "integrity": "sha512-zttWvnkhcDyGOhSH4vO2qCBILpdCMv/MX8lp4cqgRkQoDRGK2oZxi2GfWhlP2dIXmk7BaKeOTuzbHhyC68o8XQ==" - }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -8790,11 +9784,6 @@ "duplexer": "~0.1.1" } }, - "node_modules/eventemitter-asyncresource": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", - "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==" - }, "node_modules/eventemitter2": { "version": "6.4.7", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", @@ -8871,16 +9860,16 @@ "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==" }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.1.tgz", + "integrity": "sha512-K4w1/Bp7y8iSiVObmCrtq8Cs79XjJc/RU2YYkZQ7wpUu5ZyZ7MtPHkqoMz4pf+mgXfNvo2qft8D9OnrH2ABk9w==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -8911,11 +9900,6 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -9082,9 +10066,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", - "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -9104,7 +10088,8 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, "node_modules/fast-safe-stringify": { "version": "2.0.7", @@ -9252,13 +10237,22 @@ "node": ">=8" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { @@ -9266,15 +10260,15 @@ } }, "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "devOptional": true }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -9311,9 +10305,9 @@ } }, "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "engines": { "node": ">=14" }, @@ -9353,15 +10347,15 @@ } }, "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "engines": { "node": "*" }, "funding": { "type": "patreon", - "url": "https://www.patreon.com/infusion" + "url": "https://github.com/sponsors/rawify" } }, "node_modules/fresh": { @@ -9411,20 +10405,20 @@ } }, "node_modules/fs-minipass": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.2.tgz", - "integrity": "sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", "dependencies": { - "minipass": "^5.0.0" + "minipass": "^7.0.3" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/fs-monkey": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", - "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==" }, "node_modules/fs.realpath": { "version": "1.0.0", @@ -9432,9 +10426,9 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, "os": [ @@ -9452,32 +10446,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -9509,15 +10477,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9668,23 +10640,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/guess-parser": { - "version": "0.4.22", - "resolved": "https://registry.npmjs.org/guess-parser/-/guess-parser-0.4.22.tgz", - "integrity": "sha512-KcUWZ5ACGaBM69SbqwVIuWGoSAgD+9iJnchR9j/IarVI1jHVeXv+bUXBIMeqVMSKt3zrn0Dgf9UpcOEpPBLbSg==", - "dependencies": { - "@wessberg/ts-evaluator": "0.0.27" - }, - "peerDependencies": { - "typescript": ">=3.7.5" - } - }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -9737,11 +10698,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9783,11 +10744,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, "node_modules/hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", @@ -9853,21 +10809,6 @@ "node": ">= 0.4" } }, - "node_modules/hdr-histogram-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", - "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", - "dependencies": { - "@assemblyscript/loader": "^0.10.1", - "base64-js": "^1.2.0", - "pako": "^1.0.3" - } - }, - "node_modules/hdr-histogram-percentiles-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", - "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==" - }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -9879,22 +10820,22 @@ } }, "node_modules/hosted-git-info": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", - "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", + "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", "dependencies": { - "lru-cache": "^7.5.1" + "lru-cache": "^10.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "engines": { - "node": ">=12" + "node": "14 || >=16.14" } }, "node_modules/hpack.js": { @@ -9908,21 +10849,10 @@ "wbuf": "^1.1.0" } }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dependencies": { - "whatwg-encoding": "^1.0.5" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "funding": [ { "type": "github", @@ -10012,16 +10942,15 @@ } }, "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/http-proxy-middleware": { @@ -10067,15 +10996,15 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "dependencies": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/human-signals": { @@ -10087,14 +11016,6 @@ "node": ">=8.12.0" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dependencies": { - "ms": "^2.0.0" - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -10137,17 +11058,17 @@ ] }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "engines": { "node": ">= 4" } }, "node_modules/ignore-walk": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.3.tgz", - "integrity": "sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.4.tgz", + "integrity": "sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==", "dependencies": { "minimatch": "^9.0.0" }, @@ -10190,9 +11111,13 @@ } }, "node_modules/immutable": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.1.tgz", - "integrity": "sha512-lj9cnmB/kVS0QHsJnYKD1uo3o39nrbKxszjnqS9Fr6NB7bZzW45U6WSGBPKXDL/CvDKqDNPA4r3DoDQ8GTxo2A==" + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/import-fresh": { "version": "3.3.0", @@ -10248,9 +11173,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -10272,110 +11197,41 @@ } }, "node_modules/inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "version": "9.2.15", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", + "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", + "@ljharb/through": "^2.3.12", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "figures": "^3.2.0", "lodash": "^4.17.21", - "mute-stream": "0.0.8", + "mute-stream": "1.0.0", "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" }, "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18" } }, "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/inquirer/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/inquirer/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/inquirer/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/insert-module-globals": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", @@ -10396,10 +11252,22 @@ "insert-module-globals": "bin/cmd.js" } }, - "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" }, "node_modules/ipaddr.js": { "version": "2.1.0", @@ -10491,11 +11359,11 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10615,6 +11483,15 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-like": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", + "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", + "devOptional": true, + "dependencies": { + "lodash.isfinite": "^3.3.2" + } + }, "node_modules/is-number-object": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", @@ -10657,11 +11534,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" - }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -10833,9 +11705,9 @@ } }, "node_modules/jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -10885,9 +11757,9 @@ } }, "node_modules/jiti": { - "version": "1.19.3", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.3.tgz", - "integrity": "sha512-5eEbBDQT/jF1xg6l36P+mWGGoH9Spuy0PCdSr2dtWRDGC6ph/w9ZCL4lmESW8f8F7MwT3XKescfP0wnZWAKL9w==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", "bin": { "jiti": "bin/jiti.js" } @@ -10911,16 +11783,6 @@ "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==", "peer": true }, - "node_modules/js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -10944,101 +11806,6 @@ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "optional": true }, - "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/jsdom/node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/jsdom/node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jsdom/node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jsdom/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -11050,6 +11817,12 @@ "node": ">=4" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -11079,9 +11852,9 @@ "optional": true }, "node_modules/json5": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", - "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "bin": { "json5": "lib/cli.js" }, @@ -11090,9 +11863,9 @@ } }, "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==" }, "node_modules/jsonfile": { "version": "6.1.0", @@ -11282,6 +12055,15 @@ "node": ">=8.17.0" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -11308,12 +12090,12 @@ } }, "node_modules/launch-editor": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", - "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", + "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", "dependencies": { "picocolors": "^1.0.0", - "shell-quote": "^1.7.3" + "shell-quote": "^1.8.1" } }, "node_modules/lazy-ass": { @@ -11326,9 +12108,9 @@ } }, "node_modules/less": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", - "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", + "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -11422,12 +12204,13 @@ } }, "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -11457,6 +12240,12 @@ "fancy-canvas": "0.2.2" } }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", + "devOptional": true + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -11587,6 +12376,12 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "optional": true }, + "node_modules/lodash.isfinite": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", + "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==", + "devOptional": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -11784,9 +12579,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", - "integrity": "sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, @@ -11801,36 +12596,24 @@ "dev": true }, "node_modules/make-fetch-happen": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", - "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz", + "integrity": "sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==", "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", + "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", "ssri": "^10.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "engines": { - "node": ">=12" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/map-stream": { @@ -11964,11 +12747,12 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.7.6", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", - "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", + "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", "dependencies": { - "schema-utils": "^4.0.0" + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" }, "engines": { "node": ">= 12.13.0" @@ -12089,41 +12873,30 @@ } }, "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", "dependencies": { - "minipass": "^3.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/minipass-fetch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.3.tgz", - "integrity": "sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz", + "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==", "dependencies": { - "minipass": "^5.0.0", + "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" }, @@ -12243,6 +13016,12 @@ "node": ">=8" } }, + "node_modules/mitt": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz", + "integrity": "sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw==", + "devOptional": true + }, "node_modules/mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -12299,9 +13078,9 @@ } }, "node_modules/mrmime": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", - "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", "engines": { "node": ">=10" } @@ -12312,9 +13091,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multi-stage-sourcemap": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/multi-stage-sourcemap/-/multi-stage-sourcemap-0.2.1.tgz", - "integrity": "sha512-umaOM+8BZByZIB/ciD3dQLzTv50rEkkGJV78ta/tIVc/J/rfGZY5y1R+fBD3oTaolx41mK8rRcyGtYbDXlzx8Q==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/multi-stage-sourcemap/-/multi-stage-sourcemap-0.3.1.tgz", + "integrity": "sha512-UiTLYjqeIoVnJHyWGskwMKIhtZKK9uXUjSTWuwatarrc0d2H/6MAVFdwvEA/aKOHamIn7z4tfvxjz+FYucFpNQ==", "dependencies": { "source-map": "^0.1.34" } @@ -12348,9 +13127,12 @@ "integrity": "sha512-KU5tVjIdTGsMb92JlWwEZCGrvtI1ku9G9GuNbWdQT/Ici1ztFXX0L8lWpbbC3pISVMfBNL56wdqplHvva2XSlA==" }, "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/mutexify": { "version": "1.3.1", @@ -12428,9 +13210,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "funding": [ { "type": "github", @@ -12450,12 +13232,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node_modules/needle": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", @@ -12508,14 +13284,14 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, "node_modules/ngx-echarts": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/ngx-echarts/-/ngx-echarts-16.2.0.tgz", - "integrity": "sha512-yhuDbp6qdkmR4kRVLS06Z0Iumod7xOj5n/Z++clRiKM24OQ4sM8WuOTicdfWy6eeYDNywdGSrri4Y5SUGRD8bg==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/ngx-echarts/-/ngx-echarts-17.1.0.tgz", + "integrity": "sha512-DSNF/aKmJSxJWb9UwPUgNtY8Ma9SmViDBRacvAwpakc/5mJerunxndDgoBQkYk5JFKAjXX6bp4ZWLRKL3/5AGA==", "dependencies": { "tslib": "^2.3.0" }, @@ -12524,15 +13300,15 @@ } }, "node_modules/ngx-infinite-scroll": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-16.0.0.tgz", - "integrity": "sha512-bzyNYd+wVlUUxcopRVr2DAa81eEc8vITtKVvb+c7R1uy8hWPTlxOEXf3L1qA4FMwTEzCQ9b37TXzlJji3qBy+A==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-17.0.0.tgz", + "integrity": "sha512-pQXLuRiuhRuDKD3nmgyW1V08JVNBepmk6nb8qjHc5hgsWNts01+R/p33rYcRDzcut6/PWqGyrZ9o9i8swzMYMA==", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/common": ">=16.0.0 <17.0.0", - "@angular/core": ">=16.0.0 <17.0.0" + "@angular/common": ">=17.0.0 <18.0.0", + "@angular/core": ">=17.0.0 <18.0.0" } }, "node_modules/nice-napi": { @@ -12619,33 +13395,32 @@ } }, "node_modules/node-gyp": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz", - "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.0.1.tgz", + "integrity": "sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", + "glob": "^10.3.10", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^11.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", "semver": "^7.3.5", "tar": "^6.1.2", - "which": "^2.0.2" + "which": "^4.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^12.13 || ^14.13 || >=16" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", "optional": true, "bin": { "node-gyp-build": "bin.js", @@ -12653,37 +13428,102 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-gyp/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "engines": { + "node": ">=16" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, "node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", + "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", "dependencies": { - "abbrev": "^1.0.0" + "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/normalize-package-data": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", - "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", + "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", "dependencies": { - "hosted-git-info": "^6.0.0", + "hosted-git-info": "^7.0.0", "is-core-module": "^2.8.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/normalize-path": { @@ -12714,9 +13554,9 @@ } }, "node_modules/npm-install-checks": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.1.1.tgz", - "integrity": "sha512-dH3GmQL4vsPtld59cOn8uY0iOqRmqKvV+DLGwNXV/Q7MDgD2QfOADWd/mFXcIE5LVhYYGjA3baz6W9JneqnuCw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", + "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", "dependencies": { "semver": "^7.1.1" }, @@ -12733,59 +13573,59 @@ } }, "node_modules/npm-package-arg": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", - "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", + "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", "dependencies": { - "hosted-git-info": "^6.0.0", + "hosted-git-info": "^7.0.0", "proc-log": "^3.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm-packlist": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz", - "integrity": "sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", + "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", "dependencies": { - "ignore-walk": "^6.0.0" + "ignore-walk": "^6.0.4" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm-pick-manifest": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.1.tgz", - "integrity": "sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.0.0.tgz", + "integrity": "sha512-VfvRSs/b6n9ol4Qb+bDwNGUXutpy76x6MARw/XssevE0TnctIKcmklJZM5Z7nqs5z5aW+0S63pgCNbpkUNNXBg==", "dependencies": { "npm-install-checks": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^10.0.0", + "npm-package-arg": "^11.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm-registry-fetch": { - "version": "14.0.5", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz", - "integrity": "sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-16.1.0.tgz", + "integrity": "sha512-PQCELXKt8Azvxnt5Y85GseQDJJlglTFM9L9U9gkv2y4e9s0k3GVDdOx3YoB6gm2Do0hlkzC39iCGXby+Wve1Bw==", "dependencies": { - "make-fetch-happen": "^11.0.0", - "minipass": "^5.0.0", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-json-stream": "^1.0.1", "minizlib": "^2.1.2", - "npm-package-arg": "^10.0.0", + "npm-package-arg": "^11.0.0", "proc-log": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm-run-path": { @@ -12799,20 +13639,6 @@ "node": ">=8" } }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -12824,11 +13650,6 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -12845,21 +13666,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -12868,14 +13674,6 @@ "node": ">= 0.4" } }, - "node_modules/object-path": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.8.tgz", - "integrity": "sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==", - "engines": { - "node": ">= 10.12.0" - } - }, "node_modules/object.assign": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", @@ -12902,8 +13700,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "ee-first": "1.1.1" }, @@ -12957,17 +13754,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "node_modules/opn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", + "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", + "devOptional": true, "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/opn/node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "devOptional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -13161,26 +13980,26 @@ } }, "node_modules/pacote": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.2.0.tgz", - "integrity": "sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==", + "version": "17.0.6", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-17.0.6.tgz", + "integrity": "sha512-cJKrW21VRE8vVTRskJo78c/RCvwJCn1f4qgfxL4w77SOWrTCRcmfkYHlHtS0gqpgjv3zhXflRtgsrUCX5xwNnQ==", "dependencies": { - "@npmcli/git": "^4.0.0", + "@npmcli/git": "^5.0.0", "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/promise-spawn": "^6.0.1", - "@npmcli/run-script": "^6.0.0", - "cacache": "^17.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^7.0.0", + "cacache": "^18.0.0", "fs-minipass": "^3.0.0", - "minipass": "^5.0.0", - "npm-package-arg": "^10.0.0", - "npm-packlist": "^7.0.0", - "npm-pick-manifest": "^8.0.0", - "npm-registry-fetch": "^14.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^16.0.0", "proc-log": "^3.0.0", "promise-retry": "^2.0.1", - "read-package-json": "^6.0.0", + "read-package-json": "^7.0.0", "read-package-json-fast": "^3.0.0", - "sigstore": "^1.3.0", + "sigstore": "^2.2.0", "ssri": "^10.0.0", "tar": "^6.1.11" }, @@ -13188,7 +14007,7 @@ "pacote": "lib/bin.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/pako": { @@ -13348,9 +14167,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz", - "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "engines": { "node": "14 || >=16.14" } @@ -13439,14 +14258,9 @@ } }, "node_modules/piscina": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.0.0.tgz", - "integrity": "sha512-641nAmJS4k4iqpNUqfggqUBUMmlw0ZoM5VZKdQkV2e970Inn3Tk9kroCc1wpsYLD07vCwpys5iY0d3xI/9WkTg==", - "dependencies": { - "eventemitter-asyncresource": "^1.0.0", - "hdr-histogram-js": "^2.0.1", - "hdr-histogram-percentiles-obj": "^3.0.0" - }, + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.4.0.tgz", + "integrity": "sha512-+AQduEJefrOApE4bV7KRmp3N2JnnyErlVqq4P/jmko4FPz9Z877BCccl/iB3FdrWSUkvbGV9Kan/KllJgat3Vg==", "optionalDependencies": { "nice-napi": "^1.0.2" } @@ -13560,10 +14374,33 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/portscanner": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", + "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", + "devOptional": true, + "dependencies": { + "async": "^2.6.0", + "is-number-like": "^1.0.3" + }, + "engines": { + "node": ">=0.4", + "npm": ">=1.0.0" + } + }, + "node_modules/portscanner/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "devOptional": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/postcss": { - "version": "8.4.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", - "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "funding": [ { "type": "opencollective", @@ -13579,7 +14416,7 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -13588,26 +14425,40 @@ } }, "node_modules/postcss-loader": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.3.tgz", - "integrity": "sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", + "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", "dependencies": { - "cosmiconfig": "^8.2.0", - "jiti": "^1.18.2", - "semver": "^7.3.8" + "cosmiconfig": "^9.0.0", + "jiti": "^1.20.0", + "semver": "^7.5.4" }, "engines": { - "node": ">= 14.15.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { + "@rspack/core": "0.x || 1.x", "postcss": "^7.0.0 || ^8.0.1", "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==" + }, "node_modules/postcss-modules-extract-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", @@ -13620,9 +14471,9 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", - "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz", + "integrity": "sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==", "dependencies": { "icss-utils": "^5.0.0", "postcss-selector-parser": "^6.0.2", @@ -13636,9 +14487,9 @@ } }, "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz", + "integrity": "sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA==", "dependencies": { "postcss-selector-parser": "^6.0.4" }, @@ -13664,9 +14515,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -13681,9 +14532,10 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, "engines": { "node": ">= 0.8.0" } @@ -13707,6 +14559,7 @@ "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "optional": true, "engines": { "node": ">=6" }, @@ -13810,7 +14663,8 @@ "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "optional": true }, "node_modules/public-encrypt": { "version": "4.0.3", @@ -13841,9 +14695,9 @@ } }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "engines": { "node": ">=6" } @@ -13931,7 +14785,8 @@ "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "optional": true }, "node_modules/queue-microtask": { "version": "1.2.3", @@ -13978,9 +14833,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -14000,17 +14855,17 @@ } }, "node_modules/read-package-json": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", - "integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-7.0.0.tgz", + "integrity": "sha512-uL4Z10OKV4p6vbdvIXB+OzhInYtIozl/VxUBPgNkBuUi2DeRonnuspmaVAMcrkmfjKGNmRndyQAbE7/AmzGwFg==", "dependencies": { "glob": "^10.2.2", "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^5.0.0", + "normalize-package-data": "^6.0.0", "npm-normalize-package-bin": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/read-package-json-fast": { @@ -14026,9 +14881,9 @@ } }, "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -14042,18 +14897,18 @@ } }, "node_modules/read-package-json/node_modules/glob": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", - "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" }, "bin": { - "glob": "dist/cjs/src/bin.js" + "glob": "dist/esm/bin.mjs" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -14063,9 +14918,9 @@ } }, "node_modules/read-package-json/node_modules/json-parse-even-better-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -14115,9 +14970,9 @@ } }, "node_modules/reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==" }, "node_modules/regenerate": { "version": "1.4.2", @@ -14125,9 +14980,9 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", "dependencies": { "regenerate": "^1.4.2" }, @@ -14136,9 +14991,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regenerator-transform": { "version": "0.15.2", @@ -14153,34 +15008,6 @@ "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/regexpu-core": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", @@ -14252,11 +15079,11 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dependencies": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -14298,6 +15125,34 @@ "node": ">=0.10.0" } }, + "node_modules/resp-modifier": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz", + "integrity": "sha512-U1+0kWC/+4ncRFYqQWTx/3qkfE6a4B/h3XXgmXypfa0SPZ3t7cbbaFk297PjQS/yov24R18h6OZe6iZwj3NSLw==", + "devOptional": true, + "dependencies": { + "debug": "^2.2.0", + "minimatch": "^3.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/resp-modifier/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "devOptional": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/resp-modifier/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "devOptional": true + }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -14357,24 +15212,40 @@ } }, "node_modules/rollup": { - "version": "3.26.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.3.tgz", - "integrity": "sha512-7Tin0C8l86TkpcMtXvQu6saWH93nhG3dGQ1/+l5V2TDMceTxO7kDiK6GzbfLWNNxqJXm591PcEZUozZm51ogwQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "dependencies": { + "@types/estree": "1.0.5" + }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", "fsevents": "~2.3.2" } }, "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "engines": { "node": ">=0.12.0" } @@ -14401,6 +15272,12 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rx": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", + "integrity": "sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug==", + "devOptional": true + }, "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -14420,9 +15297,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass": { - "version": "1.64.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.64.1.tgz", - "integrity": "sha512-16rRACSOFEE8VN7SCgBu1MpYCyN7urj9At898tyzdXFhC+a+yOX5dXwAR7L8/IdPJ1NB8OYoXmD55DM30B2kEQ==", + "version": "1.71.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", + "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -14436,28 +15313,28 @@ } }, "node_modules/sass-loader": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.3.2.tgz", - "integrity": "sha512-CQbKl57kdEv+KDLquhC+gE3pXt74LEAzm+tzywcA0/aHZuub8wTErbjAoNI57rPUWRYRNC5WUnNl8eGJNbDdwg==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.1.1.tgz", + "integrity": "sha512-QX8AasDg75monlybel38BZ49JP5Z+uSKfKwF2rO7S74BywaRmGQMUBw9dtkS+ekyM/QnP+NOrRYq8ABMZ9G8jw==", "dependencies": { "neo-async": "^2.6.2" }, "engines": { - "node": ">= 14.15.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "fibers": ">= 3.1.0", + "@rspack/core": "0.x || 1.x", "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "sass": "^1.3.0", "sass-embedded": "*", "webpack": "^5.0.0" }, "peerDependenciesMeta": { - "fibers": { + "@rspack/core": { "optional": true }, "node-sass": { @@ -14468,26 +15345,23 @@ }, "sass-embedded": { "optional": true + }, + "webpack": { + "optional": true } } }, + "node_modules/sass/node_modules/immutable": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==" + }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "optional": true }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/schema-utils": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", @@ -14562,10 +15436,11 @@ "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" }, "node_modules/selfsigned": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", - "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dependencies": { + "@types/node-forge": "^1.3.0", "node-forge": "^1" }, "engines": { @@ -14741,19 +15616,28 @@ "node": ">= 0.8.0" } }, + "node_modules/server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", + "devOptional": true + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -14815,9 +15699,12 @@ } }, "node_modules/shell-quote": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/side-channel": { "version": "1.0.4", @@ -14838,19 +15725,19 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sigstore": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.7.0.tgz", - "integrity": "sha512-KP7QULhWdlu3hlp+jw2EvgWKlOGOY9McLj/jrchLjHNlNPK0KWIwF919cbmOp6QiKXLmPijR2qH/5KYWlbtG9Q==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.2.2.tgz", + "integrity": "sha512-2A3WvXkQurhuMgORgT60r6pOWiCOO5LlEqY2ADxGBDGVYLSo5HN0uLtb68YpVpuL/Vi8mLTe7+0Dx2Fq8lLqEg==", "dependencies": { - "@sigstore/protobuf-specs": "^0.1.0", - "@sigstore/tuf": "^1.0.1", - "make-fetch-happen": "^11.0.1" - }, - "bin": { - "sigstore": "bin/sigstore.js" + "@sigstore/bundle": "^2.2.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.0", + "@sigstore/sign": "^2.2.3", + "@sigstore/tuf": "^2.3.1", + "@sigstore/verify": "^1.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/simple-concat": { @@ -15001,8 +15888,7 @@ "version": "4.7.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.1.tgz", "integrity": "sha512-W+utHys2w//dhFjy7iQQu9sGd3eokCjGbl2r59tyLqNiJJBdIebn3GAKEXBr3osqHTObJi2die/25bCx2zsaaw==", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", @@ -15020,8 +15906,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "ws": "~8.11.0" } @@ -15030,8 +15915,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "optional": true, - "peer": true, + "devOptional": true, "engines": { "node": ">=10.0.0" }, @@ -15048,12 +15932,26 @@ } } }, + "node_modules/socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "devOptional": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -15073,29 +15971,29 @@ } }, "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", + "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", "dependencies": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", + "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "socks": "^2.7.1" }, "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/source-map": { @@ -15115,16 +16013,15 @@ } }, "node_modules/source-map-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.1.tgz", - "integrity": "sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", "dependencies": { - "abab": "^2.0.6", "iconv-lite": "^0.6.3", "source-map-js": "^1.0.2" }, "engines": { - "node": ">= 14.15.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", @@ -15177,9 +16074,9 @@ } }, "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", @@ -15191,9 +16088,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==" }, "node_modules/spdy": { "version": "4.0.2", @@ -15248,6 +16145,11 @@ "node": "*" } }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" + }, "node_modules/sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -15274,11 +16176,11 @@ } }, "node_modules/ssri": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.4.tgz", - "integrity": "sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==", + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz", + "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==", "dependencies": { - "minipass": "^5.0.0" + "minipass": "^7.0.3" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -15354,6 +16256,22 @@ "readable-stream": "^2.0.2" } }, + "node_modules/stream-throttle": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz", + "integrity": "sha512-889+B9vN9dq7/vLbGyuHeZ6/ctf5sNuGWsDy89uNxkFTAgzy0eK7+w5fL3KLNRTkLle7EgZGvHUphZW0Q26MnQ==", + "devOptional": true, + "dependencies": { + "commander": "^2.2.0", + "limiter": "^1.0.5" + }, + "bin": { + "throttleproxy": "bin/throttleproxy.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/streamroller": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz", @@ -15509,11 +16427,6 @@ "node": ">=0.10" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" - }, "node_modules/syntax-error": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", @@ -15531,9 +16444,9 @@ } }, "node_modules/tar": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", - "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -15568,6 +16481,14 @@ "node": ">=8" } }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/tar/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -15580,9 +16501,9 @@ } }, "node_modules/terser": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", - "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", + "version": "5.29.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz", + "integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -15597,15 +16518,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", + "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" + "terser": "^5.26.0" }, "engines": { "node": ">= 10.13.0" @@ -15629,18 +16550,13 @@ } } }, - "node_modules/terser-webpack-plugin/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" - }, "node_modules/terser-webpack-plugin/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { @@ -15687,7 +16603,8 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/throttleit": { "version": "1.0.0", @@ -15720,30 +16637,70 @@ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, "node_modules/tinyify": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyify/-/tinyify-3.1.0.tgz", - "integrity": "sha512-r4tHoDkWhhoItWbxJ3KCHXask3hJN7gCUkR5PLfnQzQagTA6oDkzhCbiEDHkMqo7Ck7vVSA1pTP1gDc9p1AC1w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tinyify/-/tinyify-4.0.0.tgz", + "integrity": "sha512-jNDxImwUrJJAU2NyGG144J8aWx2ni39UuBo7ppCXFRmhSH0CbpWL4HgjNvrsAW05WQAgNZePwAlEemNuB+byaA==", "dependencies": { - "@goto-bus-stop/envify": "^5.0.0", - "acorn-node": "^1.8.2", + "@browserify/envify": "^6.0.0", + "@browserify/uglifyify": "^6.0.0", "browser-pack-flat": "^3.0.9", "bundle-collapser": "^1.3.0", "common-shakeify": "^1.1.1", - "dash-ast": "^1.0.0", "minify-stream": "^2.0.1", "multisplice": "^1.0.0", - "through2": "^3.0.1", - "uglifyify": "^5.0.0", - "unassertify": "^2.1.1" + "terser": "3.16.1", + "through2": "^4.0.2", + "unassertify": "^3.0.1" + } + }, + "node_modules/tinyify/node_modules/commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" + }, + "node_modules/tinyify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tinyify/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyify/node_modules/terser": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.16.1.tgz", + "integrity": "sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==", + "dependencies": { + "commander": "~2.17.1", + "source-map": "~0.6.1", + "source-map-support": "~0.5.9" + }, + "bin": { + "terser": "bin/uglifyjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/tinyify/node_modules/through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" + "readable-stream": "3" } }, "node_modules/tlite": { @@ -15793,6 +16750,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "optional": true, "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -15807,21 +16765,11 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "optional": true, "engines": { "node": ">= 4.0.0" } }, - "node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/transform-ast": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/transform-ast/-/transform-ast-2.4.4.tgz", @@ -15890,6 +16838,18 @@ "tree-kill": "cli.js" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -15955,42 +16915,21 @@ } }, "node_modules/tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tuf-js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.7.tgz", - "integrity": "sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.0.tgz", + "integrity": "sha512-ZSDngmP1z6zw+FIkIBjvOp/II/mIub/O7Pp12j1WNsiCpg5R5wAc//i555bBQsE44O94btLt0xM/Zr2LQjwdCg==", "dependencies": { - "@tufjs/models": "1.0.4", + "@tufjs/models": "2.0.0", "debug": "^4.3.4", - "make-fetch-happen": "^11.1.1" + "make-fetch-happen": "^13.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/tunnel-agent": { @@ -16017,11 +16956,12 @@ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" }, "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "dependencies": { - "prelude-ls": "~1.1.2" + "prelude-ls": "^1.2.1" }, "engines": { "node": ">= 0.8.0" @@ -16037,11 +16977,11 @@ } }, "node_modules/type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -16070,15 +17010,15 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/ua-parser-js": { @@ -16101,47 +17041,6 @@ "node": "*" } }, - "node_modules/uglifyify": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/uglifyify/-/uglifyify-5.0.2.tgz", - "integrity": "sha512-NcSk6pgoC+IgwZZ2tVLVHq+VNKSvLPlLkF5oUiHPVOJI0s/OlSVYEGXG9PCAH0hcyFZLyvt4KBdPAQBRlVDn1Q==", - "dependencies": { - "convert-source-map": "~1.1.0", - "minimatch": "^3.0.2", - "terser": "^3.7.5", - "through": "~2.3.4", - "xtend": "^4.0.1" - } - }, - "node_modules/uglifyify/node_modules/convert-source-map": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", - "integrity": "sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==" - }, - "node_modules/uglifyify/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uglifyify/node_modules/terser": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", - "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", - "dependencies": { - "commander": "^2.19.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.10" - }, - "bin": { - "terser": "bin/uglifyjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/umd": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", @@ -16151,36 +17050,38 @@ } }, "node_modules/unassert": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/unassert/-/unassert-1.6.0.tgz", - "integrity": "sha512-GoMtWTwGSxSFuRD0NKmbjlx3VJkgvSogzDzMPpJXYmBZv6MIWButsyMqEYhMx3NI4osXACcZA9mXiBteXyJtRw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unassert/-/unassert-2.0.2.tgz", + "integrity": "sha512-P6OOg/aRdQmWH+b0g+T4U+9MgL+DG7w6oQPG+N3F2IMuvvd1WfZ5alT/Rjik2lMFVyhfACUxF7PGP1VCwSHlQA==", "dependencies": { - "acorn": "^7.0.0", - "call-matcher": "^2.0.0", - "deep-equal": "^1.0.0", - "espurify": "^2.0.1", - "estraverse": "^4.1.0", - "esutils": "^2.0.2", - "object-assign": "^4.1.0" + "estraverse": "^5.0.0" + } + }, + "node_modules/unassert/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" } }, "node_modules/unassertify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/unassertify/-/unassertify-2.1.1.tgz", - "integrity": "sha512-YIAaIlc6/KC9Oib8cVZLlpDDhK1UTEuaDyx9BwD97xqxDZC0cJOqwFcs/Y6K3m73B5VzHsRTBLXNO0dxS/GkTw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/unassertify/-/unassertify-3.0.1.tgz", + "integrity": "sha512-461ykSPY3oWU+39J5haiq7S/hcYy1oGJ2nHU92lqdL3jft+pSU6oAbb7o6VVmM7nZGLqppszgyzfpCnRBFgFtw==", "dependencies": { - "acorn": "^5.1.0", + "acorn": "^8.0.0", "convert-source-map": "^1.1.1", - "escodegen": "^1.6.1", - "multi-stage-sourcemap": "^0.2.1", + "escodegen": "^2.0.0", + "multi-stage-sourcemap": "^0.3.1", "through": "^2.3.7", - "unassert": "^1.3.1" + "unassert": "^2.0.0" } }, "node_modules/unassertify/node_modules/acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "bin": { "acorn": "bin/acorn" }, @@ -16188,36 +17089,6 @@ "node": ">=0.4.0" } }, - "node_modules/unassertify/node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/unassertify/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -16247,6 +17118,14 @@ "undeclared-identifiers": "bin.js" } }, + "node_modules/undici": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.7.1.tgz", + "integrity": "sha512-+Wtb9bAQw6HYWzCnxrPTMVEV3Q1QjYanI0E4q02ehReMuquQdLTEFEYbfs7hcImVYKcQkWSwT6buEmSVIiDDtQ==", + "engines": { + "node": ">=18.0" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -16332,9 +17211,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "funding": [ { "type": "opencollective", @@ -16381,6 +17260,7 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "optional": true, "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -16461,28 +17341,28 @@ } }, "node_modules/vite": { - "version": "4.4.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.7.tgz", - "integrity": "sha512-6pYf9QJ1mHylfVh39HpuSfMPojPSKVxZvnclX1K1FyZ1PXDOcLBibdq5t1qxJSnL63ca8Wf4zts6mD8u8oc9Fw==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.5.tgz", + "integrity": "sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q==", "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.26", - "rollup": "^3.25.2" + "esbuild": "^0.19.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": ">= 14", + "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", @@ -16514,6 +17394,388 @@ } } }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -16529,26 +17791,6 @@ "node": ">=0.10.0" } }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dependencies": { - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/wait-on": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz", @@ -16627,27 +17869,19 @@ "defaults": "^1.0.3" } }, - "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "engines": { - "node": ">=10.4" - } - }, "node_modules/webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", + "version": "5.90.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz", + "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==", "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", + "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.11.5", "@webassemblyjs/wasm-edit": "^1.11.5", "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.15.0", "es-module-lexer": "^1.2.1", @@ -16661,7 +17895,7 @@ "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", + "terser-webpack-plugin": "^5.3.10", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, @@ -16766,10 +18000,18 @@ } } }, + "node_modules/webpack-dev-server/node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", "dependencies": { "colorette": "^2.0.10", "memfs": "^3.4.3", @@ -16788,32 +18030,13 @@ "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/webpack-merge": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", - "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", "dependencies": { "clone-deep": "^4.0.1", + "flat": "^5.0.2", "wildcard": "^2.0.0" }, "engines": { @@ -16905,32 +18128,6 @@ "node": ">=0.8.0" } }, - "node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dependencies": { - "iconv-lite": "0.4.24" - } - }, - "node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" - }, - "node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -16985,27 +18182,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==" }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -17107,15 +18288,15 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -17134,15 +18315,14 @@ "node": ">= 6" } }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "devOptional": true, + "engines": { + "node": ">=0.4.0" + } }, "node_modules/xtend": { "version": "4.0.2", @@ -17311,17 +18491,17 @@ } }, "node_modules/zone.js": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.1.tgz", - "integrity": "sha512-+bIeDAFEBYuXRuU3qGQvzdPap+N1zjM4KkBAiiQuVVCrHrhjDuY6VkUhNa5+U27+9w0q3fbKiMCbpJ0XzMmSWA==", + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.4.tgz", + "integrity": "sha512-NtTUvIlNELez7Q1DzKVIFZBzNb646boQMgpATo9z3Ftuu/gWvzxCW7jdjcUDoRGxRikrhVHB/zLXh1hxeJawvw==", "dependencies": { "tslib": "^2.3.0" } }, "node_modules/zrender": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.4.4.tgz", - "integrity": "sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz", + "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==", "dependencies": { "tslib": "2.3.0" } @@ -17340,114 +18520,124 @@ "dev": true }, "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + } } }, "@angular-devkit/architect": { - "version": "0.1602.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.0.tgz", - "integrity": "sha512-ZRmUTBeD+uGr605eOHnsovEn6f1mOBI+kxP64DRvagNweX5TN04s3iyQ8jmLSAHQD9ush31LFxv3dVNxv3ceXQ==", + "version": "0.1703.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1703.1.tgz", + "integrity": "sha512-vkfvURv7O+3fHMTE9K+yUEiFS0v4JNYKsDP0LE1ChH5Ocy0bJXGcH2Cyz2W8qdJGDG/tKe41VzvOLpu88Xv3zQ==", "requires": { - "@angular-devkit/core": "16.2.0", + "@angular-devkit/core": "17.3.1", "rxjs": "7.8.1" } }, "@angular-devkit/build-angular": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.2.0.tgz", - "integrity": "sha512-miylwjOqvlKmYrzS84bjRaJrecZxOXH9xsPVvQE8VBe8UKePJjRAL6yyOqXUOGtzlch2YmT98RAnuni7y0FEAw==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.3.1.tgz", + "integrity": "sha512-e+hZvLVH5AvHCFbVtKRd5oJeFsEmjg7kK1V6hsVxH4YE2f2x399TSr+AGxwV+R3jnjZ67ujIeXXd0Uuf1RwcSg==", "requires": { - "@ampproject/remapping": "2.2.1", - "@angular-devkit/architect": "0.1602.0", - "@angular-devkit/build-webpack": "0.1602.0", - "@angular-devkit/core": "16.2.0", - "@babel/core": "7.22.9", - "@babel/generator": "7.22.9", + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1703.1", + "@angular-devkit/build-webpack": "0.1703.1", + "@angular-devkit/core": "17.3.1", + "@babel/core": "7.24.0", + "@babel/generator": "7.23.6", "@babel/helper-annotate-as-pure": "7.22.5", "@babel/helper-split-export-declaration": "7.22.6", - "@babel/plugin-proposal-async-generator-functions": "7.20.7", - "@babel/plugin-transform-async-to-generator": "7.22.5", - "@babel/plugin-transform-runtime": "7.22.9", - "@babel/preset-env": "7.22.9", - "@babel/runtime": "7.22.6", - "@babel/template": "7.22.5", + "@babel/plugin-transform-async-generator-functions": "7.23.9", + "@babel/plugin-transform-async-to-generator": "7.23.3", + "@babel/plugin-transform-runtime": "7.24.0", + "@babel/preset-env": "7.24.0", + "@babel/runtime": "7.24.0", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "16.2.0", - "@vitejs/plugin-basic-ssl": "1.0.1", + "@ngtools/webpack": "17.3.1", + "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", - "autoprefixer": "10.4.14", + "autoprefixer": "10.4.18", "babel-loader": "9.1.3", "babel-plugin-istanbul": "6.1.1", "browserslist": "^4.21.5", - "chokidar": "3.5.3", "copy-webpack-plugin": "11.0.0", - "critters": "0.0.20", - "css-loader": "6.8.1", - "esbuild": "0.18.17", - "esbuild-wasm": "0.18.17", - "fast-glob": "3.3.1", - "guess-parser": "0.4.22", - "https-proxy-agent": "5.0.1", - "inquirer": "8.2.4", - "jsonc-parser": "3.2.0", + "critters": "0.0.22", + "css-loader": "6.10.0", + "esbuild": "0.20.1", + "esbuild-wasm": "0.20.1", + "fast-glob": "3.3.2", + "http-proxy-middleware": "2.0.6", + "https-proxy-agent": "7.0.4", + "inquirer": "9.2.15", + "jsonc-parser": "3.2.1", "karma-source-map-support": "1.4.0", - "less": "4.1.3", + "less": "4.2.0", "less-loader": "11.1.0", "license-webpack-plugin": "4.0.2", "loader-utils": "3.2.1", - "magic-string": "0.30.1", - "mini-css-extract-plugin": "2.7.6", - "mrmime": "1.0.1", + "magic-string": "0.30.8", + "mini-css-extract-plugin": "2.8.1", + "mrmime": "2.0.0", "open": "8.4.2", "ora": "5.4.1", "parse5-html-rewriting-stream": "7.0.0", - "picomatch": "2.3.1", - "piscina": "4.0.0", - "postcss": "8.4.27", - "postcss-loader": "7.3.3", + "picomatch": "4.0.1", + "piscina": "4.4.0", + "postcss": "8.4.35", + "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.64.1", - "sass-loader": "13.3.2", - "semver": "7.5.4", - "source-map-loader": "4.0.1", + "sass": "1.71.1", + "sass-loader": "14.1.1", + "semver": "7.6.0", + "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.19.2", - "text-table": "0.2.0", + "terser": "5.29.1", "tree-kill": "1.2.2", - "tslib": "2.6.1", - "vite": "4.4.7", - "webpack": "5.88.2", + "tslib": "2.6.2", + "undici": "6.7.1", + "vite": "5.1.5", + "watchpack": "2.4.0", + "webpack": "5.90.3", "webpack-dev-middleware": "6.1.1", "webpack-dev-server": "4.15.1", - "webpack-merge": "5.9.0", + "webpack-merge": "5.10.0", "webpack-subresource-integrity": "5.1.0" }, "dependencies": { "@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", + "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.0", + "@babel/parser": "^7.24.0", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "^6.3.1" }, "dependencies": { @@ -17458,204 +18648,218 @@ } } }, + "@esbuild/aix-ppc64": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.1.tgz", + "integrity": "sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==", + "optional": true + }, "@esbuild/android-arm": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.17.tgz", - "integrity": "sha512-wHsmJG/dnL3OkpAcwbgoBTTMHVi4Uyou3F5mf58ZtmUyIKfcdA7TROav/6tCzET4A3QW2Q2FC+eFneMU+iyOxg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.1.tgz", + "integrity": "sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==", "optional": true }, "@esbuild/android-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.17.tgz", - "integrity": "sha512-9np+YYdNDed5+Jgr1TdWBsozZ85U1Oa3xW0c7TWqH0y2aGghXtZsuT8nYRbzOMcl0bXZXjOGbksoTtVOlWrRZg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz", + "integrity": "sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==", "optional": true }, "@esbuild/android-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.17.tgz", - "integrity": "sha512-O+FeWB/+xya0aLg23hHEM2E3hbfwZzjqumKMSIqcHbNvDa+dza2D0yLuymRBQQnC34CWrsJUXyH2MG5VnLd6uw==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.1.tgz", + "integrity": "sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==", "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.17.tgz", - "integrity": "sha512-M9uJ9VSB1oli2BE/dJs3zVr9kcCBBsE883prage1NWz6pBS++1oNn/7soPNS3+1DGj0FrkSvnED4Bmlu1VAE9g==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz", + "integrity": "sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==", "optional": true }, "@esbuild/darwin-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.17.tgz", - "integrity": "sha512-XDre+J5YeIJDMfp3n0279DFNrGCXlxOuGsWIkRb1NThMZ0BsrWXoTg23Jer7fEXQ9Ye5QjrvXpxnhzl3bHtk0g==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz", + "integrity": "sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==", "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.17.tgz", - "integrity": "sha512-cjTzGa3QlNfERa0+ptykyxs5A6FEUQQF0MuilYXYBGdBxD3vxJcKnzDlhDCa1VAJCmAxed6mYhA2KaJIbtiNuQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz", + "integrity": "sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==", "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.17.tgz", - "integrity": "sha512-sOxEvR8d7V7Kw8QqzxWc7bFfnWnGdaFBut1dRUYtu+EIRXefBc/eIsiUiShnW0hM3FmQ5Zf27suDuHsKgZ5QrA==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz", + "integrity": "sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==", "optional": true }, "@esbuild/linux-arm": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.17.tgz", - "integrity": "sha512-2d3Lw6wkwgSLC2fIvXKoMNGVaeY8qdN0IC3rfuVxJp89CRfA3e3VqWifGDfuakPmp90+ZirmTfye1n4ncjv2lg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz", + "integrity": "sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==", "optional": true }, "@esbuild/linux-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.17.tgz", - "integrity": "sha512-c9w3tE7qA3CYWjT+M3BMbwMt+0JYOp3vCMKgVBrCl1nwjAlOMYzEo+gG7QaZ9AtqZFj5MbUc885wuBBmu6aADQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz", + "integrity": "sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w==", "optional": true }, "@esbuild/linux-ia32": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.17.tgz", - "integrity": "sha512-1DS9F966pn5pPnqXYz16dQqWIB0dmDfAQZd6jSSpiT9eX1NzKh07J6VKR3AoXXXEk6CqZMojiVDSZi1SlmKVdg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz", + "integrity": "sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==", "optional": true }, "@esbuild/linux-loong64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.17.tgz", - "integrity": "sha512-EvLsxCk6ZF0fpCB6w6eOI2Fc8KW5N6sHlIovNe8uOFObL2O+Mr0bflPHyHwLT6rwMg9r77WOAWb2FqCQrVnwFg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz", + "integrity": "sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==", "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.17.tgz", - "integrity": "sha512-e0bIdHA5p6l+lwqTE36NAW5hHtw2tNRmHlGBygZC14QObsA3bD4C6sXLJjvnDIjSKhW1/0S3eDy+QmX/uZWEYQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz", + "integrity": "sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==", "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.17.tgz", - "integrity": "sha512-BAAilJ0M5O2uMxHYGjFKn4nJKF6fNCdP1E0o5t5fvMYYzeIqy2JdAP88Az5LHt9qBoUa4tDaRpfWt21ep5/WqQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz", + "integrity": "sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==", "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.17.tgz", - "integrity": "sha512-Wh/HW2MPnC3b8BqRSIme/9Zhab36PPH+3zam5pqGRH4pE+4xTrVLx2+XdGp6fVS3L2x+DrsIcsbMleex8fbE6g==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz", + "integrity": "sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==", "optional": true }, "@esbuild/linux-s390x": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.17.tgz", - "integrity": "sha512-j/34jAl3ul3PNcK3pfI0NSlBANduT2UO5kZ7FCaK33XFv3chDhICLY8wJJWIhiQ+YNdQ9dxqQctRg2bvrMlYgg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz", + "integrity": "sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==", "optional": true }, "@esbuild/linux-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.17.tgz", - "integrity": "sha512-QM50vJ/y+8I60qEmFxMoxIx4de03pGo2HwxdBeFd4nMh364X6TIBZ6VQ5UQmPbQWUVWHWws5MmJXlHAXvJEmpQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz", + "integrity": "sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==", "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.17.tgz", - "integrity": "sha512-/jGlhWR7Sj9JPZHzXyyMZ1RFMkNPjC6QIAan0sDOtIo2TYk3tZn5UDrkE0XgsTQCxWTTOcMPf9p6Rh2hXtl5TQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz", + "integrity": "sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==", "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.17.tgz", - "integrity": "sha512-rSEeYaGgyGGf4qZM2NonMhMOP/5EHp4u9ehFiBrg7stH6BYEEjlkVREuDEcQ0LfIl53OXLxNbfuIj7mr5m29TA==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz", + "integrity": "sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==", "optional": true }, "@esbuild/sunos-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.17.tgz", - "integrity": "sha512-Y7ZBbkLqlSgn4+zot4KUNYst0bFoO68tRgI6mY2FIM+b7ZbyNVtNbDP5y8qlu4/knZZ73fgJDlXID+ohY5zt5g==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz", + "integrity": "sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==", "optional": true }, "@esbuild/win32-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.17.tgz", - "integrity": "sha512-bwPmTJsEQcbZk26oYpc4c/8PvTY3J5/QK8jM19DVlEsAB41M39aWovWoHtNm78sd6ip6prilxeHosPADXtEJFw==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz", + "integrity": "sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==", "optional": true }, "@esbuild/win32-ia32": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.17.tgz", - "integrity": "sha512-H/XaPtPKli2MhW+3CQueo6Ni3Avggi6hP/YvgkEe1aSaxw+AeO8MFjq8DlgfTd9Iz4Yih3QCZI6YLMoyccnPRg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz", + "integrity": "sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==", "optional": true }, "@esbuild/win32-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.17.tgz", - "integrity": "sha512-fGEb8f2BSA3CW7riJVurug65ACLuQAzKq0SSqkY2b2yHHH0MzDfbLyKIGzHwOI/gkHcxM/leuSW6D5w/LMNitA==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz", + "integrity": "sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==", "optional": true }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "esbuild": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.17.tgz", - "integrity": "sha512-1GJtYnUxsJreHYA0Y+iQz2UEykonY66HNWOb0yXYZi9/kNrORUEHVg87eQsCtqh59PEJ5YVZJO98JHznMJSWjg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz", + "integrity": "sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==", "optional": true, "requires": { - "@esbuild/android-arm": "0.18.17", - "@esbuild/android-arm64": "0.18.17", - "@esbuild/android-x64": "0.18.17", - "@esbuild/darwin-arm64": "0.18.17", - "@esbuild/darwin-x64": "0.18.17", - "@esbuild/freebsd-arm64": "0.18.17", - "@esbuild/freebsd-x64": "0.18.17", - "@esbuild/linux-arm": "0.18.17", - "@esbuild/linux-arm64": "0.18.17", - "@esbuild/linux-ia32": "0.18.17", - "@esbuild/linux-loong64": "0.18.17", - "@esbuild/linux-mips64el": "0.18.17", - "@esbuild/linux-ppc64": "0.18.17", - "@esbuild/linux-riscv64": "0.18.17", - "@esbuild/linux-s390x": "0.18.17", - "@esbuild/linux-x64": "0.18.17", - "@esbuild/netbsd-x64": "0.18.17", - "@esbuild/openbsd-x64": "0.18.17", - "@esbuild/sunos-x64": "0.18.17", - "@esbuild/win32-arm64": "0.18.17", - "@esbuild/win32-ia32": "0.18.17", - "@esbuild/win32-x64": "0.18.17" - } - }, - "fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "@esbuild/aix-ppc64": "0.20.1", + "@esbuild/android-arm": "0.20.1", + "@esbuild/android-arm64": "0.20.1", + "@esbuild/android-x64": "0.20.1", + "@esbuild/darwin-arm64": "0.20.1", + "@esbuild/darwin-x64": "0.20.1", + "@esbuild/freebsd-arm64": "0.20.1", + "@esbuild/freebsd-x64": "0.20.1", + "@esbuild/linux-arm": "0.20.1", + "@esbuild/linux-arm64": "0.20.1", + "@esbuild/linux-ia32": "0.20.1", + "@esbuild/linux-loong64": "0.20.1", + "@esbuild/linux-mips64el": "0.20.1", + "@esbuild/linux-ppc64": "0.20.1", + "@esbuild/linux-riscv64": "0.20.1", + "@esbuild/linux-s390x": "0.20.1", + "@esbuild/linux-x64": "0.20.1", + "@esbuild/netbsd-x64": "0.20.1", + "@esbuild/openbsd-x64": "0.20.1", + "@esbuild/sunos-x64": "0.20.1", + "@esbuild/win32-arm64": "0.20.1", + "@esbuild/win32-ia32": "0.20.1", + "@esbuild/win32-x64": "0.20.1" } }, "loader-utils": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==" + }, + "picomatch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==" + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "requires": { + "lru-cache": "^6.0.0" + } } } }, "@angular-devkit/build-webpack": { - "version": "0.1602.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1602.0.tgz", - "integrity": "sha512-KdSr6iAcO30i/LIGL8mYi+d1buVXuDCp2dptzEJ4vxReOMFJca90KLwb+tVHEqqnDb0WkNfWm8Ii2QYh2FrNyA==", + "version": "0.1703.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1703.1.tgz", + "integrity": "sha512-nVUzewX8RCzaEPQZ1JQpE42wpsYchKQwfXUSCkoUsuCMB2c6zuEz0Jt94nzJg3UjSEEV4ZqCH8v5MDOvB49Rlw==", "requires": { - "@angular-devkit/architect": "0.1602.0", + "@angular-devkit/architect": "0.1703.1", "rxjs": "7.8.1" } }, "@angular-devkit/core": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.0.tgz", - "integrity": "sha512-l1k6Rqm3YM16BEn3CWyQKrk9xfu+2ux7Bw3oS+h1TO4/RoxO2PgHj8LLRh/WNrYVarhaqO7QZ5ePBkXNMkzJ1g==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.1.tgz", + "integrity": "sha512-EP7zwqBEaOPuBJwzKmh2abfgNFITGX178BOyTG6zTymeMzEbrvy2OdeQXSslkJ/RGLCpx60GT+0CFW7wGlQR6Q==", "requires": { "ajv": "8.12.0", "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", + "jsonc-parser": "3.2.1", + "picomatch": "4.0.1", "rxjs": "7.8.1", "source-map": "0.7.4" }, @@ -17675,50 +18879,55 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "picomatch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==" } } }, "@angular-devkit/schematics": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.0.tgz", - "integrity": "sha512-QMDJXPE0+YQJ9Ap3MMzb0v7rx6ZbBEokmHgpdIjN3eILYmbAdsSGE8HTV8NjS9nKmcyE9OGzFCMb7PFrDTlTAw==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.1.tgz", + "integrity": "sha512-c3tp5zC5zp6XpK9w8wJf3d4Dyw9BNbmg/VEoXtePGivp4hzks6zuMAFknNRwdK7roOlH0HyM5No4WUZHBFpOmw==", "requires": { - "@angular-devkit/core": "16.2.0", - "jsonc-parser": "3.2.0", - "magic-string": "0.30.1", + "@angular-devkit/core": "17.3.1", + "jsonc-parser": "3.2.1", + "magic-string": "0.30.8", "ora": "5.4.1", "rxjs": "7.8.1" } }, "@angular/animations": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-16.2.2.tgz", - "integrity": "sha512-p0QefudkPGXjq9inZDrtW6WJrDcSeL+Nkc8lxubjg5fLQATKWKpsUBb+u2xEVu8OvWqj8BvrZUDnXYLyTdM4vw==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.3.1.tgz", + "integrity": "sha512-2TZ0M5J0IizhHpb404DeqArlv8Ki9BFz5ZUuET2uFROpKW8IMDCht8fSrn/DKHpjB9lvzPUhNFaRxNWEY6klnA==", "requires": { "tslib": "^2.3.0" } }, "@angular/cli": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.0.tgz", - "integrity": "sha512-xT8vJOyw6Rc2364XDW2jHagLgKu7342ktd/lt+c0u6R+AB2XVFMePR7VceLohX9N/vRUsbQ0nVSZr+ru/hA+HA==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.3.1.tgz", + "integrity": "sha512-IVnnbRi53BZvZ3LE0PCfFefoB2uHlO1sHtilZf/xCpdV4E1Mkz0/hHln5CRHwAXErdSiY57VoMsF5tffxAfaBQ==", "requires": { - "@angular-devkit/architect": "0.1602.0", - "@angular-devkit/core": "16.2.0", - "@angular-devkit/schematics": "16.2.0", - "@schematics/angular": "16.2.0", + "@angular-devkit/architect": "0.1703.1", + "@angular-devkit/core": "17.3.1", + "@angular-devkit/schematics": "17.3.1", + "@schematics/angular": "17.3.1", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.3", - "ini": "4.1.1", - "inquirer": "8.2.4", - "jsonc-parser": "3.2.0", - "npm-package-arg": "10.1.0", - "npm-pick-manifest": "8.0.1", + "ini": "4.1.2", + "inquirer": "9.2.15", + "jsonc-parser": "3.2.1", + "npm-package-arg": "11.0.1", + "npm-pick-manifest": "9.0.0", "open": "8.4.2", "ora": "5.4.1", - "pacote": "15.2.0", - "resolve": "1.22.2", - "semver": "7.5.4", + "pacote": "17.0.6", + "resolve": "1.22.8", + "semver": "7.6.0", "symbol-observable": "4.0.0", "yargs": "17.7.2" }, @@ -17754,6 +18963,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "requires": { + "lru-cache": "^6.0.0" + } + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -17791,31 +19008,31 @@ } }, "@angular/common": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-16.2.2.tgz", - "integrity": "sha512-2ww8/heDHkfJEBwjakbQeleq610ljcvytNs6ZN1xiXib060xMP+xx17Oa9I3onhi369JsKCHkMR5Qs2U5af1uA==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-17.3.1.tgz", + "integrity": "sha512-HyUTJ4RxhE3bOmFRV6Fv2y01ixbrUb8Hd4MxPm8REbNMGKsWCfXhR3FfxFL18Sc03SAF+o0Md0wwekjFKTNKfQ==", "requires": { "tslib": "^2.3.0" } }, "@angular/compiler": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-16.2.2.tgz", - "integrity": "sha512-0X9i5NsqjX++0gmFy0fy2Uc5dHJMxDq6Yu/j1L3RdbvycL1GW+P8GgPfIvD/+v/YiDqpOHQswQXLbkcHw1+svA==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.3.1.tgz", + "integrity": "sha512-8qqlWPGZEyD2FY5losOW3Aocro+lFysPDzsf0LHgQUM6Ub1b+pq4jUOjH6w0vzaxG3TfxkgzOQ9aNdWtSV67Rg==", "requires": { "tslib": "^2.3.0" } }, "@angular/compiler-cli": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-16.2.2.tgz", - "integrity": "sha512-+4i7o0yBc6xSljO8rdYL1G9AiZr2OW5dJAHfPuO21yNhp9BjIJ/TW+Sw1+o/WH4Gnim9adtnonL18UM+vuYeXg==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.3.1.tgz", + "integrity": "sha512-xLV9KU+zOpe57/2rQ59ku21EaStNpLSlR9+qkDYf8JR09fB+W9vY3UYbpi5RjHxAFIZBM5D9SFQjjll8rch26g==", "requires": { - "@babel/core": "7.22.5", + "@babel/core": "7.23.9", "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^3.0.0", "convert-source-map": "^1.5.1", - "reflect-metadata": "^0.1.2", + "reflect-metadata": "^0.2.0", "semver": "^7.0.0", "tslib": "^2.3.0", "yargs": "^17.2.1" @@ -17889,34 +19106,35 @@ } }, "@angular/core": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-16.2.2.tgz", - "integrity": "sha512-l6nJlppguroov7eByBIpbxn/mEPcQrL//Ru1TSPzTtXOLR1p41VqPMaeJXj7xYVx7im57YLTDPAjhtLzkUT/Ow==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-17.3.1.tgz", + "integrity": "sha512-Qf3/sgkXS1LHwOTtqAVYprySrn0YpPIZqerPc0tK+hyQfwAz5BQlpcBhbH8RWKlfCY8eO0cqo/j0+e8DQOgYfg==", "requires": { "tslib": "^2.3.0" } }, "@angular/forms": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-16.2.2.tgz", - "integrity": "sha512-Q3GmOCLSD5BXSjvlLkMsJLXWXb4SO0gA2Aya8JaG1y0doQT/CdGcYXrsCrCT3ot13wqp0HdGQ/ATNd0cNjmz2A==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.3.1.tgz", + "integrity": "sha512-HndsO90k67sFHzd+sII+rhAUksffBvquFuAUCc6QR9WVjILxVg2fY7oBidgS1gKNqu0mptPG0GvuORnaW/0gSg==", "requires": { "tslib": "^2.3.0" } }, "@angular/language-service": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-16.1.5.tgz", - "integrity": "sha512-Zhuoy3KB35fvD52Wg85emmmK38t2oPLHhKYV3pC1WegJunopyF4FkIE1hGqXZyk4VA29QgOT3WC1315FbUiV4Q==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-17.3.1.tgz", + "integrity": "sha512-awC+KHwIRXZ7biQz0Q7q+UZuuyeWHcxjxyQtvv0n1jwwyRpUo8WAXcduKRxl/wMOrxfZkB/tpGcd1/Eeql9CCw==", "dev": true }, "@angular/localize": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-16.2.2.tgz", - "integrity": "sha512-6WO8icVzOGAjZd0Zm4mXisg1ljhmB1+UFSjUdHWrXd0QxAKKhHuI2P91v8J+5j1wl27JIKzTVA7+/gnNQMmGsw==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-17.3.1.tgz", + "integrity": "sha512-ma8PD+DWv68OKgvbmxw7rVohT5HvIYgbmPnVg8lyEz/YkUa9lua0zzrgA+3HUComqv16oVrIaQr00oWxn/9lXQ==", "requires": { - "@babel/core": "7.22.5", - "fast-glob": "3.3.0", + "@babel/core": "7.23.9", + "@types/babel__core": "7.20.5", + "fast-glob": "3.3.2", "yargs": "^17.2.1" }, "dependencies": { @@ -17988,79 +19206,88 @@ } }, "@angular/platform-browser": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-16.2.2.tgz", - "integrity": "sha512-9RwUiHYCAmEirXqwWL/rPfXHMkU9PnpGinok6tmHF8agAmJs1kMWZedxG0GnreTzpTlBu/dI/4v6VDfR9S/D6Q==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.3.1.tgz", + "integrity": "sha512-8ABAL8PElSGzkIparVwifsU0NSu0DdqnWYw9YvLhhZQ6lOuWbG+dTUo/DXzmWhA6ezQWJGNakEZPJJytFIIy+A==", "requires": { "tslib": "^2.3.0" } }, "@angular/platform-browser-dynamic": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-16.2.2.tgz", - "integrity": "sha512-EOGDZ+oABB/aNiBR//wxc6McycjF99/9ds74Q6WoHiNy8CYkzH3plr5pHoy4zkriSyqzoETg2tCu7jSiiMbjRg==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.3.1.tgz", + "integrity": "sha512-ACW/npNaDxUNQtEomjjv/KIBY8jHEinePff5qosnAxLE0IpA4qE9eDp36zG35xoJqrPJPYjXbZCBRqqrzM7U7Q==", "requires": { "tslib": "^2.3.0" } }, "@angular/platform-server": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-16.2.2.tgz", - "integrity": "sha512-mvJsmPJMG6GzzGvOMSkjPgE9zHpuWkFfaO6HTSj0GvxyvxjrlQKsVW87gxEgqfTdhN4JbgmMA4eC9x8625VPyg==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-17.3.1.tgz", + "integrity": "sha512-yC1WgUquIac8qFCPMLjRio2ViR3XHexlXKlZpFhqpWAFPsWSHjoCHTEW+KTUFZmOPhUEFR2W8fWOChur8mjthw==", "requires": { "tslib": "^2.3.0", "xhr2": "^0.2.0" } }, "@angular/router": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-16.2.2.tgz", - "integrity": "sha512-r4KMVUVEWqjOZK0ZUsY8jRqscseGvgcigcikvYJwfxPqtCGYY7RoVAFY7HUtmXC0GAv1aIybK5o/MKTLaecD5Q==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-17.3.1.tgz", + "integrity": "sha512-H6H7lY9i5Ppu0SFwwpeWqJbCFw8cILOj8Rd1+AGoCN5m3ivPtjD2Ltz62PI2zZkqx+WhQdk19l61Wm3oRqg70A==", "requires": { "tslib": "^2.3.0" } }, - "@assemblyscript/loader": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", - "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==" + "@angular/ssr": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@angular/ssr/-/ssr-17.3.1.tgz", + "integrity": "sha512-K/2FGTSC3xJOUJEvqRNVhhhoNGMDFMXUKJqnLXe6cNE8xNkOzO52tWTc0ZZr4ZYvFSwtVMuFY4E65HUxbhGTvA==", + "requires": { + "critters": "0.0.22", + "tslib": "^2.3.0" + } }, "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" } }, "@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==" + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz", + "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==" }, "@babel/core": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", - "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", + "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helpers": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.9", + "@babel/parser": "^7.23.9", + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -18069,11 +19296,11 @@ } }, "@babel/generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", - "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "requires": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -18104,21 +19331,21 @@ } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.10.tgz", - "integrity": "sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", "requires": { - "@babel/types": "^7.22.10" + "@babel/types": "^7.22.15" } }, "@babel/helper-compilation-targets": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", - "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -18144,16 +19371,16 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz", - "integrity": "sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz", + "integrity": "sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==", "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-replace-supers": "^7.24.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" @@ -18167,9 +19394,9 @@ } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz", - "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", @@ -18184,9 +19411,9 @@ } }, "@babel/helper-define-polyfill-provider": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", - "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.1.tgz", + "integrity": "sha512-o7SDgTJuvx5vLKD6SFvkydkSMBvahDKGiNJzG22IZYXhiqoe9efY7zocICBgzHV4IRg5wdgl2nEL/tulKIEIbA==", "requires": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -18207,18 +19434,6 @@ "requires": { "@babel/template": "^7.22.15", "@babel/types": "^7.23.0" - }, - "dependencies": { - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - } - } } }, "@babel/helper-hoist-variables": { @@ -18230,31 +19445,31 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", - "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.23.0" } }, "@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.0" } }, "@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "requires": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.20" } }, "@babel/helper-optimise-call-expression": { @@ -18266,27 +19481,27 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==" + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==" }, "@babel/helper-remap-async-to-generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", - "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-wrap-function": "^7.22.9" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" } }, "@babel/helper-replace-supers": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", - "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz", + "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==", "requires": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5" } }, @@ -18315,9 +19530,9 @@ } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==" }, "@babel/helper-validator-identifier": { "version": "7.22.20", @@ -18325,72 +19540,71 @@ "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, "@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==" + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==" }, "@babel/helper-wrap-function": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz", - "integrity": "sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", "requires": { "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" } }, "@babel/helpers": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", - "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz", + "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==", "requires": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0" } }, "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "requires": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" } }, "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", + "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", - "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz", + "integrity": "sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", - "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz", + "integrity": "sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.5" + "@babel/plugin-transform-optional-chaining": "^7.24.1" } }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz", + "integrity": "sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw==", "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-proposal-private-property-in-object": { @@ -18399,15 +19613,6 @@ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "requires": {} }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -18449,19 +19654,19 @@ } }, "@babel/plugin-syntax-import-assertions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", - "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz", + "integrity": "sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-syntax-import-attributes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", - "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz", + "integrity": "sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-syntax-import-meta": { @@ -18554,235 +19759,235 @@ } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", - "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz", + "integrity": "sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-async-generator-functions": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz", - "integrity": "sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", + "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", "requires": { - "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.9", + "@babel/helper-remap-async-to-generator": "^7.22.20", "@babel/plugin-syntax-async-generators": "^7.8.4" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", - "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", + "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", "requires": { - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5" + "@babel/helper-remap-async-to-generator": "^7.22.20" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", - "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz", + "integrity": "sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz", - "integrity": "sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.1.tgz", + "integrity": "sha512-h71T2QQvDgM2SmT29UYU6ozjMlAt7s7CSs5Hvy8f8cf/GM/Z4a2zMfN+fjVGaieeCrXR3EdQl6C4gQG+OgmbKw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-class-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", - "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz", + "integrity": "sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-class-static-block": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", - "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.1.tgz", + "integrity": "sha512-FUHlKCn6J3ERiu8Dv+4eoz7w8+kFLSyeVG4vDAikwADGjUCoHw/JHokyGtr8OR4UjpwPVivyF+h8Q5iv/JmrtA==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, "@babel/plugin-transform-classes": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", - "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz", + "integrity": "sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==", "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1", "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", - "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz", + "integrity": "sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/template": "^7.24.0" } }, "@babel/plugin-transform-destructuring": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz", - "integrity": "sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz", + "integrity": "sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", - "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz", + "integrity": "sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", - "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz", + "integrity": "sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-dynamic-import": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", - "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz", + "integrity": "sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-dynamic-import": "^7.8.3" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", - "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz", + "integrity": "sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw==", "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-export-namespace-from": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", - "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz", + "integrity": "sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" } }, "@babel/plugin-transform-for-of": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", - "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz", + "integrity": "sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" } }, "@babel/plugin-transform-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", - "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz", + "integrity": "sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==", "requires": { - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-json-strings": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", - "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz", + "integrity": "sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-json-strings": "^7.8.3" } }, "@babel/plugin-transform-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", - "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz", + "integrity": "sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", - "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz", + "integrity": "sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", - "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz", + "integrity": "sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", - "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz", + "integrity": "sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ==", "requires": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", - "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz", + "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==", "requires": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-simple-access": "^7.22.5" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", - "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz", + "integrity": "sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA==", "requires": { "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-identifier": "^7.22.20" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", - "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz", + "integrity": "sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg==", "requires": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-named-capturing-groups-regex": { @@ -18795,134 +20000,133 @@ } }, "@babel/plugin-transform-new-target": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", - "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz", + "integrity": "sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", - "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz", + "integrity": "sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" } }, "@babel/plugin-transform-numeric-separator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", - "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz", + "integrity": "sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, "@babel/plugin-transform-object-rest-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", - "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.1.tgz", + "integrity": "sha512-XjD5f0YqOtebto4HGISLNfiNMTTs6tbkFf2TOqJlYKYmbo+mN9Dnpl4SRoofiziuOWMIyq3sZEUqLo3hLITFEA==", "requires": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.5" + "@babel/plugin-transform-parameters": "^7.24.1" } }, "@babel/plugin-transform-object-super": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", - "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz", + "integrity": "sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1" } }, "@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", - "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz", + "integrity": "sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" } }, "@babel/plugin-transform-optional-chaining": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz", - "integrity": "sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz", + "integrity": "sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/plugin-syntax-optional-chaining": "^7.8.3" } }, "@babel/plugin-transform-parameters": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", - "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz", + "integrity": "sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-private-methods": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", - "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz", + "integrity": "sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-private-property-in-object": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", - "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.1.tgz", + "integrity": "sha512-pTHxDVa0BpUbvAgX3Gat+7cSciXqUcY9j2VZKTbSB6+VQGpNgNO9ailxTGHSXlqOnX1Hcx1Enme2+yv7VqP9bg==", "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } }, "@babel/plugin-transform-property-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", - "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz", + "integrity": "sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-regenerator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", - "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz", + "integrity": "sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "regenerator-transform": "^0.15.2" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", - "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz", + "integrity": "sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-runtime": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz", - "integrity": "sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.0.tgz", + "integrity": "sha512-zc0GA5IitLKJrSfXlXmp8KDqLrnGECK7YRfQBmEKg1NmBOQ7e+KuclBEKJgzifQeUYLdNiAw4B4bjyvzWVLiSA==", "requires": { - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.4", - "babel-plugin-polyfill-corejs3": "^0.8.2", - "babel-plugin-polyfill-regenerator": "^0.5.1", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0", + "babel-plugin-polyfill-corejs2": "^0.4.8", + "babel-plugin-polyfill-corejs3": "^0.9.0", + "babel-plugin-polyfill-regenerator": "^0.5.5", "semver": "^6.3.1" }, "dependencies": { @@ -18934,100 +20138,101 @@ } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", - "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz", + "integrity": "sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", - "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz", + "integrity": "sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", - "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz", + "integrity": "sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-template-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", - "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz", + "integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", - "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz", + "integrity": "sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", - "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz", + "integrity": "sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-unicode-property-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", - "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz", + "integrity": "sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", - "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz", + "integrity": "sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", - "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz", + "integrity": "sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/preset-env": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.9.tgz", - "integrity": "sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.0.tgz", + "integrity": "sha512-ZxPEzV9IgvGn73iK0E6VB9/95Nd7aMFpbE0l8KQFDG70cOV9IxRP7Y2FUPmlK0v6ImlLqYX50iuZ3ZTVhOF2lA==", "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/compat-data": "^7.23.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.22.5", - "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-assertions": "^7.23.3", + "@babel/plugin-syntax-import-attributes": "^7.23.3", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -19039,59 +20244,58 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.7", - "@babel/plugin-transform-async-to-generator": "^7.22.5", - "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.22.5", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.5", - "@babel/plugin-transform-classes": "^7.22.6", - "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.5", - "@babel/plugin-transform-dotall-regex": "^7.22.5", - "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.5", - "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.5", - "@babel/plugin-transform-for-of": "^7.22.5", - "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.5", - "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", - "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.5", - "@babel/plugin-transform-modules-systemjs": "^7.22.5", - "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-arrow-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.9", + "@babel/plugin-transform-async-to-generator": "^7.23.3", + "@babel/plugin-transform-block-scoped-functions": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.4", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.4", + "@babel/plugin-transform-classes": "^7.23.8", + "@babel/plugin-transform-computed-properties": "^7.23.3", + "@babel/plugin-transform-destructuring": "^7.23.3", + "@babel/plugin-transform-dotall-regex": "^7.23.3", + "@babel/plugin-transform-duplicate-keys": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.4", + "@babel/plugin-transform-exponentiation-operator": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-for-of": "^7.23.6", + "@babel/plugin-transform-function-name": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.4", + "@babel/plugin-transform-literals": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-member-expression-literals": "^7.23.3", + "@babel/plugin-transform-modules-amd": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.9", + "@babel/plugin-transform-modules-umd": "^7.23.3", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", - "@babel/plugin-transform-numeric-separator": "^7.22.5", - "@babel/plugin-transform-object-rest-spread": "^7.22.5", - "@babel/plugin-transform-object-super": "^7.22.5", - "@babel/plugin-transform-optional-catch-binding": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.6", - "@babel/plugin-transform-parameters": "^7.22.5", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.5", - "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.5", - "@babel/plugin-transform-reserved-words": "^7.22.5", - "@babel/plugin-transform-shorthand-properties": "^7.22.5", - "@babel/plugin-transform-spread": "^7.22.5", - "@babel/plugin-transform-sticky-regex": "^7.22.5", - "@babel/plugin-transform-template-literals": "^7.22.5", - "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.5", - "@babel/plugin-transform-unicode-property-regex": "^7.22.5", - "@babel/plugin-transform-unicode-regex": "^7.22.5", - "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.4", - "babel-plugin-polyfill-corejs3": "^0.8.2", - "babel-plugin-polyfill-regenerator": "^0.5.1", + "@babel/plugin-transform-new-target": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-numeric-separator": "^7.23.4", + "@babel/plugin-transform-object-rest-spread": "^7.24.0", + "@babel/plugin-transform-object-super": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-parameters": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", + "@babel/plugin-transform-property-literals": "^7.23.3", + "@babel/plugin-transform-regenerator": "^7.23.3", + "@babel/plugin-transform-reserved-words": "^7.23.3", + "@babel/plugin-transform-shorthand-properties": "^7.23.3", + "@babel/plugin-transform-spread": "^7.23.3", + "@babel/plugin-transform-sticky-regex": "^7.23.3", + "@babel/plugin-transform-template-literals": "^7.23.3", + "@babel/plugin-transform-typeof-symbol": "^7.23.3", + "@babel/plugin-transform-unicode-escapes": "^7.23.3", + "@babel/plugin-transform-unicode-property-regex": "^7.23.3", + "@babel/plugin-transform-unicode-regex": "^7.23.3", + "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.8", + "babel-plugin-polyfill-corejs3": "^0.9.0", + "babel-plugin-polyfill-regenerator": "^0.5.5", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, @@ -19104,13 +20308,11 @@ } }, "@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" } @@ -19121,55 +20323,55 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "@babel/runtime": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", - "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", + "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" } }, "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "dependencies": { "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz", + "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==", "requires": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" } }, "@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "requires": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -19178,15 +20380,93 @@ } }, "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "requires": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, + "@browserify/envify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@browserify/envify/-/envify-6.0.0.tgz", + "integrity": "sha512-ovxHR0KTsRCyMNwD7MGV0+VCU1sT6Ds+itC4DaQHM41eUId+w5Jd0qlhLVoDkkIVBnkY3BAAM8yb2QfpBlHkPw==", + "requires": { + "acorn-node": "^2.0.1", + "dash-ast": "^2.0.1", + "multisplice": "^1.0.0", + "through2": "^4.0.2" + }, + "dependencies": { + "acorn-node": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-2.0.1.tgz", + "integrity": "sha512-VLR5sHqjk+8c5hrKeP2fWaIHb8eewsoxnZ8r2qpwRHXMHuC7KyOPflnOx9dLssVQUurzJ7rO0OzIFjHcndafWw==", + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "dash-ast": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-2.0.1.tgz", + "integrity": "sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ==" + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "requires": { + "readable-stream": "3" + } + } + } + }, + "@browserify/uglifyify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@browserify/uglifyify/-/uglifyify-6.0.0.tgz", + "integrity": "sha512-48M2a3novsgKhUSo/B3ja10awc7unliK1HfW6aYBJdLFQj3wXDx9BBJVfj6MVYERSQVEVjNHQQ7IK89h4MpCLw==", + "requires": { + "convert-source-map": "^1.9.0", + "minimatch": "^3.0.2", + "terser": "^5.15.1", + "through2": "^4.0.2", + "xtend": "^4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "requires": { + "readable-stream": "3" + } + } + } + }, "@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -19281,147 +20561,168 @@ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==" }, + "@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "optional": true + }, "@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", "optional": true }, "@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", "optional": true }, "@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", "optional": true }, "@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", "optional": true }, "@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", "optional": true }, "@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", "optional": true }, "@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", "optional": true }, "@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", "optional": true }, "@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", "optional": true }, "@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", "optional": true }, "@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", "optional": true }, "@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", "optional": true }, "@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", "optional": true }, "@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", "optional": true }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true + }, "@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -19437,9 +20738,9 @@ "dev": true }, "globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -19462,12 +20763,18 @@ } } }, + "@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true + }, "@fortawesome/angular-fontawesome": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.13.0.tgz", - "integrity": "sha512-gzSPRdveOXNO7NIiMgTyB46aiHG0i98KinnAEqHXi8qzraM/kCcHn/0y3f4MhemX6kftwsFli0IU8RyHmtXlSQ==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.14.1.tgz", + "integrity": "sha512-Yb5HLiEOAxjSLEcaOM51CKIrzdfvoDafXVJERm9vufxfZkVZPZJgrZRgqwLVpejgq4/Ez6TqHZ6SqmJwdtRF6g==", "requires": { - "tslib": "^2.4.1" + "tslib": "^2.6.2" } }, "@fortawesome/fontawesome-common-types": { @@ -19511,34 +20818,6 @@ } } }, - "@goto-bus-stop/envify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@goto-bus-stop/envify/-/envify-5.0.0.tgz", - "integrity": "sha512-xAnxuDWmwQxO8CgVuPTxKuNsKDfwyXXTyAabG4sNoK59H/ZMC7BHxTA/4ehtinsxbcH7/9L65F5VhyNdQfUyqA==", - "requires": { - "acorn-node": "^2.0.1", - "dash-ast": "^2.0.1", - "multisplice": "^1.0.0", - "through2": "^2.0.5" - }, - "dependencies": { - "acorn-node": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-2.0.1.tgz", - "integrity": "sha512-VLR5sHqjk+8c5hrKeP2fWaIHb8eewsoxnZ8r2qpwRHXMHuC7KyOPflnOx9dLssVQUurzJ7rO0OzIFjHcndafWw==", - "requires": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "dash-ast": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-2.0.1.tgz", - "integrity": "sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ==" - } - } - }, "@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -19555,13 +20834,13 @@ } }, "@humanwhocodes/config-array": { - "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==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" } }, @@ -19572,9 +20851,9 @@ "dev": true }, "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, "@isaacs/cliui": { @@ -19653,13 +20932,24 @@ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==" }, "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "requires": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + } } }, "@jridgewell/resolve-uri": { @@ -19668,9 +20958,9 @@ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" }, "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==" }, "@jridgewell/source-map": { "version": "0.3.5", @@ -19700,6 +20990,14 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "@ljharb/through": { + "version": "2.3.13", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", + "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", + "requires": { + "call-bind": "^1.0.7" + } + }, "@mempool/mempool.js": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@mempool/mempool.js/-/mempool.js-2.3.0.tgz", @@ -19718,17 +21016,17 @@ } }, "@ng-bootstrap/ng-bootstrap": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-15.1.0.tgz", - "integrity": "sha512-4Z/sXYcAq22D15jtlnZV7qztuSnlSlOgO7EVp6rJ8dyGi3CPzX9PqMfetoM6K5sKQTiSW8IfsbdXCWN7rnzxWQ==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-16.0.0.tgz", + "integrity": "sha512-+FJ3e6cX9DW2t7021Ji3oz433rk3+4jLfqzU+Jyx6/vJz1dIOaML3EAY6lYuW4TLiXgMPOMvs6KzPFALGh4Lag==", "requires": { "tslib": "^2.3.0" } }, "@ngtools/webpack": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-16.2.0.tgz", - "integrity": "sha512-c9jv4r7GnLTpnPOeF+a9yAm/3/2wwl9lMBU32i9hlY+q/Hqde4PiL95bUOLnRRL1I64DV7BFTlSZqSPgDpFXZQ==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.3.1.tgz", + "integrity": "sha512-6qRYFN6DqogZK0ZFrSlhg1OsIWm3lL3m+/Ixoj6/MLLjDBrTtHqmI93vg6P1EKYTH4fWChL7jtv7iS/LSZubgw==", "requires": {} }, "@nodelib/fs.scandir": { @@ -19754,6 +21052,25 @@ "fastq": "^1.6.0" } }, + "@npmcli/agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.1.tgz", + "integrity": "sha512-H4FrOVtNyWC8MUwL3UfjOsAihHvT1Pe8POj3JvjXhSTJipsZMtgUALCT4mGyYZNxymkUfOw3PUj6dE4QPp6osQ==", + "requires": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.1" + }, + "dependencies": { + "lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==" + } + } + }, "@npmcli/fs": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", @@ -19763,31 +21080,36 @@ } }, "@npmcli/git": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz", - "integrity": "sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.4.tgz", + "integrity": "sha512-nr6/WezNzuYUppzXRaYu/W4aT5rLxdXqEFupbh6e/ovlYFQ8hpu1UUPV3Ir/YTl+74iXl2ZOMlGzudh9ZPUchQ==", "requires": { - "@npmcli/promise-spawn": "^6.0.0", - "lru-cache": "^7.4.4", - "npm-pick-manifest": "^8.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", "proc-log": "^3.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", - "which": "^3.0.0" + "which": "^4.0.0" }, "dependencies": { + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==" + }, "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==" }, "which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "requires": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" } } } @@ -19806,42 +21128,101 @@ "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==" }, - "@npmcli/promise-spawn": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", - "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", + "@npmcli/package-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.0.0.tgz", + "integrity": "sha512-OI2zdYBLhQ7kpNPaJxiflofYIpkNLi+lnGdzqUOfRmCF3r2l1nadcjtCYMJKv/Utm/ZtlffaUuTiAktPHbc17g==", "requires": { - "which": "^3.0.0" + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.5.3" }, "dependencies": { - "which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "requires": { - "isexe": "^2.0.0" + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + } + }, + "json-parse-even-better-errors": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==" + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "@npmcli/promise-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.1.tgz", + "integrity": "sha512-P4KkF9jX3y+7yFUxgcUdDtLy+t4OlDGuEBLNs57AZsfSfg+uV6MLndqGpnl4831ggaEdXwR50XFoZP4VFtHolg==", + "requires": { + "which": "^4.0.0" + }, + "dependencies": { + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==" + }, + "which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "requires": { + "isexe": "^3.1.1" } } } }, "@npmcli/run-script": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.2.tgz", - "integrity": "sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-7.0.4.tgz", + "integrity": "sha512-9ApYM/3+rBt9V80aYg6tZfzj3UWdiYyCt7gJUD1VJKvWF5nwKDSICXbYIQbspFTq6TOpbsEtIC0LArB8d9PFmg==", "requires": { "@npmcli/node-gyp": "^3.0.0", - "@npmcli/promise-spawn": "^6.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^3.0.0", - "which": "^3.0.0" + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "which": "^4.0.0" }, "dependencies": { + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==" + }, "which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "requires": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" } } } @@ -19853,19 +21234,97 @@ "optional": true }, "@popperjs/core": { - "version": "2.11.6", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", - "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "peer": true }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "optional": true + }, + "@rollup/rollup-linux-riscv64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "optional": true + }, "@schematics/angular": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.0.tgz", - "integrity": "sha512-Ib0/ZCkjWt7a5p3209JVwEWwf41v03K3ylvlxLIEo1ZGijAZAlrBj4GrA5YQ+TmPm2hRyt+owss7x91/x+i0Gw==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.3.1.tgz", + "integrity": "sha512-B3TkpjDjZhxX+tUc2ySEHU33x82Da0sssq/EMqQ1PQBHeRMa0ecyCeExjFEs2y57ZuC+QeVTaUt+TW45lLSjQw==", "requires": { - "@angular-devkit/core": "16.2.0", - "@angular-devkit/schematics": "16.2.0", - "jsonc-parser": "3.2.0" + "@angular-devkit/core": "17.3.1", + "@angular-devkit/schematics": "17.3.1", + "jsonc-parser": "3.2.1" } }, "@sideway/address": { @@ -19889,18 +21348,52 @@ "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", "optional": true }, + "@sigstore/bundle": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.2.0.tgz", + "integrity": "sha512-5VI58qgNs76RDrwXNhpmyN/jKpq9evV/7f1XrcqcAfvxDl5SeVY/I5Rmfe96ULAV7/FK5dge9RBKGBJPhL1WsQ==", + "requires": { + "@sigstore/protobuf-specs": "^0.3.0" + } + }, + "@sigstore/core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.0.0.tgz", + "integrity": "sha512-dW2qjbWLRKGu6MIDUTBuJwXCnR8zivcSpf5inUzk7y84zqy/dji0/uahppoIgMoKeR+6pUZucrwHfkQQtiG9Rw==" + }, "@sigstore/protobuf-specs": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz", - "integrity": "sha512-a31EnjuIDSX8IXBUib3cYLDRlPMU36AWX4xS8ysLaNu4ZzUesDiPt83pgrW2X1YLMe5L2HbDyaKK5BrL4cNKaQ==" + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.0.tgz", + "integrity": "sha512-zxiQ66JFOjVvP9hbhGj/F/qNdsZfkGb/dVXSanNRNuAzMlr4MC95voPUBX8//ZNnmv3uSYzdfR/JSkrgvZTGxA==" + }, + "@sigstore/sign": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.2.3.tgz", + "integrity": "sha512-LqlA+ffyN02yC7RKszCdMTS6bldZnIodiox+IkT8B2f8oRYXCB3LQ9roXeiEL21m64CVH1wyveYAORfD65WoSw==", + "requires": { + "@sigstore/bundle": "^2.2.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.0", + "make-fetch-happen": "^13.0.0" + } }, "@sigstore/tuf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.2.tgz", - "integrity": "sha512-vjwcYePJzM01Ha6oWWZ9gNcdIgnzyFxfqfWzph483DPJTH8Tb7f7bQRRll3CYVkyH56j0AgcPAcl6Vg95DPF+Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.1.tgz", + "integrity": "sha512-9Iv40z652td/QbV0o5n/x25H9w6IYRt2pIGbTX55yFDYlApDQn/6YZomjz6+KBx69rXHLzHcbtTS586mDdFD+Q==", "requires": { - "@sigstore/protobuf-specs": "^0.1.0", - "tuf-js": "^1.1.7" + "@sigstore/protobuf-specs": "^0.3.0", + "tuf-js": "^2.2.0" + } + }, + "@sigstore/verify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.1.0.tgz", + "integrity": "sha512-1fTqnqyTBWvV7cftUUFtDcHPdSox0N3Ub7C0lRyReYx4zZUlNTZjCV+HPy4Lre+r45dV7Qx5JLKvqqsgxuyYfg==", + "requires": { + "@sigstore/bundle": "^2.2.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.0" } }, "@sinonjs/commons": { @@ -19953,13 +21446,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "optional": true, - "peer": true - }, - "@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==" + "devOptional": true }, "@tsconfig/node10": { "version": "1.0.9", @@ -19986,17 +21473,17 @@ "dev": true }, "@tufjs/canonical-json": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz", - "integrity": "sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==" }, "@tufjs/models": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-1.0.4.tgz", - "integrity": "sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.0.tgz", + "integrity": "sha512-c8nj8BaOExmZKO2DXhDfegyhSGcG9E/mPN3U13L+/PsoWm1uaGiHHjxqSHQiasDBQwDA3aHuw9+9spYAP1qvvg==", "requires": { - "@tufjs/canonical-json": "1.0.0", - "minimatch": "^9.0.0" + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.3" }, "dependencies": { "brace-expansion": { @@ -20017,6 +21504,43 @@ } } }, + "@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "requires": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "requires": { + "@babel/types": "^7.20.7" + } + }, "@types/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", @@ -20027,9 +21551,9 @@ } }, "@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "requires": { "@types/node": "*" } @@ -20043,9 +21567,9 @@ } }, "@types/connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "requires": { "@types/express-serve-static-core": "*", "@types/node": "*" @@ -20055,15 +21579,13 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "optional": true, - "peer": true + "devOptional": true }, "@types/cors": { "version": "2.8.13", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "@types/node": "*" } @@ -20096,9 +21618,9 @@ } }, "@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "@types/express": { "version": "4.17.13", @@ -20130,9 +21652,9 @@ } }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "@types/mime": { "version": "2.0.3", @@ -20144,6 +21666,14 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.18.tgz", "integrity": "sha512-/4QOuy3ZpV7Ya1GTRz5CYSz3DgkKpyUptXuQ5PPce7uuyJAOR7r9FhkmxJfvcNUXyklbC63a+YvB3jxy7s9ngw==" }, + "@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "requires": { + "@types/node": "*" + } + }, "@types/qrcode": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.0.tgz", @@ -20168,15 +21698,15 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "requires": { "@types/express": "*" } @@ -20203,17 +21733,17 @@ "optional": true }, "@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "requires": { "@types/node": "*" } }, "@types/ws": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz", - "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "requires": { "@types/node": "*" } @@ -20228,77 +21758,90 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz", - "integrity": "sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz", + "integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.48.1", - "@typescript-eslint/type-utils": "5.48.1", - "@typescript-eslint/utils": "5.48.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "7.4.0", + "@typescript-eslint/type-utils": "7.4.0", + "@typescript-eslint/utils": "7.4.0", + "@typescript-eslint/visitor-keys": "7.4.0", "debug": "^4.3.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/parser": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.1.tgz", - "integrity": "sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz", + "integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.48.1", - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/typescript-estree": "5.48.1", + "@typescript-eslint/scope-manager": "7.4.0", + "@typescript-eslint/types": "7.4.0", + "@typescript-eslint/typescript-estree": "7.4.0", + "@typescript-eslint/visitor-keys": "7.4.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz", - "integrity": "sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz", + "integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/visitor-keys": "5.48.1" + "@typescript-eslint/types": "7.4.0", + "@typescript-eslint/visitor-keys": "7.4.0" } }, "@typescript-eslint/type-utils": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz", - "integrity": "sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz", + "integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.48.1", - "@typescript-eslint/utils": "5.48.1", + "@typescript-eslint/typescript-estree": "7.4.0", + "@typescript-eslint/utils": "7.4.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.1.tgz", - "integrity": "sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz", + "integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz", - "integrity": "sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz", + "integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/visitor-keys": "5.48.1", + "@typescript-eslint/types": "7.4.0", + "@typescript-eslint/visitor-keys": "7.4.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, "globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -20313,6 +21856,15 @@ "slash": "^3.0.0" } }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -20322,35 +21874,40 @@ } }, "@typescript-eslint/utils": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.1.tgz", - "integrity": "sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz", + "integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==", "dev": true, "requires": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.48.1", - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/typescript-estree": "5.48.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.4.0", + "@typescript-eslint/types": "7.4.0", + "@typescript-eslint/typescript-estree": "7.4.0", + "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz", - "integrity": "sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz", + "integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.48.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "7.4.0", + "eslint-visitor-keys": "^3.4.1" } }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "@vitejs/plugin-basic-ssl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.0.1.tgz", - "integrity": "sha512-pcub+YbFtFhaGRTo1832FQHQSHvMrlb43974e2eS8EKleR3p1cDdkJFPci1UhwkEf1J9Bz+wKBSzqpKp7nNj2A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", + "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", "requires": {} }, "@webassemblyjs/ast": { @@ -20484,62 +22041,6 @@ "@xtuc/long": "4.2.2" } }, - "@wessberg/ts-evaluator": { - "version": "0.0.27", - "resolved": "https://registry.npmjs.org/@wessberg/ts-evaluator/-/ts-evaluator-0.0.27.tgz", - "integrity": "sha512-7gOpVm3yYojUp/Yn7F4ZybJRxyqfMNf0LXK5KJiawbPfL0XTsJV+0mgrEDjOIR6Bi0OYk2Cyg4tjFu1r8MCZaA==", - "requires": { - "chalk": "^4.1.0", - "jsdom": "^16.4.0", - "object-path": "^0.11.5", - "tslib": "^2.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -20555,15 +22056,10 @@ "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" }, - "abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" - }, "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==" }, "accepts": { "version": "1.3.8", @@ -20579,15 +22075,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -20620,21 +22107,11 @@ } }, "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "requires": { - "debug": "4" - } - }, - "agentkeepalive": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", - "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", - "requires": { - "debug": "^4.1.0", - "depd": "^2.0.0", - "humanize-ms": "^1.2.1" + "debug": "^4.3.4" } }, "aggregate-error": { @@ -20700,11 +22177,11 @@ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==" }, "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "requires": { - "type-fest": "^0.11.0" + "type-fest": "^0.21.3" } }, "ansi-html-community": { @@ -20734,38 +22211,12 @@ "picomatch": "^2.0.4" } }, - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, "arch": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "optional": true }, - "are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -20793,9 +22244,9 @@ "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=" }, "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "array-from": { "version": "2.1.1", @@ -20883,10 +22334,17 @@ "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "optional": true }, + "async-each-series": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", + "integrity": "sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==", + "devOptional": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "optional": true }, "at-least-node": { "version": "1.0.0", @@ -20895,13 +22353,13 @@ "optional": true }, "autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.18", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", + "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", "requires": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001591", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -20957,12 +22415,12 @@ } }, "babel-plugin-polyfill-corejs2": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", - "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.10.tgz", + "integrity": "sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ==", "requires": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.2", + "@babel/helper-define-polyfill-provider": "^0.6.1", "semver": "^6.3.1" }, "dependencies": { @@ -20974,20 +22432,48 @@ } }, "babel-plugin-polyfill-corejs3": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz", - "integrity": "sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", + "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.2", - "core-js-compat": "^3.31.0" + "@babel/helper-define-polyfill-provider": "^0.5.0", + "core-js-compat": "^3.34.0" + }, + "dependencies": { + "@babel/helper-define-polyfill-provider": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", + "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "requires": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + } + } } }, "babel-plugin-polyfill-regenerator": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", - "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", + "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.2" + "@babel/helper-define-polyfill-provider": "^0.5.0" + }, + "dependencies": { + "@babel/helper-define-polyfill-provider": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", + "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "requires": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + } + } } }, "balanced-match": { @@ -21004,8 +22490,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "optional": true, - "peer": true + "devOptional": true }, "batch": { "version": "0.6.1", @@ -21071,12 +22556,12 @@ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "requires": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -21084,7 +22569,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -21121,12 +22606,10 @@ } }, "bonjour-service": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", - "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "requires": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } @@ -21199,11 +22682,6 @@ "wrap-comment": "^1.0.0" } }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" - }, "browser-resolve": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", @@ -21212,6 +22690,347 @@ "resolve": "^1.17.0" } }, + "browser-sync": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-3.0.2.tgz", + "integrity": "sha512-PC9c7aWJFVR4IFySrJxOqLwB9ENn3/TaXCXtAa0SzLwocLN3qMjN+IatbjvtCX92BjNXsY6YWg9Eb7F3Wy255g==", + "devOptional": true, + "requires": { + "browser-sync-client": "^3.0.2", + "browser-sync-ui": "^3.0.2", + "bs-recipes": "1.3.4", + "chalk": "4.1.2", + "chokidar": "^3.5.1", + "connect": "3.6.6", + "connect-history-api-fallback": "^1", + "dev-ip": "^1.0.1", + "easy-extender": "^2.3.4", + "eazy-logger": "^4.0.1", + "etag": "^1.8.1", + "fresh": "^0.5.2", + "fs-extra": "3.0.1", + "http-proxy": "^1.18.1", + "immutable": "^3", + "micromatch": "^4.0.2", + "opn": "5.3.0", + "portscanner": "2.2.0", + "raw-body": "^2.3.2", + "resp-modifier": "6.0.2", + "rx": "4.1.0", + "send": "0.16.2", + "serve-index": "1.9.1", + "serve-static": "1.13.2", + "server-destroy": "1.0.1", + "socket.io": "^4.4.1", + "ua-parser-js": "^1.0.33", + "yargs": "^17.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "devOptional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "devOptional": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "devOptional": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "devOptional": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "devOptional": true + }, + "fs-extra": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "integrity": "sha512-V3Z3WZWVUYd8hoCL5xfXJCaHWYzmtwW5XWYSlLgERi8PWd8bx1kUHUk8L1BT57e49oKnDDD180mjfrHc1yA9rg==", + "devOptional": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^3.0.0", + "universalify": "^0.1.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "devOptional": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "devOptional": true + }, + "jsonfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==", + "devOptional": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "devOptional": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "devOptional": true + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "devOptional": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "devOptional": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "devOptional": true + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "devOptional": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "devOptional": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "ua-parser-js": { + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.36.tgz", + "integrity": "sha512-znuyCIXzl8ciS3+y3fHJI/2OhQIXbXw9MWC/o3qwyR+RGppjZHrM27CGFSKCJXi2Kctiz537iOu2KnXs1lMQhw==", + "devOptional": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "devOptional": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "devOptional": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "devOptional": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "devOptional": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "devOptional": true + } + } + }, + "browser-sync-client": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-3.0.2.tgz", + "integrity": "sha512-tBWdfn9L0wd2Pjuz/NWHtNEKthVb1Y67vg8/qyGNtCqetNz5lkDkFnrsx5UhPNPYUO8vci50IWC/BhYaQskDiQ==", + "devOptional": true, + "requires": { + "etag": "1.8.1", + "fresh": "0.5.2", + "mitt": "^1.1.3" + } + }, + "browser-sync-ui": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-3.0.2.tgz", + "integrity": "sha512-V3FwWAI+abVbFLTyJjXJlCMBwjc3GXf/BPGfwO2fMFACWbIGW9/4SrBOFYEOOtqzCjQE0Di+U3VIb7eES4omNA==", + "devOptional": true, + "requires": { + "async-each-series": "0.1.1", + "chalk": "4.1.2", + "connect-history-api-fallback": "^1", + "immutable": "^3", + "server-destroy": "1.0.1", + "socket.io-client": "^4.4.1", + "stream-throttle": "^0.1.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "devOptional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "devOptional": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "browser-unpack": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/browser-unpack/-/browser-unpack-1.4.2.tgz", @@ -21453,16 +23272,22 @@ } }, "browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "requires": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" } }, + "bs-recipes": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz", + "integrity": "sha512-BXvDkqhDNxXEjeGM8LFkSbR+jzmP/CYpCiVKYn+soB1dDldeU15EBNDkwVXndKuX35wnNUaPd0qSoQEAkmQtMw==", + "devOptional": true + }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -21520,16 +23345,16 @@ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "cacache": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.3.tgz", - "integrity": "sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==", + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.2.tgz", + "integrity": "sha512-r3NU8h/P+4lVUHfeRw1dtgQYar3DZMm4/cm2bZgOvrFC/su7budSOeqh52VJIC4U4iG1WWwV6vRW0znqBvxNuw==", "requires": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-collect": "^1.0.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^4.0.0", @@ -21547,21 +23372,21 @@ } }, "glob": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", - "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "requires": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" } }, "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==" }, "minimatch": { "version": "9.0.3", @@ -21585,22 +23410,15 @@ "optional": true }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "call-matcher": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/call-matcher/-/call-matcher-2.0.0.tgz", - "integrity": "sha512-CIDC5wZZfZ2VjZu849WQckS58Z3pJXFfRaSjNjgo/q3in5zxkhTwVL83vttgtmvyLG7TuDlLlBya7SKP6CjDIA==", - "requires": { - "deep-equal": "^1.0.0", - "espurify": "^2.0.0", - "estraverse": "^4.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" } }, "callsites": { @@ -21614,9 +23432,9 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "caniuse-lite": { - "version": "1.0.30001522", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz", - "integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==" + "version": "1.0.30001600", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz", + "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==" }, "caseless": { "version": "0.12.0", @@ -21748,9 +23566,9 @@ } }, "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==" }, "clipboard": { "version": "2.0.11", @@ -21800,11 +23618,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" - }, "colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -21842,6 +23655,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "optional": true, "requires": { "delayed-stream": "~1.0.0" } @@ -21932,21 +23746,67 @@ "typedarray": "^0.0.6" } }, + "connect": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha512-OO7axMmPpu/2XuX1+2Yrg0ddju31B6xLZMWkJ5rYBu4YRmRVlOjvlY6kw2FJKiAzyxGwnrDUAG4s1Pf0sbBMCQ==", + "devOptional": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.0", + "parseurl": "~1.3.2", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "devOptional": true, + "requires": { + "ms": "2.0.0" + } + }, + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha512-ejnvM9ZXYzp6PUPUyQBMBf0Co5VX2gr5H2VQe2Ui2jWXNlxv+PYZo8wpAymJNJdLsG1R4p+M4aynF8KuoUEwRw==", + "devOptional": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "devOptional": true + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha512-wuTCPGlJONk/a1kqZ4fQM2+908lC7fa7nPYpTC1EhnvqLX/IICbeP1OZGDtA374trpSq68YubKUMo8oRhN46yg==", + "devOptional": true + } + } + }, "connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "devOptional": true }, "console-browserify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, "constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", @@ -21968,9 +23828,9 @@ } }, "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "convert-source-map": { "version": "1.9.0", @@ -21978,9 +23838,9 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" }, "cookie-signature": { "version": "1.0.6", @@ -22019,11 +23879,11 @@ } }, "core-js-compat": { - "version": "3.32.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.1.tgz", - "integrity": "sha512-GSvKDv4wE0bPnQtjklV101juQ85g6H3rm5PDP20mqlS5j0kXF3pP97YvAu5hl+uFHqMictp3b2VxOHljWMAtuA==", + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", + "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==", "requires": { - "browserslist": "^4.21.10" + "browserslist": "^4.23.0" } }, "core-util-is": { @@ -22035,22 +23895,21 @@ "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "object-assign": "^4", "vary": "^1" } }, "cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "requires": { - "import-fresh": "^3.2.1", + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" + "parse-json": "^5.2.0" }, "dependencies": { "argparse": { @@ -22121,9 +23980,9 @@ "dev": true }, "critters": { - "version": "0.0.20", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.20.tgz", - "integrity": "sha512-CImNRorKOl5d8TWcnAz5n5izQ6HFsvz29k327/ELy6UFcmbiZNOsinaKvzv16WZR0P6etfSWYzE47C4/56B3Uw==", + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.22.tgz", + "integrity": "sha512-NU7DEcQZM2Dy8XTKFHxtdnIM/drE312j2T4PCVaSUcS0oBeyT/NImpRw/Ap0zOr/1SE7SgPK9tGPg1WK/sVakw==", "requires": { "chalk": "^4.1.0", "css-select": "^5.1.0", @@ -22131,7 +23990,7 @@ "domhandler": "^5.0.2", "htmlparser2": "^8.0.2", "postcss": "^8.4.23", - "pretty-bytes": "^5.3.0" + "postcss-media-query-parser": "^0.2.3" }, "dependencies": { "ansi-styles": { @@ -22208,18 +24067,18 @@ } }, "css-loader": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", - "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.10.0.tgz", + "integrity": "sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw==", "requires": { "icss-utils": "^5.1.0", - "postcss": "^8.4.21", + "postcss": "^8.4.33", "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.3", - "postcss-modules-scope": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.4", + "postcss-modules-scope": "^3.1.1", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" + "semver": "^7.5.4" } }, "css-select": { @@ -22244,26 +24103,6 @@ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" - } - } - }, "custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", @@ -22272,20 +24111,19 @@ "peer": true }, "cypress": { - "version": "13.6.2", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.2.tgz", - "integrity": "sha512-TW3bGdPU4BrfvMQYv1z3oMqj71YI4AlgJgnrycicmPZAXtvywVFZW9DAToshO65D97rCWfG/kqMFsYB6Kp91gQ==", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.7.0.tgz", + "integrity": "sha512-UimjRSJJYdTlvkChcdcfywKJ6tUYuwYuk/n1uMMglrvi+ZthNhoRYcxnWgTqUtkl17fXrPAsD5XT2rcQYN1xKA==", "optional": true, "requires": { "@cypress/request": "^3.0.0", "@cypress/xvfb": "^1.2.4", - "@types/node": "^18.17.5", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", "bluebird": "^3.7.2", - "buffer": "^5.6.0", + "buffer": "^5.7.1", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", @@ -22303,7 +24141,7 @@ "figures": "^3.2.0", "fs-extra": "^9.1.0", "getos": "^3.2.1", - "is-ci": "^3.0.0", + "is-ci": "^3.0.1", "is-installed-globally": "~0.4.0", "lazy-ass": "^1.6.0", "listr2": "^3.8.3", @@ -22478,16 +24316,6 @@ "assert-plus": "^1.0.0" } }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, "date-format": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz", @@ -22514,11 +24342,6 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, - "decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" - }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -22533,23 +24356,11 @@ "type-detect": "^4.0.0" } }, - "deep-equal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", - "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", - "requires": { - "is-arguments": "^1.1.1", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.5.1" - } - }, "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, "default-gateway": { "version": "6.0.3", @@ -22568,13 +24379,13 @@ } }, "define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "requires": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" } }, "define-lazy-prop": { @@ -22600,18 +24411,14 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "optional": true }, "delegate": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -22657,6 +24464,12 @@ "minimist": "^1.1.1" } }, + "dev-ip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz", + "integrity": "sha512-LmVkry/oDShEgSZPNgqCIp2/TlqtExeGmymru3uCELnfyjY11IzpAproLYs+1X88fXO6DBoYP3ul2Xo2yz2j6A==", + "devOptional": true + }, "di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -22700,15 +24513,10 @@ "path-type": "^4.0.0" } }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" - }, "dns-packet": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", - "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "requires": { "@leichtgewicht/ip-codec": "^2.0.1" } @@ -22755,21 +24563,6 @@ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" - } - } - }, "domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", @@ -22811,6 +24604,75 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "easy-extender": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.4.tgz", + "integrity": "sha512-8cAwm6md1YTiPpOvDULYJL4ZS6WfM5/cTeVVh4JsvyYZAoqlRVUpHL9Gr5Fy7HA6xcSZicUia3DeAgO3Us8E+Q==", + "devOptional": true, + "requires": { + "lodash": "^4.17.10" + } + }, + "eazy-logger": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eazy-logger/-/eazy-logger-4.0.1.tgz", + "integrity": "sha512-2GSFtnnC6U4IEKhEI7+PvdxrmjJ04mdsj3wHZTFiw0tUtG4HCWzTr13ZYTk8XOGnA1xQMaDljoBOYlk3D/MMSw==", + "devOptional": true, + "requires": { + "chalk": "4.1.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "devOptional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "devOptional": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -22822,12 +24684,12 @@ } }, "echarts": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.4.3.tgz", - "integrity": "sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz", + "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==", "requires": { "tslib": "2.3.0", - "zrender": "5.4.4" + "zrender": "5.5.0" }, "dependencies": { "tslib": { @@ -22843,9 +24705,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.4.500", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.500.tgz", - "integrity": "sha512-P38NO8eOuWOKY1sQk5yE0crNtrjgjJj6r3NrbIKtG18KzCHmHE2Bt+aQA7/y0w3uYsHWxDa6icOohzjLJ4vJ4A==" + "version": "1.4.715", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz", + "integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg==" }, "elliptic": { "version": "6.5.4", @@ -22920,8 +24782,7 @@ "version": "6.5.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.1.tgz", "integrity": "sha512-mGqhI+D7YxS9KJMppR6Iuo37Ed3abhU8NdfgSvJSDUafQutrN+sPTncJYTyM9+tkhSmWodKtVYGPPHyXJEwEQA==", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", @@ -22939,15 +24800,41 @@ "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "optional": true, - "peer": true + "devOptional": true }, "ws": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "optional": true, - "peer": true, + "devOptional": true, + "requires": {} + } + } + }, + "engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "devOptional": true, + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + }, + "dependencies": { + "engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "devOptional": true + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "devOptional": true, "requires": {} } } @@ -22956,8 +24843,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.1.0.tgz", "integrity": "sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==", - "optional": true, - "peer": true + "devOptional": true }, "enhanced-resolve": { "version": "5.15.0", @@ -23039,6 +24925,19 @@ "unbox-primitive": "^1.0.0" } }, + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "requires": { + "get-intrinsic": "^1.2.4" + } + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, "es-module-lexer": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", @@ -23055,13 +24954,14 @@ } }, "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" } }, "es6-iterator": { @@ -23131,38 +25031,39 @@ } }, "esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", "requires": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" } }, "esbuild-wasm": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.18.17.tgz", - "integrity": "sha512-9OHGcuRzy+I8ziF9FzjfKLWAPbvi0e/metACVg9k6bK+SI4FFxeV6PcZsz8RIVaMD4YNehw+qj6UMR3+qj/EuQ==" + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.20.1.tgz", + "integrity": "sha512-6v/WJubRsjxBbQdz6izgvx7LsVFvVaGmSdwrFHmEzoVgfXL89hkKPoQHsnVI2ngOkcBUQT9kmAM1hVL1k/Av4A==" }, "escalade": { "version": "3.1.1", @@ -23215,49 +25116,48 @@ } }, "eslint": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz", - "integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.4.1", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { @@ -23308,9 +25208,9 @@ "dev": true }, "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -23343,9 +25243,9 @@ } }, "globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -23366,16 +25266,6 @@ "argparse": "^2.0.1" } }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -23385,20 +25275,6 @@ "p-locate": "^5.0.0" } }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, "p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", @@ -23408,12 +25284,6 @@ "p-limit": "^3.0.2" } }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -23423,15 +25293,6 @@ "has-flag": "^4.0.0" } }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -23449,44 +25310,45 @@ "estraverse": "^4.1.1" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", "requires": { - "eslint-visitor-keys": "^2.0.0" + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" }, "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true + "type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" } } }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "dependencies": { "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true } } @@ -23496,15 +25358,10 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, - "espurify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/espurify/-/espurify-2.1.1.tgz", - "integrity": "sha512-zttWvnkhcDyGOhSH4vO2qCBILpdCMv/MX8lp4cqgRkQoDRGK2oZxi2GfWhlP2dIXmk7BaKeOTuzbHhyC68o8XQ==" - }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -23606,11 +25463,6 @@ } } }, - "eventemitter-asyncresource": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", - "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==" - }, "eventemitter2": { "version": "6.4.7", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", @@ -23674,16 +25526,16 @@ "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==" }, "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.1.tgz", + "integrity": "sha512-K4w1/Bp7y8iSiVObmCrtq8Cs79XjJc/RU2YYkZQ7wpUu5ZyZ7MtPHkqoMz4pf+mgXfNvo2qft8D9OnrH2ABk9w==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -23711,11 +25563,6 @@ "vary": "~1.1.2" }, "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -23839,9 +25686,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", - "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -23858,7 +25705,8 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, "fast-safe-stringify": { "version": "2.0.7", @@ -23975,26 +25823,32 @@ "path-exists": "^4.0.0" } }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==" + }, "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "requires": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "devOptional": true }, "follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==" + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" }, "foreach": { "version": "2.0.5", @@ -24011,9 +25865,9 @@ }, "dependencies": { "signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" } } }, @@ -24040,9 +25894,9 @@ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, "fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==" }, "fresh": { "version": "0.5.2", @@ -24085,17 +25939,17 @@ } }, "fs-minipass": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.2.tgz", - "integrity": "sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", "requires": { - "minipass": "^5.0.0" + "minipass": "^7.0.3" } }, "fs-monkey": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", - "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==" }, "fs.realpath": { "version": "1.0.0", @@ -24103,9 +25957,9 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "optional": true }, "function-bind": { @@ -24113,26 +25967,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" - }, - "gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - } - }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -24155,10 +25989,11 @@ "optional": true }, "get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "requires": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", @@ -24274,20 +26109,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "guess-parser": { - "version": "0.4.22", - "resolved": "https://registry.npmjs.org/guess-parser/-/guess-parser-0.4.22.tgz", - "integrity": "sha512-KcUWZ5ACGaBM69SbqwVIuWGoSAgD+9iJnchR9j/IarVI1jHVeXv+bUXBIMeqVMSKt3zrn0Dgf9UpcOEpPBLbSg==", - "requires": { - "@wessberg/ts-evaluator": "0.0.27" - } - }, "handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -24327,11 +26154,11 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "requires": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" } }, "has-proto": { @@ -24352,11 +26179,6 @@ "has-symbols": "^1.0.2" } }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, "hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", @@ -24401,21 +26223,6 @@ "function-bind": "^1.1.2" } }, - "hdr-histogram-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", - "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", - "requires": { - "@assemblyscript/loader": "^0.10.1", - "base64-js": "^1.2.0", - "pako": "^1.0.3" - } - }, - "hdr-histogram-percentiles-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", - "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==" - }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -24427,17 +26234,17 @@ } }, "hosted-git-info": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", - "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", + "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", "requires": { - "lru-cache": "^7.5.1" + "lru-cache": "^10.0.1" }, "dependencies": { "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==" } } }, @@ -24452,18 +26259,10 @@ "wbuf": "^1.1.0" } }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, "html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==" + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==" }, "htmlescape": { "version": "1.1.1", @@ -24526,13 +26325,12 @@ } }, "http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "requires": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" } }, "http-proxy-middleware": { @@ -24564,11 +26362,11 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" }, "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "requires": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" } }, @@ -24578,14 +26376,6 @@ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "optional": true }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "requires": { - "ms": "^2.0.0" - } - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -24606,14 +26396,14 @@ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==" }, "ignore-walk": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.3.tgz", - "integrity": "sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.4.tgz", + "integrity": "sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==", "requires": { "minimatch": "^9.0.0" }, @@ -24643,9 +26433,10 @@ "optional": true }, "immutable": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.1.tgz", - "integrity": "sha512-lj9cnmB/kVS0QHsJnYKD1uo3o39nrbKxszjnqS9Fr6NB7bZzW45U6WSGBPKXDL/CvDKqDNPA4r3DoDQ8GTxo2A==" + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", + "devOptional": true }, "import-fresh": { "version": "3.3.0", @@ -24688,9 +26479,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==" + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==" }, "inline-source-map": { "version": "0.6.2", @@ -24708,79 +26499,31 @@ } }, "inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "version": "9.2.15", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", + "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", + "@ljharb/through": "^2.3.12", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "figures": "^3.2.0", "lodash": "^4.17.21", - "mute-stream": "0.0.8", + "mute-stream": "1.0.0", "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" } } }, @@ -24801,10 +26544,21 @@ "xtend": "^4.0.0" } }, - "ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + "ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "requires": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "dependencies": { + "jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + } + } }, "ipaddr.js": { "version": "2.1.0", @@ -24866,11 +26620,11 @@ } }, "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "requires": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "is-date-object": { @@ -24939,6 +26693,15 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "is-number-like": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", + "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", + "devOptional": true, + "requires": { + "lodash.isfinite": "^3.3.2" + } + }, "is-number-object": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", @@ -24963,11 +26726,6 @@ "isobject": "^3.0.1" } }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" - }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -25084,9 +26842,9 @@ } }, "jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "requires": { "@isaacs/cliui": "^8.0.2", "@pkgjs/parseargs": "^0.11.0" @@ -25118,9 +26876,9 @@ } }, "jiti": { - "version": "1.19.3", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.3.tgz", - "integrity": "sha512-5eEbBDQT/jF1xg6l36P+mWGGoH9Spuy0PCdSr2dtWRDGC6ph/w9ZCL4lmESW8f8F7MwT3XKescfP0wnZWAKL9w==" + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==" }, "joi": { "version": "17.11.0", @@ -25141,12 +26899,6 @@ "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==", "peer": true }, - "js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -25167,82 +26919,17 @@ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "optional": true }, - "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" - }, - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==" - }, - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - } - } - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -25272,14 +26959,14 @@ "optional": true }, "json5": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", - "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==" }, "jsonfile": { "version": "6.1.0", @@ -25443,6 +27130,15 @@ "source-map-support": "^0.5.5" } }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -25463,12 +27159,12 @@ } }, "launch-editor": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", - "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", + "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", "requires": { "picocolors": "^1.0.0", - "shell-quote": "^1.7.3" + "shell-quote": "^1.8.1" } }, "lazy-ass": { @@ -25478,9 +27174,9 @@ "optional": true }, "less": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", - "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", + "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", "requires": { "copy-anything": "^2.0.1", "errno": "^0.1.1", @@ -25539,12 +27235,13 @@ } }, "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, "license-webpack-plugin": { @@ -25563,6 +27260,12 @@ "fancy-canvas": "0.2.2" } }, + "limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", + "devOptional": true + }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -25660,6 +27363,12 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "optional": true }, + "lodash.isfinite": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", + "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==", + "devOptional": true + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -25807,9 +27516,9 @@ } }, "magic-string": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", - "integrity": "sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "requires": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -25821,32 +27530,21 @@ "dev": true }, "make-fetch-happen": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", - "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz", + "integrity": "sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==", "requires": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", + "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", "ssri": "^10.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" - } } }, "map-stream": { @@ -25949,11 +27647,12 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "mini-css-extract-plugin": { - "version": "2.7.6", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", - "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", + "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", "requires": { - "schema-utils": "^4.0.0" + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" } }, "minify-stream": { @@ -26042,35 +27741,25 @@ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==" }, "minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", "requires": { - "minipass": "^3.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } + "minipass": "^7.0.3" } }, "minipass-fetch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.3.tgz", - "integrity": "sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz", + "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==", "requires": { "encoding": "^0.1.13", - "minipass": "^5.0.0", + "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" } @@ -26167,6 +27856,12 @@ } } }, + "mitt": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz", + "integrity": "sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw==", + "devOptional": true + }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -26211,9 +27906,9 @@ } }, "mrmime": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", - "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==" }, "ms": { "version": "2.1.2", @@ -26221,9 +27916,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "multi-stage-sourcemap": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/multi-stage-sourcemap/-/multi-stage-sourcemap-0.2.1.tgz", - "integrity": "sha512-umaOM+8BZByZIB/ciD3dQLzTv50rEkkGJV78ta/tIVc/J/rfGZY5y1R+fBD3oTaolx41mK8rRcyGtYbDXlzx8Q==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/multi-stage-sourcemap/-/multi-stage-sourcemap-0.3.1.tgz", + "integrity": "sha512-UiTLYjqeIoVnJHyWGskwMKIhtZKK9uXUjSTWuwatarrc0d2H/6MAVFdwvEA/aKOHamIn7z4tfvxjz+FYucFpNQ==", "requires": { "source-map": "^0.1.34" }, @@ -26253,9 +27948,9 @@ "integrity": "sha512-KU5tVjIdTGsMb92JlWwEZCGrvtI1ku9G9GuNbWdQT/Ici1ztFXX0L8lWpbbC3pISVMfBNL56wdqplHvva2XSlA==" }, "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==" }, "mutexify": { "version": "1.3.1", @@ -26316,9 +28011,9 @@ } }, "nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" }, "natural-compare": { "version": "1.4.0", @@ -26326,12 +28021,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "needle": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", @@ -26374,22 +28063,22 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, "ngx-echarts": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/ngx-echarts/-/ngx-echarts-16.2.0.tgz", - "integrity": "sha512-yhuDbp6qdkmR4kRVLS06Z0Iumod7xOj5n/Z++clRiKM24OQ4sM8WuOTicdfWy6eeYDNywdGSrri4Y5SUGRD8bg==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/ngx-echarts/-/ngx-echarts-17.1.0.tgz", + "integrity": "sha512-DSNF/aKmJSxJWb9UwPUgNtY8Ma9SmViDBRacvAwpakc/5mJerunxndDgoBQkYk5JFKAjXX6bp4ZWLRKL3/5AGA==", "requires": { "tslib": "^2.3.0" } }, "ngx-infinite-scroll": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-16.0.0.tgz", - "integrity": "sha512-bzyNYd+wVlUUxcopRVr2DAa81eEc8vITtKVvb+c7R1uy8hWPTlxOEXf3L1qA4FMwTEzCQ9b37TXzlJji3qBy+A==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-17.0.0.tgz", + "integrity": "sha512-pQXLuRiuhRuDKD3nmgyW1V08JVNBepmk6nb8qjHc5hgsWNts01+R/p33rYcRDzcut6/PWqGyrZ9o9i8swzMYMA==", "requires": { "tslib": "^2.3.0" } @@ -26475,48 +28164,90 @@ "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" }, "node-gyp": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz", - "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.0.1.tgz", + "integrity": "sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==", "requires": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", + "glob": "^10.3.10", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^11.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", "semver": "^7.3.5", "tar": "^6.1.2", - "which": "^2.0.2" + "which": "^4.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + } + }, + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==" + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "requires": { + "isexe": "^3.1.1" + } + } } }, "node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", "optional": true }, "node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, "nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", + "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", "requires": { - "abbrev": "^1.0.0" + "abbrev": "^2.0.0" } }, "normalize-package-data": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", - "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", + "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", "requires": { - "hosted-git-info": "^6.0.0", + "hosted-git-info": "^7.0.0", "is-core-module": "^2.8.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" @@ -26541,9 +28272,9 @@ } }, "npm-install-checks": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.1.1.tgz", - "integrity": "sha512-dH3GmQL4vsPtld59cOn8uY0iOqRmqKvV+DLGwNXV/Q7MDgD2QfOADWd/mFXcIE5LVhYYGjA3baz6W9JneqnuCw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", + "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", "requires": { "semver": "^7.1.1" } @@ -26554,46 +28285,46 @@ "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==" }, "npm-package-arg": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", - "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", + "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", "requires": { - "hosted-git-info": "^6.0.0", + "hosted-git-info": "^7.0.0", "proc-log": "^3.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^5.0.0" } }, "npm-packlist": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz", - "integrity": "sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", + "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", "requires": { - "ignore-walk": "^6.0.0" + "ignore-walk": "^6.0.4" } }, "npm-pick-manifest": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.1.tgz", - "integrity": "sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.0.0.tgz", + "integrity": "sha512-VfvRSs/b6n9ol4Qb+bDwNGUXutpy76x6MARw/XssevE0TnctIKcmklJZM5Z7nqs5z5aW+0S63pgCNbpkUNNXBg==", "requires": { "npm-install-checks": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^10.0.0", + "npm-package-arg": "^11.0.0", "semver": "^7.3.5" } }, "npm-registry-fetch": { - "version": "14.0.5", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz", - "integrity": "sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-16.1.0.tgz", + "integrity": "sha512-PQCELXKt8Azvxnt5Y85GseQDJJlglTFM9L9U9gkv2y4e9s0k3GVDdOx3YoB6gm2Do0hlkzC39iCGXby+Wve1Bw==", "requires": { - "make-fetch-happen": "^11.0.0", - "minipass": "^5.0.0", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-json-stream": "^1.0.1", "minizlib": "^2.1.2", - "npm-package-arg": "^10.0.0", + "npm-package-arg": "^11.0.0", "proc-log": "^3.0.0" } }, @@ -26605,17 +28336,6 @@ "path-key": "^3.0.0" } }, - "npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "requires": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - } - }, "nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -26624,11 +28344,6 @@ "boolbase": "^1.0.0" } }, - "nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -26639,25 +28354,11 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==" }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, - "object-path": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.8.tgz", - "integrity": "sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==" - }, "object.assign": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", @@ -26678,8 +28379,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "ee-first": "1.1.1" } @@ -26715,17 +28415,35 @@ "is-wsl": "^2.2.0" } }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "opn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", + "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", + "devOptional": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" + "is-wsl": "^1.1.0" + }, + "dependencies": { + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "devOptional": true + } + } + }, + "optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" } }, "ora": { @@ -26862,26 +28580,26 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "pacote": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.2.0.tgz", - "integrity": "sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==", + "version": "17.0.6", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-17.0.6.tgz", + "integrity": "sha512-cJKrW21VRE8vVTRskJo78c/RCvwJCn1f4qgfxL4w77SOWrTCRcmfkYHlHtS0gqpgjv3zhXflRtgsrUCX5xwNnQ==", "requires": { - "@npmcli/git": "^4.0.0", + "@npmcli/git": "^5.0.0", "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/promise-spawn": "^6.0.1", - "@npmcli/run-script": "^6.0.0", - "cacache": "^17.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^7.0.0", + "cacache": "^18.0.0", "fs-minipass": "^3.0.0", - "minipass": "^5.0.0", - "npm-package-arg": "^10.0.0", - "npm-packlist": "^7.0.0", - "npm-pick-manifest": "^8.0.0", - "npm-registry-fetch": "^14.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^16.0.0", "proc-log": "^3.0.0", "promise-retry": "^2.0.1", - "read-package-json": "^6.0.0", + "read-package-json": "^7.0.0", "read-package-json-fast": "^3.0.0", - "sigstore": "^1.3.0", + "sigstore": "^2.2.0", "ssri": "^10.0.0", "tar": "^6.1.11" } @@ -27001,9 +28719,9 @@ }, "dependencies": { "lru-cache": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz", - "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==" + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==" } } }, @@ -27073,13 +28791,10 @@ "optional": true }, "piscina": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.0.0.tgz", - "integrity": "sha512-641nAmJS4k4iqpNUqfggqUBUMmlw0ZoM5VZKdQkV2e970Inn3Tk9kroCc1wpsYLD07vCwpys5iY0d3xI/9WkTg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.4.0.tgz", + "integrity": "sha512-+AQduEJefrOApE4bV7KRmp3N2JnnyErlVqq4P/jmko4FPz9Z877BCccl/iB3FdrWSUkvbGV9Kan/KllJgat3Vg==", "requires": { - "eventemitter-asyncresource": "^1.0.0", - "hdr-histogram-js": "^2.0.1", - "hdr-histogram-percentiles-obj": "^3.0.0", "nice-napi": "^1.0.2" } }, @@ -27147,26 +28862,52 @@ "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", "peer": true }, - "postcss": { - "version": "8.4.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", - "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "portscanner": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", + "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", + "devOptional": true, "requires": { - "nanoid": "^3.3.6", + "async": "^2.6.0", + "is-number-like": "^1.0.3" + }, + "dependencies": { + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "devOptional": true, + "requires": { + "lodash": "^4.17.14" + } + } + } + }, + "postcss": { + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "requires": { + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "postcss-loader": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.3.tgz", - "integrity": "sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", + "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", "requires": { - "cosmiconfig": "^8.2.0", - "jiti": "^1.18.2", - "semver": "^7.3.8" + "cosmiconfig": "^9.0.0", + "jiti": "^1.20.0", + "semver": "^7.5.4" } }, + "postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==" + }, "postcss-modules-extract-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", @@ -27174,9 +28915,9 @@ "requires": {} }, "postcss-modules-local-by-default": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", - "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz", + "integrity": "sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==", "requires": { "icss-utils": "^5.0.0", "postcss-selector-parser": "^6.0.2", @@ -27184,9 +28925,9 @@ } }, "postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz", + "integrity": "sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA==", "requires": { "postcss-selector-parser": "^6.0.4" } @@ -27200,9 +28941,9 @@ } }, "postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -27214,9 +28955,10 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true }, "prettier": { "version": "3.0.0", @@ -27227,7 +28969,8 @@ "pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==" + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "optional": true }, "pretty-hrtime": { "version": "1.0.3", @@ -27303,7 +29046,8 @@ "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "optional": true }, "public-encrypt": { "version": "4.0.3", @@ -27336,9 +29080,9 @@ } }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" }, "qjobs": { "version": "1.2.0", @@ -27400,7 +29144,8 @@ "querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "optional": true }, "queue-microtask": { "version": "1.2.3", @@ -27430,9 +29175,9 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "requires": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -27449,13 +29194,13 @@ } }, "read-package-json": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", - "integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-7.0.0.tgz", + "integrity": "sha512-uL4Z10OKV4p6vbdvIXB+OzhInYtIozl/VxUBPgNkBuUi2DeRonnuspmaVAMcrkmfjKGNmRndyQAbE7/AmzGwFg==", "requires": { "glob": "^10.2.2", "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^5.0.0", + "normalize-package-data": "^6.0.0", "npm-normalize-package-bin": "^3.0.0" }, "dependencies": { @@ -27468,21 +29213,21 @@ } }, "glob": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", - "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "requires": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" } }, "json-parse-even-better-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==" }, "minimatch": { "version": "9.0.3", @@ -27504,9 +29249,9 @@ }, "dependencies": { "json-parse-even-better-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==" } } }, @@ -27540,9 +29285,9 @@ } }, "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==" }, "regenerate": { "version": "1.4.2", @@ -27550,17 +29295,17 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", "requires": { "regenerate": "^1.4.2" } }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "regenerator-transform": { "version": "0.15.2", @@ -27575,22 +29320,6 @@ "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" }, - "regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, "regexpu-core": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", @@ -27649,11 +29378,11 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "requires": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -27682,6 +29411,33 @@ } } }, + "resp-modifier": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz", + "integrity": "sha512-U1+0kWC/+4ncRFYqQWTx/3qkfE6a4B/h3XXgmXypfa0SPZ3t7cbbaFk297PjQS/yov24R18h6OZe6iZwj3NSLw==", + "devOptional": true, + "requires": { + "debug": "^2.2.0", + "minimatch": "^3.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "devOptional": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "devOptional": true + } + } + }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -27725,17 +29481,31 @@ } }, "rollup": { - "version": "3.26.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.3.tgz", - "integrity": "sha512-7Tin0C8l86TkpcMtXvQu6saWH93nhG3dGQ1/+l5V2TDMceTxO7kDiK6GzbfLWNNxqJXm591PcEZUozZm51ogwQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", "requires": { + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@types/estree": "1.0.5", "fsevents": "~2.3.2" } }, "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==" }, "run-parallel": { "version": "1.2.0", @@ -27745,6 +29515,12 @@ "queue-microtask": "^1.2.2" } }, + "rx": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", + "integrity": "sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug==", + "devOptional": true + }, "rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -27764,19 +29540,26 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.64.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.64.1.tgz", - "integrity": "sha512-16rRACSOFEE8VN7SCgBu1MpYCyN7urj9At898tyzdXFhC+a+yOX5dXwAR7L8/IdPJ1NB8OYoXmD55DM30B2kEQ==", + "version": "1.71.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", + "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==", "requires": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" + }, + "dependencies": { + "immutable": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==" + } } }, "sass-loader": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.3.2.tgz", - "integrity": "sha512-CQbKl57kdEv+KDLquhC+gE3pXt74LEAzm+tzywcA0/aHZuub8wTErbjAoNI57rPUWRYRNC5WUnNl8eGJNbDdwg==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.1.1.tgz", + "integrity": "sha512-QX8AasDg75monlybel38BZ49JP5Z+uSKfKwF2rO7S74BywaRmGQMUBw9dtkS+ekyM/QnP+NOrRYq8ABMZ9G8jw==", "requires": { "neo-async": "^2.6.2" } @@ -27787,14 +29570,6 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "optional": true }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "requires": { - "xmlchars": "^2.2.0" - } - }, "schema-utils": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", @@ -27857,10 +29632,11 @@ "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" }, "selfsigned": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", - "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "requires": { + "@types/node-forge": "^1.3.0", "node-forge": "^1" } }, @@ -28006,19 +29782,28 @@ "send": "0.18.0" } }, + "server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", + "devOptional": true + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "requires": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" } }, "setprototypeof": { @@ -28065,9 +29850,9 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, "shell-quote": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==" }, "side-channel": { "version": "1.0.4", @@ -28085,13 +29870,16 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "sigstore": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.7.0.tgz", - "integrity": "sha512-KP7QULhWdlu3hlp+jw2EvgWKlOGOY9McLj/jrchLjHNlNPK0KWIwF919cbmOp6QiKXLmPijR2qH/5KYWlbtG9Q==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.2.2.tgz", + "integrity": "sha512-2A3WvXkQurhuMgORgT60r6pOWiCOO5LlEqY2ADxGBDGVYLSo5HN0uLtb68YpVpuL/Vi8mLTe7+0Dx2Fq8lLqEg==", "requires": { - "@sigstore/protobuf-specs": "^0.1.0", - "@sigstore/tuf": "^1.0.1", - "make-fetch-happen": "^11.0.1" + "@sigstore/bundle": "^2.2.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.0", + "@sigstore/sign": "^2.2.3", + "@sigstore/tuf": "^2.3.1", + "@sigstore/verify": "^1.1.0" } }, "simple-concat": { @@ -28194,8 +29982,7 @@ "version": "4.7.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.1.tgz", "integrity": "sha512-W+utHys2w//dhFjy7iQQu9sGd3eokCjGbl2r59tyLqNiJJBdIebn3GAKEXBr3osqHTObJi2die/25bCx2zsaaw==", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "accepts": "~1.3.4", "base64id": "~2.0.0", @@ -28210,8 +29997,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "ws": "~8.11.0" }, @@ -28220,18 +30006,28 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "optional": true, - "peer": true, + "devOptional": true, "requires": {} } } }, + "socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "devOptional": true, + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + } + }, "socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -28248,22 +30044,22 @@ } }, "socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", + "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", "requires": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" } }, "socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", + "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", "requires": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "socks": "^2.7.1" } }, "source-map": { @@ -28277,11 +30073,10 @@ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, "source-map-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.1.tgz", - "integrity": "sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", "requires": { - "abab": "^2.0.6", "iconv-lite": "^0.6.3", "source-map-js": "^1.0.2" }, @@ -28327,9 +30122,9 @@ } }, "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==" }, "spdx-expression-parse": { "version": "3.0.1", @@ -28341,9 +30136,9 @@ } }, "spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==" }, "spdy": { "version": "4.0.2", @@ -28391,6 +30186,11 @@ "through": "2" } }, + "sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" + }, "sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -28409,11 +30209,11 @@ } }, "ssri": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.4.tgz", - "integrity": "sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==", + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz", + "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==", "requires": { - "minipass": "^5.0.0" + "minipass": "^7.0.3" } }, "start-server-and-test": { @@ -28477,6 +30277,16 @@ "readable-stream": "^2.0.2" } }, + "stream-throttle": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz", + "integrity": "sha512-889+B9vN9dq7/vLbGyuHeZ6/ctf5sNuGWsDy89uNxkFTAgzy0eK7+w5fL3KLNRTkLle7EgZGvHUphZW0Q26MnQ==", + "devOptional": true, + "requires": { + "commander": "^2.2.0", + "limiter": "^1.0.5" + } + }, "streamroller": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz", @@ -28588,11 +30398,6 @@ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==" }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" - }, "syntax-error": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", @@ -28607,9 +30412,9 @@ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" }, "tar": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", - "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -28637,6 +30442,11 @@ } } }, + "minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" + }, "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -28645,9 +30455,9 @@ } }, "terser": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", - "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", + "version": "5.29.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz", + "integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==", "requires": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -28663,29 +30473,24 @@ } }, "terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "requires": { - "@jridgewell/trace-mapping": "^0.3.17", + "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" + "terser": "^5.26.0" }, "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" - }, "@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "schema-utils": { @@ -28713,7 +30518,8 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "throttleit": { "version": "1.0.0", @@ -28746,30 +30552,58 @@ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, "tinyify": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyify/-/tinyify-3.1.0.tgz", - "integrity": "sha512-r4tHoDkWhhoItWbxJ3KCHXask3hJN7gCUkR5PLfnQzQagTA6oDkzhCbiEDHkMqo7Ck7vVSA1pTP1gDc9p1AC1w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tinyify/-/tinyify-4.0.0.tgz", + "integrity": "sha512-jNDxImwUrJJAU2NyGG144J8aWx2ni39UuBo7ppCXFRmhSH0CbpWL4HgjNvrsAW05WQAgNZePwAlEemNuB+byaA==", "requires": { - "@goto-bus-stop/envify": "^5.0.0", - "acorn-node": "^1.8.2", + "@browserify/envify": "^6.0.0", + "@browserify/uglifyify": "^6.0.0", "browser-pack-flat": "^3.0.9", "bundle-collapser": "^1.3.0", "common-shakeify": "^1.1.1", - "dash-ast": "^1.0.0", "minify-stream": "^2.0.1", "multisplice": "^1.0.0", - "through2": "^3.0.1", - "uglifyify": "^5.0.0", - "unassertify": "^2.1.1" + "terser": "3.16.1", + "through2": "^4.0.2", + "unassertify": "^3.0.1" }, "dependencies": { - "through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "requires": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "terser": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.16.1.tgz", + "integrity": "sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==", + "requires": { + "commander": "~2.17.1", + "source-map": "~0.6.1", + "source-map-support": "~0.5.9" + } + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "requires": { + "readable-stream": "3" } } } @@ -28809,6 +30643,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "optional": true, "requires": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -28819,18 +30654,11 @@ "universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "optional": true } } }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "requires": { - "punycode": "^2.1.1" - } - }, "transform-ast": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/transform-ast/-/transform-ast-2.4.4.tgz", @@ -28878,6 +30706,13 @@ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==" }, + "ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "requires": {} + }, "ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -28914,35 +30749,18 @@ } }, "tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "tuf-js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.7.tgz", - "integrity": "sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.0.tgz", + "integrity": "sha512-ZSDngmP1z6zw+FIkIBjvOp/II/mIub/O7Pp12j1WNsiCpg5R5wAc//i555bBQsE44O94btLt0xM/Zr2LQjwdCg==", "requires": { - "@tufjs/models": "1.0.4", + "@tufjs/models": "2.0.0", "debug": "^4.3.4", - "make-fetch-happen": "^11.1.1" + "make-fetch-happen": "^13.0.0" } }, "tunnel-agent": { @@ -28966,11 +30784,12 @@ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" }, "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "^1.2.1" } }, "type-detect": { @@ -28980,9 +30799,9 @@ "optional": true }, "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" }, "type-is": { "version": "1.6.18", @@ -29004,9 +30823,9 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==" }, "ua-parser-js": { "version": "0.7.35", @@ -29015,94 +30834,43 @@ "optional": true, "peer": true }, - "uglifyify": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/uglifyify/-/uglifyify-5.0.2.tgz", - "integrity": "sha512-NcSk6pgoC+IgwZZ2tVLVHq+VNKSvLPlLkF5oUiHPVOJI0s/OlSVYEGXG9PCAH0hcyFZLyvt4KBdPAQBRlVDn1Q==", - "requires": { - "convert-source-map": "~1.1.0", - "minimatch": "^3.0.2", - "terser": "^3.7.5", - "through": "~2.3.4", - "xtend": "^4.0.1" - }, - "dependencies": { - "convert-source-map": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", - "integrity": "sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "terser": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", - "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", - "requires": { - "commander": "^2.19.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.10" - } - } - } - }, "umd": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==" }, "unassert": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/unassert/-/unassert-1.6.0.tgz", - "integrity": "sha512-GoMtWTwGSxSFuRD0NKmbjlx3VJkgvSogzDzMPpJXYmBZv6MIWButsyMqEYhMx3NI4osXACcZA9mXiBteXyJtRw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unassert/-/unassert-2.0.2.tgz", + "integrity": "sha512-P6OOg/aRdQmWH+b0g+T4U+9MgL+DG7w6oQPG+N3F2IMuvvd1WfZ5alT/Rjik2lMFVyhfACUxF7PGP1VCwSHlQA==", "requires": { - "acorn": "^7.0.0", - "call-matcher": "^2.0.0", - "deep-equal": "^1.0.0", - "espurify": "^2.0.1", - "estraverse": "^4.1.0", - "esutils": "^2.0.2", - "object-assign": "^4.1.0" + "estraverse": "^5.0.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + } } }, "unassertify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/unassertify/-/unassertify-2.1.1.tgz", - "integrity": "sha512-YIAaIlc6/KC9Oib8cVZLlpDDhK1UTEuaDyx9BwD97xqxDZC0cJOqwFcs/Y6K3m73B5VzHsRTBLXNO0dxS/GkTw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/unassertify/-/unassertify-3.0.1.tgz", + "integrity": "sha512-461ykSPY3oWU+39J5haiq7S/hcYy1oGJ2nHU92lqdL3jft+pSU6oAbb7o6VVmM7nZGLqppszgyzfpCnRBFgFtw==", "requires": { - "acorn": "^5.1.0", + "acorn": "^8.0.0", "convert-source-map": "^1.1.1", - "escodegen": "^1.6.1", - "multi-stage-sourcemap": "^0.2.1", + "escodegen": "^2.0.0", + "multi-stage-sourcemap": "^0.3.1", "through": "^2.3.7", - "unassert": "^1.3.1" + "unassert": "^2.0.0" }, "dependencies": { "acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==" - }, - "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==" } } }, @@ -29129,6 +30897,11 @@ "xtend": "^4.0.1" } }, + "undici": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.7.1.tgz", + "integrity": "sha512-+Wtb9bAQw6HYWzCnxrPTMVEV3Q1QjYanI0E4q02ehReMuquQdLTEFEYbfs7hcImVYKcQkWSwT6buEmSVIiDDtQ==" + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -29187,9 +30960,9 @@ "optional": true }, "update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "requires": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -29223,6 +30996,7 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "optional": true, "requires": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -29283,14 +31057,184 @@ } }, "vite": { - "version": "4.4.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.7.tgz", - "integrity": "sha512-6pYf9QJ1mHylfVh39HpuSfMPojPSKVxZvnclX1K1FyZ1PXDOcLBibdq5t1qxJSnL63ca8Wf4zts6mD8u8oc9Fw==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.5.tgz", + "integrity": "sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q==", "requires": { - "esbuild": "^0.18.10", - "fsevents": "~2.3.2", - "postcss": "^8.4.26", - "rollup": "^3.25.2" + "esbuild": "^0.19.3", + "fsevents": "~2.3.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" + }, + "dependencies": { + "@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "optional": true + }, + "esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "requires": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + } } }, "vm-browserify": { @@ -29305,22 +31249,6 @@ "optional": true, "peer": true }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "requires": { - "xml-name-validator": "^3.0.0" - } - }, "wait-on": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz", @@ -29389,24 +31317,19 @@ "defaults": "^1.0.3" } }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" - }, "webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", + "version": "5.90.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz", + "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==", "requires": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", + "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.11.5", "@webassemblyjs/wasm-edit": "^1.11.5", "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.15.0", "es-module-lexer": "^1.2.1", @@ -29420,7 +31343,7 @@ "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", + "terser-webpack-plugin": "^5.3.10", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, @@ -29497,10 +31420,15 @@ "ws": "^8.13.0" }, "dependencies": { + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" + }, "webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", "requires": { "colorette": "^2.0.10", "memfs": "^3.4.3", @@ -29508,21 +31436,16 @@ "range-parser": "^1.2.1", "schema-utils": "^4.0.0" } - }, - "ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "requires": {} } } }, "webpack-merge": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", - "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", "requires": { "clone-deep": "^4.0.1", + "flat": "^5.0.2", "wildcard": "^2.0.0" } }, @@ -29554,29 +31477,6 @@ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" - }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -29616,24 +31516,11 @@ "is-typed-array": "^1.1.3" } }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==" }, - "word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==" - }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -29711,9 +31598,9 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "requires": {} }, "xhr2": { @@ -29721,15 +31608,11 @@ "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.2.1.tgz", "integrity": "sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw==" }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "devOptional": true }, "xtend": { "version": "4.0.2", @@ -29861,17 +31744,17 @@ "dev": true }, "zone.js": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.1.tgz", - "integrity": "sha512-+bIeDAFEBYuXRuU3qGQvzdPap+N1zjM4KkBAiiQuVVCrHrhjDuY6VkUhNa5+U27+9w0q3fbKiMCbpJ0XzMmSWA==", + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.4.tgz", + "integrity": "sha512-NtTUvIlNELez7Q1DzKVIFZBzNb646boQMgpATo9z3Ftuu/gWvzxCW7jdjcUDoRGxRikrhVHB/zLXh1hxeJawvw==", "requires": { "tslib": "^2.3.0" } }, "zrender": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.4.4.tgz", - "integrity": "sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz", + "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==", "requires": { "tslib": "2.3.0" }, diff --git a/frontend/package.json b/frontend/package.json index 330250871..5864a2284 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,13 +34,12 @@ "start:local-prod": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-prod", "start:local-staging": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-staging", "start:mixed": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c mixed", - "build": "npm run generate-config && npm run ng -- build --configuration production --localize && npm run sync-assets && npm run build-mempool.js", + "build": "npm run generate-config && npm run ng -- build --configuration production --localize && npm run sync-assets-dev && npm run sync-assets && npm run build-mempool.js", "sync-assets": "rsync -av ./src/resources ./dist/mempool/browser && node sync-assets.js 'dist/mempool/browser/resources/'", "sync-assets-dev": "node sync-assets.js 'src/resources/'", "generate-config": "node generate-config.js", - "build-mempool.js": "npm run build-mempool-js && npm run build-mempool-liquid-js && npm run build-mempool-bisq-js", + "build-mempool.js": "npm run build-mempool-js && npm run build-mempool-liquid-js", "build-mempool-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index.js --standalone mempoolJS > ./dist/mempool/browser/en-US/mempool.js", - "build-mempool-bisq-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index-bisq.js --standalone bisqJS > ./dist/mempool/browser/en-US/bisq.js", "build-mempool-liquid-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index-liquid.js --standalone liquidJS > ./dist/mempool/browser/en-US/liquid.js", "test": "npm run ng -- test", "lint": "./node_modules/.bin/eslint . --ext .ts", @@ -48,69 +47,75 @@ "prettier": "prettier --write \"src/app/**/*.{js,json,css,scss,less,md,ts,html,component.html}\"", "e2e": "npm run generate-config && npm run ng -- e2e", "e2e:ci": "npm run cypress:run:ci", - "config:defaults:mempool": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true LIQUID_TESTNET_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=mempool BLOCK_WEIGHT_UNITS=4000000 && npm run generate-config", - "config:defaults:liquid": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true LIQUID_TESTNET_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=liquid BLOCK_WEIGHT_UNITS=300000 && npm run generate-config", - "config:defaults:bisq": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=bisq BLOCK_WEIGHT_UNITS=4000000 && npm run generate-config", + "dev:ssr": "npm run generate-config && ng run mempool:serve-ssr", + "serve:ssr": "npm run generate-config && node server.run.js", + "build:ssr": "npm run build && ng run mempool:server:production && ./node_modules/typescript/bin/tsc server.run.ts", + "config:defaults:mempool": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true LIQUID_TESTNET_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=mempool BLOCK_WEIGHT_UNITS=4000000 && npm run generate-config", + "config:defaults:liquid": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true LIQUID_TESTNET_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=liquid BLOCK_WEIGHT_UNITS=300000 && npm run generate-config", "prerender": "npm run ng -- run mempool:prerender", "cypress:open": "cypress open", "cypress:run": "cypress run", "cypress:run:record": "cypress run --record", - "cypress:open:ci": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:open", - "cypress:run:ci": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:run:record", - "cypress:open:ci:staging": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:open", - "cypress:run:ci:staging": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:run:record" + "cypress:open:ci": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:open", + "cypress:run:ci": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:run:record", + "cypress:open:ci:staging": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:open", + "cypress:run:ci:staging": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:run:record" }, "dependencies": { - "@angular-devkit/build-angular": "^16.2.0", - "@angular/animations": "^16.2.2", - "@angular/cli": "^16.2.0", - "@angular/common": "^16.2.2", - "@angular/compiler": "^16.2.2", - "@angular/core": "^16.2.2", - "@angular/forms": "^16.2.2", - "@angular/localize": "^16.2.2", - "@angular/platform-browser": "^16.2.2", - "@angular/platform-browser-dynamic": "^16.2.2", - "@angular/platform-server": "^16.2.2", - "@angular/router": "^16.2.2", - "@fortawesome/angular-fontawesome": "~0.13.0", + "@angular-devkit/build-angular": "^17.3.1", + "@angular/animations": "^17.3.1", + "@angular/cli": "^17.3.1", + "@angular/common": "^17.3.1", + "@angular/compiler": "^17.3.1", + "@angular/core": "^17.3.1", + "@angular/forms": "^17.3.1", + "@angular/localize": "^17.3.1", + "@angular/platform-browser": "^17.3.1", + "@angular/platform-browser-dynamic": "^17.3.1", + "@angular/platform-server": "^17.3.1", + "@angular/router": "^17.3.1", + "@angular/ssr": "^17.3.1", + "@fortawesome/angular-fontawesome": "~0.14.1", "@fortawesome/fontawesome-common-types": "~6.5.1", "@fortawesome/fontawesome-svg-core": "~6.5.1", "@fortawesome/free-solid-svg-icons": "~6.5.1", "@mempool/mempool.js": "2.3.0", - "@ng-bootstrap/ng-bootstrap": "^15.1.0", + "@ng-bootstrap/ng-bootstrap": "^16.0.0", "@types/qrcode": "~1.5.0", "bootstrap": "~4.6.2", "browserify": "^17.0.0", "clipboard": "^2.0.11", "domino": "^2.1.6", - "echarts": "~5.4.3", + "echarts": "~5.5.0", "lightweight-charts": "~3.8.0", - "ngx-echarts": "~16.2.0", - "ngx-infinite-scroll": "^16.0.0", + "ngx-echarts": "~17.1.0", + "ngx-infinite-scroll": "^17.0.0", "qrcode": "1.5.1", "rxjs": "~7.8.1", - "tinyify": "^3.1.0", + "esbuild": "^0.20.2", + "tinyify": "^4.0.0", "tlite": "^0.1.9", "tslib": "~2.6.0", - "zone.js": "~0.13.1" + "zone.js": "~0.14.4" }, "devDependencies": { - "@angular/compiler-cli": "^16.1.5", - "@angular/language-service": "^16.1.5", + "@angular/compiler-cli": "^17.3.1", + "@angular/language-service": "^17.3.1", "@types/node": "^18.11.9", - "@typescript-eslint/eslint-plugin": "^5.48.1", - "@typescript-eslint/parser": "^5.48.1", - "eslint": "^8.31.0", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", + "eslint": "^8.57.0", + "browser-sync": "^3.0.0", "http-proxy-middleware": "~2.0.6", "prettier": "^3.0.0", + "source-map-support": "^0.5.21", "ts-node": "~10.9.1", - "typescript": "~4.9.3" + "typescript": "~5.4.3" }, "optionalDependencies": { "@cypress/schematic": "^2.5.0", "@types/cypress": "^1.1.3", - "cypress": "^13.6.2", + "cypress": "^13.7.0", "cypress-fail-on-console-error": "~5.1.0", "cypress-wait-until": "^2.0.1", "mock-socket": "~9.3.1", diff --git a/frontend/proxy.conf.js b/frontend/proxy.conf.js index f5384bef0..b63d343e2 100644 --- a/frontend/proxy.conf.js +++ b/frontend/proxy.conf.js @@ -22,7 +22,6 @@ PROXY_CONFIG = [ { context: ['*', '/api/**', '!/api/v1/ws', - '!/bisq', '!/bisq/**', '!/bisq/', '!/liquid', '!/liquid/**', '!/liquid/', '!/liquidtestnet', '!/liquidtestnet/**', '!/liquidtestnet/', '/testnet/api/**', '/signet/api/**' @@ -39,16 +38,6 @@ PROXY_CONFIG = [ secure: false, changeOrigin: true, }, - { - context: ['/api/bisq**', '/bisq/api/**'], - target: "https://bisq.markets", - pathRewrite: { - "^/api/bisq/": "/bisq/api" - }, - ws: true, - secure: false, - changeOrigin: true - }, { context: ['/api/liquid**', '/liquid/api/**'], target: "https://liquid.network", diff --git a/frontend/proxy.conf.local-esplora.js b/frontend/proxy.conf.local-esplora.js index a7137f3bc..0cdc9d459 100644 --- a/frontend/proxy.conf.local-esplora.js +++ b/frontend/proxy.conf.local-esplora.js @@ -67,40 +67,6 @@ if (configContent && configContent.BASE_MODULE === 'liquid') { ]); } - -if (configContent && configContent.BASE_MODULE === 'bisq') { - PROXY_CONFIG.push(...[ - { - context: ['/bisq/api/v1/ws'], - target: `http://127.0.0.1:8999`, - secure: false, - ws: true, - changeOrigin: true, - proxyTimeout: 30000, - pathRewrite: { - "^/bisq": "" - }, - }, - { - context: ['/bisq/api/v1/**'], - target: `http://127.0.0.1:8999`, - secure: false, - changeOrigin: true, - proxyTimeout: 30000, - }, - { - context: ['/bisq/api/**'], - target: `http://127.0.0.1:8999`, - secure: false, - changeOrigin: true, - proxyTimeout: 30000, - pathRewrite: { - "^/bisq/api/": "/api/v1/bisq/" - }, - } - ]); -} - PROXY_CONFIG.push(...[ { context: ['/testnet/api/v1/lightning/**'], diff --git a/frontend/proxy.conf.local.js b/frontend/proxy.conf.local.js index 3a502e0ed..e7bfeee70 100644 --- a/frontend/proxy.conf.local.js +++ b/frontend/proxy.conf.local.js @@ -67,40 +67,6 @@ if (configContent && configContent.BASE_MODULE === 'liquid') { ]); } - -if (configContent && configContent.BASE_MODULE === 'bisq') { - PROXY_CONFIG.push(...[ - { - context: ['/bisq/api/v1/ws'], - target: `http://localhost:8999`, - secure: false, - ws: true, - changeOrigin: true, - proxyTimeout: 30000, - pathRewrite: { - "^/bisq": "" - }, - }, - { - context: ['/bisq/api/v1/**'], - target: `http://localhost:8999`, - secure: false, - changeOrigin: true, - proxyTimeout: 30000, - }, - { - context: ['/bisq/api/**'], - target: `http://localhost:8999`, - secure: false, - changeOrigin: true, - proxyTimeout: 30000, - pathRewrite: { - "^/bisq/api/": "/api/v1/bisq/" - }, - } - ]); -} - PROXY_CONFIG.push(...[ { context: ['/testnet/api/v1/lightning/**'], diff --git a/frontend/proxy.conf.mixed.js b/frontend/proxy.conf.mixed.js index 76bb06607..db8af5d95 100644 --- a/frontend/proxy.conf.mixed.js +++ b/frontend/proxy.conf.mixed.js @@ -61,39 +61,6 @@ if (configContent && configContent.BASE_MODULE === 'liquid') { ]); } -if (configContent && configContent.BASE_MODULE === 'bisq') { - PROXY_CONFIG.push(...[ - { - context: ['/bisq/api/v1/ws'], - target: `http://localhost:8999`, - secure: false, - ws: true, - changeOrigin: true, - proxyTimeout: 30000, - pathRewrite: { - "^/bisq": "" - }, - }, - { - context: ['/bisq/api/v1/**'], - target: `http://localhost:8999`, - secure: false, - changeOrigin: true, - proxyTimeout: 30000, - }, - { - context: ['/bisq/api/**'], - target: `http://localhost:8999`, - secure: false, - changeOrigin: true, - proxyTimeout: 30000, - pathRewrite: { - "^/bisq/api/": "/api/v1/bisq/" - }, - }, - ]); -} - PROXY_CONFIG.push(...[ { context: ['/api/v1/services/**'], diff --git a/frontend/proxy.conf.staging.js b/frontend/proxy.conf.staging.js index 0cf366ca7..e24662038 100644 --- a/frontend/proxy.conf.staging.js +++ b/frontend/proxy.conf.staging.js @@ -5,7 +5,6 @@ let PROXY_CONFIG = require('./proxy.conf'); PROXY_CONFIG.forEach(entry => { entry.target = entry.target.replace("mempool.space", "mempool-staging.fra.mempool.space"); entry.target = entry.target.replace("liquid.network", "liquid-staging.fra.mempool.space"); - entry.target = entry.target.replace("bisq.markets", "bisq-staging.fra.mempool.space"); }); module.exports = PROXY_CONFIG; diff --git a/frontend/server.run.ts b/frontend/server.run.ts new file mode 100644 index 000000000..692af7023 --- /dev/null +++ b/frontend/server.run.ts @@ -0,0 +1,104 @@ +import './src/resources/config.js'; + +import * as domino from 'domino'; +import * as express from 'express'; +import * as fs from 'fs'; +import * as path from 'path'; + +const {readFileSync, existsSync} = require('fs'); +const {createProxyMiddleware} = require('http-proxy-middleware'); + +const template = fs.readFileSync(path.join(process.cwd(), 'dist/mempool/browser/en-US/', 'index.html')).toString(); +const win = domino.createWindow(template); + +// @ts-ignore +win.__env = global.__env; + +// @ts-ignore +win.matchMedia = () => { + return { + matches: true + }; +}; +// @ts-ignore +win.setTimeout = (fn) => { fn(); }; +win.document.body.scrollTo = (() => {}); +// @ts-ignore +global['window'] = win; +global['document'] = win.document; +// @ts-ignore +global['history'] = { state: { } }; + +global['localStorage'] = { + getItem: () => '', + setItem: () => {}, + removeItem: () => {}, + clear: () => {}, + length: 0, + key: () => '', +}; + +/** + * Return the list of supported and actually active locales + */ +function getActiveLocales() { + const angularConfig = JSON.parse(readFileSync('angular.json', 'utf8')); + + const supportedLocales = [ + angularConfig.projects.mempool.i18n.sourceLocale, + ...Object.keys(angularConfig.projects.mempool.i18n.locales), + ]; + + return supportedLocales.filter(locale => locale === 'en-US' && existsSync(`./dist/mempool/server/${locale}`)); + // return supportedLocales.filter(locale => existsSync(`./dist/mempool/server/${locale}`)); +} + +function app() { + const server = express(); + + // proxy websocket + server.get('/api/v1/ws', createProxyMiddleware({ + target: 'ws://localhost:4200/api/v1/ws', + changeOrigin: true, + ws: true, + logLevel: 'debug' + })); + // proxy API to nginx + server.get('/api/**', createProxyMiddleware({ + // @ts-ignore + target: win.__env.NGINX_PROTOCOL + '://' + win.__env.NGINX_HOSTNAME + ':' + win.__env.NGINX_PORT, + changeOrigin: true, + })); + server.get('/resources/**', express.static('./src')); + + + // map / and /en to en-US + const defaultLocale = 'en-US'; + console.log(`serving default locale: ${defaultLocale}`); + const appServerModule = require(`./dist/mempool/server/${defaultLocale}/main.js`); + server.use('/', appServerModule.app(defaultLocale)); + server.use('/en', appServerModule.app(defaultLocale)); + + // map each locale to its localized main.js + getActiveLocales().forEach(locale => { + console.log('serving locale:', locale); + const appServerModule = require(`./dist/mempool/server/${locale}/main.js`); + + // map everything to itself + server.use(`/${locale}`, appServerModule.app(locale)); + + }); + + return server; +} + +function run() { + const port = process.env.PORT || 4000; + + // Start up the Node server + app().listen(port, () => { + console.log(`Node Express server listening on port ${port}`); + }); +} + +run(); \ No newline at end of file diff --git a/frontend/server.ts b/frontend/server.ts new file mode 100644 index 000000000..8e6c6b634 --- /dev/null +++ b/frontend/server.ts @@ -0,0 +1,108 @@ +import 'zone.js'; +import './src/resources/config.js'; + +import { CommonEngine } from '@angular/ssr'; +import * as express from 'express'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as domino from 'domino'; + +import { join } from 'path'; +import { AppServerModule } from './src/main.server'; +import { APP_BASE_HREF } from '@angular/common'; +import { existsSync } from 'fs'; + +import { ResizeObserver } from './shims'; + +const commonEngine = new CommonEngine(); + +const template = fs.readFileSync(path.join(process.cwd(), 'dist/mempool/browser/en-US/', 'index.html')).toString(); +const win = domino.createWindow(template); + +// @ts-ignore +win.__env = global.__env; + +// @ts-ignore +win.matchMedia = (media) => { + return { + media, + matches: true, + }; +}; + +// @ts-ignore +win.setTimeout = (fn) => { fn(); }; +win.document.body.scrollTo = (() => {}); +win['ResizeObserver'] = ResizeObserver; +// @ts-ignore +global['window'] = win; +// @ts-ignore +global['document'] = win.document; +// @ts-ignore +global['history'] = { state: { } }; +// @ts-ignore +Object.defineProperty(global, 'navigator', { + value: win.navigator, + writable: true +}); + +global['localStorage'] = { + getItem: () => '', + setItem: () => {}, + removeItem: () => {}, + clear: () => {}, + length: 0, + key: () => '', +}; + +// The Express app is exported so that it can be used by serverless Functions. +export function app(locale: string): express.Express { + const server = express(); + const distFolder = join(process.cwd(), `dist/mempool/browser/${locale}`); + const indexHtml = join(distFolder, 'index.html'); + + server.set('view engine', 'html'); + server.set('views', distFolder); + + // static file handler so we send HTTP 404 to nginx + server.get('/**.(css|js|json|ico|webmanifest|png|jpg|jpeg|svg|mp4)*', express.static(distFolder, { maxAge: '1y', fallthrough: false })); + // handle page routes + server.get('*', (req, res, next) => { + const { protocol, originalUrl, baseUrl, headers } = req; + + commonEngine + .render({ + bootstrap: AppServerModule, + documentFilePath: indexHtml, + url: `${protocol}://${headers.host}${originalUrl}`, + publicPath: distFolder, + providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], + }) + .then((html) => res.send(html)) + .catch((err) => next(err)); + }); + + return server; +} + + +// only used for development mode +function run(): void { + const port = process.env.PORT || 4000; + + // Start up the Node server + const server = app('en-US'); + server.listen(port, () => { + console.log(`Node Express server listening on port ${port}`); + }); +} + +// Webpack will replace 'require' with '__webpack_require__' +// '__non_webpack_require__' is a proxy to Node 'require' +// The below code is to ensure that the server is run only when not requiring the bundle. +declare const __non_webpack_require__: NodeRequire; +const mainModule = __non_webpack_require__.main; +const moduleFilename = mainModule && mainModule.filename || ''; +if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { + run(); +} \ No newline at end of file diff --git a/frontend/shims.ts b/frontend/shims.ts new file mode 100644 index 000000000..50f1b6f33 --- /dev/null +++ b/frontend/shims.ts @@ -0,0 +1,7 @@ +export class ResizeObserver { + constructor() {} + + disconnect() {} + observe() {} + unobserve() {} +} \ No newline at end of file diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index e123a1525..78811a6b7 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -6,6 +6,7 @@ import { EightBlocksComponent } from './components/eight-blocks/eight-blocks.com import { MempoolBlockViewComponent } from './components/mempool-block-view/mempool-block-view.component'; import { ClockComponent } from './components/clock/clock.component'; import { StatusViewComponent } from './components/status-view/status-view.component'; +import { AddressGroupComponent } from './components/address-group/address-group.component'; const browserWindow = window || {}; // @ts-ignore @@ -26,6 +27,14 @@ let routes: Routes = [ loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule), data: { preload: true }, }, + { + path: 'wallet', + children: [], + component: AddressGroupComponent, + data: { + networkSpecific: true, + } + }, { path: 'status', data: { networks: ['bitcoin', 'liquid'] }, @@ -61,6 +70,14 @@ let routes: Routes = [ loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule), data: { preload: true }, }, + { + path: 'wallet', + children: [], + component: AddressGroupComponent, + data: { + networkSpecific: true, + } + }, { path: 'status', data: { networks: ['bitcoin', 'liquid'] }, @@ -88,6 +105,14 @@ let routes: Routes = [ loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule), data: { preload: true }, }, + { + path: 'wallet', + children: [], + component: AddressGroupComponent, + data: { + networkSpecific: true, + } + }, { path: 'preview', children: [ @@ -145,13 +170,6 @@ let routes: Routes = [ }, ]; -if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'bisq') { - routes = [{ - path: '', - loadChildren: () => import('./bisq/bisq.module').then(m => m.BisqModule) - }]; -} - if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { routes = [ { @@ -168,6 +186,14 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { loadChildren: () => import ('./liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule), data: { preload: true }, }, + { + path: 'wallet', + children: [], + component: AddressGroupComponent, + data: { + networkSpecific: true, + } + }, { path: 'status', data: { networks: ['bitcoin', 'liquid'] }, @@ -195,6 +221,14 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { loadChildren: () => import ('./liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule), data: { preload: true }, }, + { + path: 'wallet', + children: [], + component: AddressGroupComponent, + data: { + networkSpecific: true, + } + }, { path: 'preview', children: [ diff --git a/frontend/src/app/app.constants.ts b/frontend/src/app/app.constants.ts index f510c6480..17105d97e 100644 --- a/frontend/src/app/app.constants.ts +++ b/frontend/src/app/app.constants.ts @@ -268,4 +268,134 @@ export const fiatCurrencies = { code: 'USD', indexed: true, }, + BGN: { + name: 'Bulgarian Lev', + code: 'BGN', + indexed: true, + }, + BRL: { + name: 'Brazilian Real', + code: 'BRL', + indexed: true, + }, + CNY: { + name: 'Chinese Yuan', + code: 'CNY', + indexed: true, + }, + CZK: { + name: 'Czech Koruna', + code: 'CZK', + indexed: true, + }, + DKK: { + name: 'Danish Krone', + code: 'DKK', + indexed: true, + }, + HKD: { + name: 'Hong Kong Dollar', + code: 'HKD', + indexed: true, + }, + HRK: { + name: 'Croatian Kuna', + code: 'HRK', + indexed: true, + }, + HUF: { + name: 'Hungarian Forint', + code: 'HUF', + indexed: true, + }, + IDR: { + name: 'Indonesian Rupiah', + code: 'IDR', + indexed: true, + }, + ILS: { + name: 'Israeli Shekel', + code: 'ILS', + indexed: true, + }, + INR: { + name: 'Indian Rupee', + code: 'INR', + indexed: true, + }, + ISK: { + name: 'Icelandic Krona', + code: 'ISK', + indexed: true, + }, + KRW: { + name: 'South Korean Won', + code: 'KRW', + indexed: true, + }, + MXN: { + name: 'Mexican Peso', + code: 'MXN', + indexed: true, + }, + MYR: { + name: 'Malaysian Ringgit', + code: 'MYR', + indexed: true, + }, + NOK: { + name: 'Norwegian Krone', + code: 'NOK', + indexed: true, + }, + NZD: { + name: 'New Zealand Dollar', + code: 'NZD', + indexed: true, + }, + PHP: { + name: 'Philippine Peso', + code: 'PHP', + indexed: true, + }, + PLN: { + name: 'Polish Zloty', + code: 'PLN', + indexed: true, + }, + RON: { + name: 'Romanian Leu', + code: 'RON', + indexed: true, + }, + RUB: { + name: 'Russian Ruble', + code: 'RUB', + indexed: true, + }, + SEK: { + name: 'Swedish Krona', + code: 'SEK', + indexed: true, + }, + SGD: { + name: 'Singapore Dollar', + code: 'SGD', + indexed: true, + }, + THB: { + name: 'Thai Baht', + code: 'THB', + indexed: true, + }, + TRY: { + name: 'Turkish Lira', + code: 'TRY', + indexed: true, + }, + ZAR: { + name: 'South African Rand', + code: 'ZAR', + indexed: true, + }, }; \ No newline at end of file diff --git a/frontend/src/app/app.server.module.ts b/frontend/src/app/app.module.server.ts similarity index 63% rename from frontend/src/app/app.server.module.ts rename to frontend/src/app/app.module.server.ts index ff4885db1..4149fa593 100644 --- a/frontend/src/app/app.server.module.ts +++ b/frontend/src/app/app.module.server.ts @@ -1,20 +1,23 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { NgModule } from '@angular/core'; -import { ServerModule, ServerTransferStateModule } from '@angular/platform-server'; +import { ServerModule } from '@angular/platform-server'; +import { ZONE_SERVICE } from './injection-tokens'; import { AppModule } from './app.module'; import { AppComponent } from './components/app/app.component'; import { HttpCacheInterceptor } from './services/http-cache.interceptor'; +import { ZoneService } from './services/zone.service'; + @NgModule({ imports: [ AppModule, ServerModule, - ServerTransferStateModule, ], providers: [ - { provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true } + { provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true }, + { provide: ZONE_SERVICE, useClass: ZoneService }, ], bootstrap: [AppComponent], }) -export class AppServerModule {} +export class AppServerModule {} \ No newline at end of file diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 378ef11ed..e3f585a25 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -2,6 +2,7 @@ import { BrowserModule } from '@angular/platform-browser'; import { ModuleWithProviders, NgModule } from '@angular/core'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ZONE_SERVICE } from './injection-tokens'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './components/app/app.component'; import { ElectrsApiService } from './services/electrs-api.service'; @@ -13,6 +14,7 @@ import { WebsocketService } from './services/websocket.service'; import { AudioService } from './services/audio.service'; import { SeoService } from './services/seo.service'; import { OpenGraphService } from './services/opengraph.service'; +import { ZoneService } from './services/zone-shim.service'; import { SharedModule } from './shared/shared.module'; import { StorageService } from './services/storage.service'; import { HttpCacheInterceptor } from './services/http-cache.interceptor'; @@ -22,6 +24,7 @@ import { FiatCurrencyPipe } from './shared/pipes/fiat-currency.pipe'; import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-string.pipe'; import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe'; import { AppPreloadingStrategy } from './app.preloading-strategy'; +import { ServicesApiServices } from './services/services-api.service'; const providers = [ ElectrsApiService, @@ -40,7 +43,9 @@ const providers = [ FiatCurrencyPipe, CapAddressPipe, AppPreloadingStrategy, - { provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true } + ServicesApiServices, + { provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true }, + { provide: ZONE_SERVICE, useClass: ZoneService }, ]; @NgModule({ diff --git a/frontend/src/app/bisq/bisq-address/bisq-address.component.html b/frontend/src/app/bisq/bisq-address/bisq-address.component.html deleted file mode 100644 index c2cbbb76a..000000000 --- a/frontend/src/app/bisq/bisq-address/bisq-address.component.html +++ /dev/null @@ -1,110 +0,0 @@ -
-

Address

- - - - - -
- -
- - -
- -
-
- - - - - - - - - - - - - - - -
Total received{{ totalReceived / 100 | number: '1.2-2' }} BSQ
Total sent{{ totalSent / 100 | number: '1.2-2' }} BSQ
Balance{{ (totalReceived - totalSent) / 100 | number: '1.2-2' }} BSQ
-
-
-
-
- -
-
-
- -
- -
- -

- - {{ i }} transaction - {{ i }} transactions -

- - - -
- - {{ tx.id | shortenString : 16 }} - {{ tx.id }} - -
- ‎{{ tx.time | date:'yyyy-MM-dd HH:mm' }} -
-
-
- - - -
-
- -
- - - -
-
-
- - - - - - - - - - - - -
-
-
-
- -
-
-
- -
- - -
- Error loading address data. -
- {{ error.error }} -
-
- -
- -
\ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-address/bisq-address.component.scss b/frontend/src/app/bisq/bisq-address/bisq-address.component.scss deleted file mode 100644 index e7d0a9fd0..000000000 --- a/frontend/src/app/bisq/bisq-address/bisq-address.component.scss +++ /dev/null @@ -1,75 +0,0 @@ -.qr-wrapper { - background-color: #FFF; - padding: 10px; - padding-bottom: 5px; - display: inline-block; -} - -.qrcode-col { - text-align: center; -} - -.qrcode-col > div { - margin: 20px auto 5px; - @media (min-width: 768px) { - text-align: center; - margin: auto; - } -} - -.fiat { - display: block; - font-size: 13px; - @media (min-width: 768px) { - display: inline-block; - font-size: 14px; - margin-left: 10px; - } -} -.table { - tr td { - &:last-child { - text-align: right; - @media (min-width: 768px) { - text-align: left; - } - } - } -} - -h1 { - margin: 0px; - padding: 0px; - @media (min-width: 576px) { - float: left; - margin-right: 10px; - } -} - -.address-link { - line-height: 26px; - margin-left: 0px; - top: 14px; - position: relative; - display: flex; - flex-direction: row; - @media (min-width: 768px) { - line-height: 38px; - } -} - -.row{ - flex-direction: column; - @media (min-width: 576px) { - flex-direction: row; - } -} - -@media (max-width: 767.98px) { - .mobile-bottomcol { - margin-top: 15px; - } - .details-table td:first-child { - white-space: pre-wrap; - } -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-address/bisq-address.component.ts b/frontend/src/app/bisq/bisq-address/bisq-address.component.ts deleted file mode 100644 index 1e4f0a178..000000000 --- a/frontend/src/app/bisq/bisq-address/bisq-address.component.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { SeoService } from '../../services/seo.service'; -import { switchMap, filter, catchError } from 'rxjs/operators'; -import { ParamMap, ActivatedRoute } from '@angular/router'; -import { Subscription, of } from 'rxjs'; -import { BisqTransaction } from '../bisq.interfaces'; -import { BisqApiService } from '../bisq-api.service'; -import { WebsocketService } from '../../services/websocket.service'; - -@Component({ - selector: 'app-bisq-address', - templateUrl: './bisq-address.component.html', - styleUrls: ['./bisq-address.component.scss'] -}) -export class BisqAddressComponent implements OnInit, OnDestroy { - transactions: BisqTransaction[]; - addressString: string; - isLoadingAddress = true; - error: any; - mainSubscription: Subscription; - - totalReceived = 0; - totalSent = 0; - - constructor( - private websocketService: WebsocketService, - private route: ActivatedRoute, - private seoService: SeoService, - private bisqApiService: BisqApiService, - ) { } - - ngOnInit() { - this.websocketService.want(['blocks']); - - this.mainSubscription = this.route.paramMap - .pipe( - switchMap((params: ParamMap) => { - this.error = undefined; - this.isLoadingAddress = true; - this.transactions = null; - document.body.scrollTo(0, 0); - this.addressString = params.get('id') || ''; - this.seoService.setTitle($localize`:@@bisq-address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`); - this.seoService.setDescription($localize`:@@meta.description.bisq.address:See current balance, pending transactions, and history of confirmed transactions for BSQ address ${this.addressString}:INTERPOLATION:.`); - - return this.bisqApiService.getAddress$(this.addressString) - .pipe( - catchError((err) => { - this.isLoadingAddress = false; - this.error = err; - this.seoService.logSoft404(); - console.log(err); - return of(null); - }) - ); - }), - filter((transactions) => transactions !== null) - ) - .subscribe((transactions: BisqTransaction[]) => { - this.transactions = transactions; - this.updateChainStats(); - this.isLoadingAddress = false; - }, - (error) => { - console.log(error); - this.error = error; - this.seoService.logSoft404(); - this.isLoadingAddress = false; - }); - } - - updateChainStats() { - const shortenedAddress = this.addressString.substr(1); - - this.totalSent = this.transactions.reduce((acc, tx) => - acc + tx.inputs - .filter((input) => input.address === shortenedAddress) - .reduce((a, input) => a + input.bsqAmount, 0), 0); - - this.totalReceived = this.transactions.reduce((acc, tx) => - acc + tx.outputs - .filter((output) => output.address === shortenedAddress) - .reduce((a, output) => a + output.bsqAmount, 0), 0); - } - - ngOnDestroy() { - this.mainSubscription.unsubscribe(); - } -} diff --git a/frontend/src/app/bisq/bisq-api.service.ts b/frontend/src/app/bisq/bisq-api.service.ts deleted file mode 100644 index 2d8994d86..000000000 --- a/frontend/src/app/bisq/bisq-api.service.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse, HttpParams } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { BisqTransaction, BisqBlock, BisqStats, MarketVolume, Trade, Markets, Tickers, Offers, Currencies, HighLowOpenClose, SummarizedInterval } from './bisq.interfaces'; - -const API_BASE_URL = '/bisq/api'; - -@Injectable({ - providedIn: 'root' -}) -export class BisqApiService { - apiBaseUrl: string; - - constructor( - private httpClient: HttpClient, - ) { } - - getStats$(): Observable { - return this.httpClient.get(API_BASE_URL + '/stats'); - } - - getTransaction$(txId: string): Observable { - return this.httpClient.get(API_BASE_URL + '/tx/' + txId); - } - - listTransactions$(start: number, length: number, types: string[]): Observable> { - let params = new HttpParams(); - types.forEach((t: string) => { - params = params.append('types[]', t); - }); - return this.httpClient.get(API_BASE_URL + `/txs/${start}/${length}`, { params, observe: 'response' }); - } - - getBlock$(hash: string): Observable { - return this.httpClient.get(API_BASE_URL + '/block/' + hash); - } - - listBlocks$(start: number, length: number): Observable> { - return this.httpClient.get(API_BASE_URL + `/blocks/${start}/${length}`, { observe: 'response' }); - } - - getAddress$(address: string): Observable { - return this.httpClient.get(API_BASE_URL + '/address/' + address); - } - - getMarkets$(): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/markets'); - } - - getMarketsTicker$(): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/ticker'); - } - - getMarketsCurrencies$(): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/currencies'); - } - - getMarketsHloc$(market: string, interval: 'minute' | 'half_hour' | 'hour' | 'half_day' | 'day' - | 'week' | 'month' | 'year' | 'auto'): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/hloc?market=' + market + '&interval=' + interval); - } - - getMarketOffers$(market: string): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/offers?market=' + market); - } - - getMarketTrades$(market: string): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/trades?market=' + market); - } - - getMarketVolumesByTime$(period: string): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/volumes/' + period); - } - - getAllVolumesDay$(): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/volumes?interval=week'); - } -} diff --git a/frontend/src/app/bisq/bisq-block/bisq-block.component.html b/frontend/src/app/bisq/bisq-block/bisq-block.component.html deleted file mode 100644 index 4f79d8838..000000000 --- a/frontend/src/app/bisq/bisq-block/bisq-block.component.html +++ /dev/null @@ -1,114 +0,0 @@ -
- -
-

Block

-
- - {{ blockHeight }} - -
- - - -
-
-
- - - - - - - - - - -
Hash{{ block.hash | shortenString : 13 }}
Timestamp - ‎{{ block.time | date:'yyyy-MM-dd HH:mm' }} -
- () -
-
-
- -
-
- -
- -
- -

- - {{ i }} transaction - {{ i }} transactions -

- - - -
- - {{ tx.id | shortenString : 16 }} - {{ tx.id }} - -
- ‎{{ tx.time | date:'yyyy-MM-dd HH:mm' }} -
-
-
- - - -
-
- -
- - -
-
-
- - - - - - - - - - -
Hash
Timestamp
-
-
- - - - - - -
Previous hash
-
-
-
-
- - -
- -
- Error loading block -
- {{ error.status }}: {{ error.statusText }} -
-
- -
\ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-block/bisq-block.component.scss b/frontend/src/app/bisq/bisq-block/bisq-block.component.scss deleted file mode 100644 index 04e0e1a35..000000000 --- a/frontend/src/app/bisq/bisq-block/bisq-block.component.scss +++ /dev/null @@ -1,44 +0,0 @@ -.td-width { - width: 140px; - @media (min-width: 768px) { - width: 175px; - } -} - -h1 { - margin: 0px; - padding: 0px; - @media (min-width: 576px) { - float: left; - margin-right: 10px; - } -} - -.row{ - flex-direction: column; - @media (min-width: 768px) { - flex-direction: row; - } -} - -.block-container { - .table { - tr td { - &:last-child { - text-align: right; - @media (min-width: 992px) { - text-align: left; - } - } - } - } - .fiat { - display: block; - font-size: 13px; - @media (min-width: 992px) { - display: inline-block; - font-size: 14px; - margin-left: 10px; - } - } -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-block/bisq-block.component.ts b/frontend/src/app/bisq/bisq-block/bisq-block.component.ts deleted file mode 100644 index 59bb16a9e..000000000 --- a/frontend/src/app/bisq/bisq-block/bisq-block.component.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { BisqBlock } from '../../bisq/bisq.interfaces'; -import { Location } from '@angular/common'; -import { BisqApiService } from '../bisq-api.service'; -import { ActivatedRoute, ParamMap, Router } from '@angular/router'; -import { Subscription, of } from 'rxjs'; -import { switchMap, catchError } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; -import { ElectrsApiService } from '../../services/electrs-api.service'; -import { HttpErrorResponse } from '@angular/common/http'; -import { WebsocketService } from '../../services/websocket.service'; - -@Component({ - selector: 'app-bisq-block', - templateUrl: './bisq-block.component.html', - styleUrls: ['./bisq-block.component.scss'] -}) -export class BisqBlockComponent implements OnInit, OnDestroy { - block: BisqBlock; - subscription: Subscription; - blockHash = ''; - blockHeight = 0; - isLoading = true; - error: HttpErrorResponse | null; - - constructor( - private websocketService: WebsocketService, - private bisqApiService: BisqApiService, - private route: ActivatedRoute, - private seoService: SeoService, - private electrsApiService: ElectrsApiService, - private router: Router, - private location: Location, - ) { } - - ngOnInit(): void { - this.websocketService.want(['blocks']); - - this.subscription = this.route.paramMap - .pipe( - switchMap((params: ParamMap) => { - const blockHash = params.get('id') || ''; - document.body.scrollTo(0, 0); - this.isLoading = true; - this.error = null; - if (history.state.data && history.state.data.blockHeight) { - this.blockHeight = history.state.data.blockHeight; - } - if (history.state.data && history.state.data.block) { - this.blockHeight = history.state.data.block.height; - return of(history.state.data.block); - } - - let isBlockHeight = false; - if (/^[0-9]+$/.test(blockHash)) { - isBlockHeight = true; - } else { - this.blockHash = blockHash; - } - - if (isBlockHeight) { - return this.electrsApiService.getBlockHashFromHeight$(parseInt(blockHash, 10)) - .pipe( - switchMap((hash) => { - if (!hash) { - return; - } - this.blockHash = hash; - this.location.replaceState( - this.router.createUrlTree(['/bisq/block/', hash]).toString() - ); - this.seoService.updateCanonical(this.location.path()); - return this.bisqApiService.getBlock$(this.blockHash) - .pipe(catchError(this.caughtHttpError.bind(this))); - }), - catchError(this.caughtHttpError.bind(this)) - ); - } - - return this.bisqApiService.getBlock$(this.blockHash) - .pipe(catchError(this.caughtHttpError.bind(this))); - }) - ) - .subscribe((block: BisqBlock) => { - if (!block) { - this.seoService.logSoft404(); - return; - } - this.isLoading = false; - this.blockHeight = block.height; - this.seoService.setTitle($localize`:@@bisq-block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.hash}:BLOCK_HASH:`); - this.seoService.setDescription($localize`:@@meta.description.bisq.block:See all BSQ transactions in Bitcoin block ${block.height}:BLOCK_HEIGHT: (block hash ${block.hash}:BLOCK_HASH:).`); - this.block = block; - }); - } - - ngOnDestroy() { - this.subscription.unsubscribe(); - } - - caughtHttpError(err: HttpErrorResponse){ - this.error = err; - this.seoService.logSoft404(); - return of(null); - } -} diff --git a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.html b/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.html deleted file mode 100644 index 15f15b258..000000000 --- a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.html +++ /dev/null @@ -1,40 +0,0 @@ -
-

BSQ Blocks

-
- -
- - - -
- - - - - - - - - - - - - - - -
HeightConfirmedTotal sentTransactions
{{ block.height }}{{ calculateTotalOutput(block) / 100 | number: '1.2-2' }} BSQ{{ block.txs.length }}
-
- -
- - -
-
-
-
- - - - - - diff --git a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.scss b/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.scss deleted file mode 100644 index e8db46928..000000000 --- a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.scss +++ /dev/null @@ -1,6 +0,0 @@ -.pagination-container { - float: none; - @media(min-width: 400px){ - float: right; - } -} diff --git a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.ts b/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.ts deleted file mode 100644 index 7ab742655..000000000 --- a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; -import { BisqApiService } from '../bisq-api.service'; -import { switchMap, map, take, mergeMap, tap } from 'rxjs/operators'; -import { Observable } from 'rxjs'; -import { BisqBlock, BisqOutput, BisqTransaction } from '../bisq.interfaces'; -import { SeoService } from '../../services/seo.service'; -import { ActivatedRoute, Router } from '@angular/router'; -import { WebsocketService } from '../../services/websocket.service'; - -@Component({ - selector: 'app-bisq-blocks', - templateUrl: './bisq-blocks.component.html', - styleUrls: ['./bisq-blocks.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class BisqBlocksComponent implements OnInit { - blocks$: Observable<[BisqBlock[], number]>; - page = 1; - itemsPerPage: number; - contentSpace = window.innerHeight - (165 + 75); - fiveItemsPxSize = 250; - loadingItems: number[]; - isLoading = true; - // @ts-ignore - paginationSize: 'sm' | 'lg' = 'md'; - paginationMaxSize = 5; - - constructor( - private websocketService: WebsocketService, - private bisqApiService: BisqApiService, - private seoService: SeoService, - private route: ActivatedRoute, - private router: Router, - ) { } - - ngOnInit(): void { - this.websocketService.want(['blocks']); - this.seoService.setTitle($localize`:@@8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`); - this.seoService.setDescription($localize`:@@meta.description.bisq.blocks:See a list of recent Bitcoin blocks with BSQ transactions, total BSQ sent per block, and more.`); - this.itemsPerPage = Math.max(Math.round(this.contentSpace / this.fiveItemsPxSize) * 5, 10); - this.loadingItems = Array(this.itemsPerPage); - if (document.body.clientWidth < 670) { - this.paginationSize = 'sm'; - this.paginationMaxSize = 3; - } - - this.blocks$ = this.route.queryParams - .pipe( - take(1), - tap((qp) => { - if (qp.page) { - this.page = parseInt(qp.page, 10); - } - }), - mergeMap(() => this.route.queryParams), - map((queryParams) => { - if (queryParams.page) { - const newPage = parseInt(queryParams.page, 10); - this.page = newPage; - return newPage; - } else { - this.page = 1; - } - return 1; - }), - switchMap((page) => this.bisqApiService.listBlocks$((page - 1) * this.itemsPerPage, this.itemsPerPage)), - map((response) => [response.body, parseInt(response.headers.get('x-total-count'), 10)]), - ); - } - - calculateTotalOutput(block: BisqBlock): number { - return block.txs.reduce((a: number, tx: BisqTransaction) => - a + tx.outputs.reduce((acc: number, output: BisqOutput) => acc + output.bsqAmount, 0), 0 - ); - } - - trackByFn(index: number) { - return index; - } - - pageChange(page: number) { - this.router.navigate([], { - queryParams: { page: page }, - queryParamsHandling: 'merge', - }); - } - - onResize(event: any) { - this.paginationMaxSize = event.target.innerWidth < 670 ? 3 : 5; - } -} diff --git a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html deleted file mode 100644 index 132384fa9..000000000 --- a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html +++ /dev/null @@ -1,63 +0,0 @@ -
- -

Bisq Trading Volume

- -
- -
-
-
-
- - - -
- -

- -
-

- Markets - Bitcoin Markets -

- -
- - - - - - - - - - - - - - - -
Currency PriceVolume (7d) Trades (7d)
{{ ticker.name }}) - - - {{ ticker.last | currency: ticker.market.rsymbol }} - - - - {{ ticker.volume?.num_trades ? ticker.volume?.num_trades : 0 }}
-
- -

- -

Latest Trades

- - -
-
-
- - - - - - diff --git a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.scss b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.scss deleted file mode 100644 index 6248f9461..000000000 --- a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.scss +++ /dev/null @@ -1,22 +0,0 @@ -#volumeHolder { - height: 500px; - background-color: #000; - overflow: hidden; - display: flex; - justify-content: center; -} - -.table { - max-width: 100%; - overflow: scroll; -} - -.loadingVolumes { - position: relative; - top: 45%; - z-index: 100; -} - -.container-info{ - overflow-x: scroll; -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts deleted file mode 100644 index 8834d09e9..000000000 --- a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; -import { Observable, combineLatest, BehaviorSubject, of } from 'rxjs'; -import { map, share, switchMap } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { BisqApiService } from '../bisq-api.service'; -import { Trade } from '../bisq.interfaces'; - -@Component({ - selector: 'app-bisq-dashboard', - templateUrl: './bisq-dashboard.component.html', - styleUrls: ['./bisq-dashboard.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class BisqDashboardComponent implements OnInit { - tickers$: Observable; - volumes$: Observable; - trades$: Observable; - sort$ = new BehaviorSubject('trades'); - - allowCryptoCoins = ['usdc', 'l-btc', 'bsq']; - - constructor( - private websocketService: WebsocketService, - private bisqApiService: BisqApiService, - public stateService: StateService, - private seoService: SeoService, - ) { } - - ngOnInit(): void { - this.seoService.setTitle($localize`:@@meta.title.bisq.markets:Markets`); - this.seoService.setDescription($localize`:@@meta.description.bisq.markets:Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See Bisq market prices, trading activity, and more.`); - this.websocketService.want(['blocks']); - - this.volumes$ = this.bisqApiService.getAllVolumesDay$() - .pipe( - map((volumes) => { - const data = volumes.map((volume) => { - return { - time: volume.period_start, - value: volume.volume, - }; - }); - - const linesData = volumes.map((volume) => { - return { - time: volume.period_start, - value: volume.num_trades, - }; - }); - - return { - data: data, - linesData: linesData, - }; - }) - ); - - const getMarkets = this.bisqApiService.getMarkets$().pipe(share()); - - this.tickers$ = combineLatest([ - this.bisqApiService.getMarketsTicker$(), - getMarkets, - this.bisqApiService.getMarketVolumesByTime$('7d'), - ]) - .pipe( - map(([tickers, markets, volumes]) => { - - const newTickers = []; - for (const t in tickers) { - - if (this.stateService.env.BASE_MODULE !== 'bisq') { - const pair = t.split('_'); - if (pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1) { - continue; - } - } - - const mappedTicker: any = tickers[t]; - - mappedTicker.pair_url = t; - mappedTicker.pair = t.replace('_', '/').toUpperCase(); - mappedTicker.market = markets[t]; - mappedTicker.volume = volumes[t]; - mappedTicker.name = `${mappedTicker.market.rtype === 'crypto' ? mappedTicker.market.lname : mappedTicker.market.rname} (${mappedTicker.market.rtype === 'crypto' ? mappedTicker.market.lsymbol : mappedTicker.market.rsymbol}`; - newTickers.push(mappedTicker); - } - return newTickers; - }), - switchMap((tickers) => combineLatest([this.sort$, of(tickers)])), - map(([sort, tickers]) => { - if (sort === 'trades') { - tickers.sort((a, b) => (b.volume && b.volume.num_trades || 0) - (a.volume && a.volume.num_trades || 0)); - } else if (sort === 'volumes') { - tickers.sort((a, b) => (b.volume && b.volume.volume || 0) - (a.volume && a.volume.volume || 0)); - } else if (sort === 'name') { - tickers.sort((a, b) => a.name.localeCompare(b.name)); - } - return tickers; - }) - ); - - this.trades$ = combineLatest([ - this.bisqApiService.getMarketTrades$('all'), - getMarkets, - ]) - .pipe( - map(([trades, markets]) => { - if (this.stateService.env.BASE_MODULE !== 'bisq') { - trades = trades.filter((trade) => { - const pair = trade.market.split('_'); - return !(pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1); - }); - } - return trades.map((trade => { - trade._market = markets[trade.market]; - return trade; - })); - }) - ); - } - - trackByFn(index: number) { - return index; - } - - sort(by: string) { - this.sort$.next(by); - } - -} diff --git a/frontend/src/app/bisq/bisq-icon/bisq-icon.component.html b/frontend/src/app/bisq/bisq-icon/bisq-icon.component.html deleted file mode 100644 index 5ea603892..000000000 --- a/frontend/src/app/bisq/bisq-icon/bisq-icon.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/frontend/src/app/bisq/bisq-icon/bisq-icon.component.scss b/frontend/src/app/bisq/bisq-icon/bisq-icon.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/app/bisq/bisq-icon/bisq-icon.component.ts b/frontend/src/app/bisq/bisq-icon/bisq-icon.component.ts deleted file mode 100644 index 6137d5aa6..000000000 --- a/frontend/src/app/bisq/bisq-icon/bisq-icon.component.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Component, ChangeDetectionStrategy, Input, OnChanges } from '@angular/core'; -import { IconPrefix, IconName } from '@fortawesome/fontawesome-common-types'; - -@Component({ - selector: 'app-bisq-icon', - templateUrl: './bisq-icon.component.html', - styleUrls: ['./bisq-icon.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class BisqIconComponent implements OnChanges { - @Input() txType: string; - - iconProp: [IconPrefix, IconName] = ['fas', 'leaf']; - color: string; - - constructor() { } - - ngOnChanges() { - switch (this.txType) { - case 'UNVERIFIED': - this.iconProp[1] = 'question'; - this.color = 'ffac00'; - break; - case 'INVALID': - this.iconProp[1] = 'exclamation-triangle'; - this.color = 'ff4500'; - break; - case 'GENESIS': - this.iconProp[1] = 'rocket'; - this.color = '25B135'; - break; - case 'TRANSFER_BSQ': - this.iconProp[1] = 'retweet'; - this.color = 'a3a3a3'; - break; - case 'PAY_TRADE_FEE': - this.iconProp[1] = 'leaf'; - this.color = '689f43'; - break; - case 'PROPOSAL': - this.iconProp[1] = 'file-alt'; - this.color = '6c8b3b'; - break; - case 'COMPENSATION_REQUEST': - this.iconProp[1] = 'money-bill'; - this.color = '689f43'; - break; - case 'REIMBURSEMENT_REQUEST': - this.iconProp[1] = 'money-bill'; - this.color = '04a908'; - break; - case 'BLIND_VOTE': - this.iconProp[1] = 'eye-slash'; - this.color = '07579a'; - break; - case 'VOTE_REVEAL': - this.iconProp[1] = 'eye'; - this.color = '4AC5FF'; - break; - case 'LOCKUP': - this.iconProp[1] = 'lock'; - this.color = '0056c4'; - break; - case 'UNLOCK': - this.iconProp[1] = 'lock-open'; - this.color = '1d965f'; - break; - case 'ASSET_LISTING_FEE': - this.iconProp[1] = 'file-alt'; - this.color = '6c8b3b'; - break; - case 'PROOF_OF_BURN': - this.iconProp[1] = 'file-alt'; - this.color = '6c8b3b'; - break; - case 'IRREGULAR': - this.iconProp[1] = 'exclamation-circle'; - this.color = 'ffd700'; - break; - default: - this.iconProp[1] = 'question'; - this.color = 'ffac00'; - } - // @ts-ignore - this.iconProp = this.iconProp.slice(); - } -} diff --git a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.html b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.html deleted file mode 100644 index cd99d6ed3..000000000 --- a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.html +++ /dev/null @@ -1,124 +0,0 @@ -
- -
- -
-
-
-
-
Bisq Price Index
-
- - {{ usdPrice | currency:'USD':'symbol':'1.2-2' }} - -
-
-
-
-
-
-
-
Bisq Market Price
-
- - {{ bisqMarketPrice | currency:'USD':'symbol':'1.2-2' }} - -
-
-
-
-
- -
-
-
-
-
US Dollar - BTC/USD
-
- - - -
-
-
-
-
-
-
-
Bisq Trading Volume
-
- - - -
-
-
-
-
- -
- - -
-
-
-
- Markets - Bitcoin Markets -
- -
- - - - - - - - - - - - - -
Currency PriceTrades (7d)
{{ ticker.name }}) - - - {{ ticker.last | currency: ticker.market.rsymbol }} - - {{ ticker.volume?.num_trades ? ticker.volume?.num_trades : 0 }}
-
- - -
-
-
-
-
-
-
Latest Trades
- - -
-
-
-
-
- -
- - - - - - - - -
-
-
-
- - -
-
diff --git a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.scss b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.scss deleted file mode 100644 index 4eeeee64e..000000000 --- a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.scss +++ /dev/null @@ -1,112 +0,0 @@ -#volumeHolder { - height: 500px; - background-color: #000; - overflow: hidden; - display: flex; - justify-content: center; -} - -.table { - max-width: 100%; - overflow: scroll; -} - -.loadingGraphs { - position: relative; - top: 45%; - z-index: 100; -} - -.table-container { - overflow: scroll; - scrollbar-width: none; - font-size: 13px; - &::-webkit-scrollbar { - display: none; - } - @media(min-width: 576px){ - font-size: 16px; - } - thead th{ - text-align: right; - &:first-child { - text-align: left; - } - &:nth-child(3) { - display: none; - @media(min-width: 1100px){ - display: table-cell; - } - } - } - tr { - td { - text-align: right; - &:first-child { - text-align: left; - } - &:nth-child(3) { - display: none; - @media(min-width: 1100px){ - display: table-cell; - } - } - } - } -} - - -.chart-container { - height: 300px; -} - -.big-fiat { - color: #3bcc49; - font-size: 26px; -} - - - .card { - background-color: #1d1f31; - height: 100%; - } - - .card-title { - color: #4a68b9; - font-size: 1rem; - } - - .info-block { - float: left; - width: 350px; - line-height: 25px; - } - - .progress { - display: inline-flex; - width: 100%; - background-color: #2d3348; - height: 1.1rem; - } - - .bg-warning { - background-color: #b58800 !important; - } - - .skeleton-loader { - max-width: 100%; - &.shorter { - max-width: 150px; - } - } - - .more-padding { - padding: 1.25rem 2rem 1.25rem 2rem; - } - - .graph-card { - height: 100%; - @media (min-width: 992px) { - height: 385px; - } - } diff --git a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts deleted file mode 100644 index e7e4471c9..000000000 --- a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; -import { Observable, combineLatest, BehaviorSubject, of } from 'rxjs'; -import { map, share, switchMap } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { BisqApiService } from '../bisq-api.service'; -import { Trade } from '../bisq.interfaces'; - -@Component({ - selector: 'app-main-bisq-dashboard', - templateUrl: './bisq-main-dashboard.component.html', - styleUrls: ['./bisq-main-dashboard.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class BisqMainDashboardComponent implements OnInit { - tickers$: Observable; - volumes$: Observable; - trades$: Observable; - sort$ = new BehaviorSubject('trades'); - hlocData$: Observable; - usdPrice$: Observable; - isLoadingGraph = true; - bisqMarketPrice = 0; - - allowCryptoCoins = ['usdc', 'l-btc', 'bsq']; - - constructor( - private websocketService: WebsocketService, - private bisqApiService: BisqApiService, - public stateService: StateService, - private seoService: SeoService, - ) { } - - ngOnInit(): void { - this.seoService.resetTitle(); - this.seoService.resetDescription(); - this.websocketService.want(['blocks']); - - this.usdPrice$ = this.stateService.conversions$.asObservable().pipe( - map((conversions) => conversions.USD) - ); - - this.volumes$ = this.bisqApiService.getAllVolumesDay$() - .pipe( - map((volumes) => { - const data = volumes.map((volume) => { - return { - time: volume.period_start, - value: volume.volume, - }; - }); - - const linesData = volumes.map((volume) => { - return { - time: volume.period_start, - value: volume.num_trades, - }; - }); - - return { - data: data, - linesData: linesData, - }; - }) - ); - - const getMarkets = this.bisqApiService.getMarkets$().pipe(share()); - - this.tickers$ = combineLatest([ - this.bisqApiService.getMarketsTicker$(), - getMarkets, - this.bisqApiService.getMarketVolumesByTime$('7d'), - ]) - .pipe( - map(([tickers, markets, volumes]) => { - - const newTickers = []; - for (const t in tickers) { - - if (this.stateService.env.BASE_MODULE !== 'bisq') { - const pair = t.split('_'); - if (pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1) { - continue; - } - } - - const mappedTicker: any = tickers[t]; - - mappedTicker.pair_url = t; - mappedTicker.pair = t.replace('_', '/').toUpperCase(); - mappedTicker.market = markets[t]; - mappedTicker.volume = volumes[t]; - mappedTicker.name = `${mappedTicker.market.rtype === 'crypto' ? mappedTicker.market.lname : mappedTicker.market.rname} (${mappedTicker.market.rtype === 'crypto' ? mappedTicker.market.lsymbol : mappedTicker.market.rsymbol}`; - newTickers.push(mappedTicker); - } - return newTickers; - }), - switchMap((tickers) => combineLatest([this.sort$, of(tickers)])), - map(([sort, tickers]) => { - if (sort === 'trades') { - tickers.sort((a, b) => (b.volume && b.volume.num_trades || 0) - (a.volume && a.volume.num_trades || 0)); - } else if (sort === 'volumes') { - tickers.sort((a, b) => (b.volume && b.volume.volume || 0) - (a.volume && a.volume.volume || 0)); - } else if (sort === 'name') { - tickers.sort((a, b) => a.name.localeCompare(b.name)); - } - return tickers.slice(0, 10); - }) - ); - - this.trades$ = combineLatest([ - this.bisqApiService.getMarketTrades$('all'), - getMarkets, - ]) - .pipe( - map(([trades, markets]) => { - if (this.stateService.env.BASE_MODULE !== 'bisq') { - trades = trades.filter((trade) => { - const pair = trade.market.split('_'); - return !(pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1); - }); - } - return trades.map((trade => { - trade._market = markets[trade.market]; - return trade; - })).slice(0, 10); - }) - ); - - this.hlocData$ = this.bisqApiService.getMarketsHloc$('btc_usd', 'day') - .pipe( - map((hlocData) => { - this.isLoadingGraph = false; - - hlocData = hlocData.map((h) => { - h.time = h.period_start; - return h; - }); - - const hlocVolume = hlocData.map((h) => { - return { - time: h.time, - value: h.volume_right, - color: h.close > h.avg ? 'rgba(0, 41, 74, 0.7)' : 'rgba(0, 41, 74, 1)', - }; - }); - - // Add whitespace - if (hlocData.length > 1) { - const newHloc = []; - newHloc.push(hlocData[0]); - - const period = 86400; - let periods = 0; - const startingDate = hlocData[0].period_start; - let index = 1; - while (true) { - periods++; - if (hlocData[index].period_start > startingDate + period * periods) { - newHloc.push({ - time: startingDate + period * periods, - }); - } else { - newHloc.push(hlocData[index]); - index++; - if (!hlocData[index]) { - break; - } - } - } - hlocData = newHloc; - } - - this.bisqMarketPrice = hlocData[hlocData.length - 1].close; - - return { - hloc: hlocData, - volume: hlocVolume, - }; - }), - ); - } - - trackByFn(index: number) { - return index; - } - - sort(by: string) { - this.sort$.next(by); - } - -} diff --git a/frontend/src/app/bisq/bisq-market/bisq-market.component.html b/frontend/src/app/bisq/bisq-market/bisq-market.component.html deleted file mode 100644 index bcf890d40..000000000 --- a/frontend/src/app/bisq/bisq-market/bisq-market.component.html +++ /dev/null @@ -1,112 +0,0 @@ -
- - - - -

{{ currency.market.rtype === 'crypto' ? currency.market.lname : currency.market.rname }} - {{ currency.pair }}

-
- {{ hlocData.hloc[hlocData.hloc.length - 1].close | currency: currency.market.rsymbol }} - {{ hlocData.hloc[hlocData.hloc.length - 1].close | number: '1.' + currency.market.rprecision + '-' + currency.market.rprecision }} {{ currency.market.rsymbol }} -
- -
-
- - - - - - - -
-
- -
- -
-
-
-
- -
- -
- - -
- - -
-
- -

- - -

Latest Trades

- - -
- -
-
- -
- - - -
-

- Buy Offers - Sell Offers -

-
- - - - - - - - - - - - - -
Price
- {{ offer.price | currency: market.rsymbol }} - {{ offer.price | number: '1.2-' + market.rprecision }} {{ market.rsymbol }} - - {{ offer.amount | currency: market.rsymbol }} - {{ offer.amount | number: '1.2-' + market.lprecision }} {{ market.lsymbol }} - - {{ offer.volume | currency: market.rsymbol }} - {{ offer.volume | number: '1.2-' + market.rprecision }} {{ market.rsymbol }} -
-
-
-
- - -
-
-
-
-
-
- -Amount ({{ i }}) diff --git a/frontend/src/app/bisq/bisq-market/bisq-market.component.scss b/frontend/src/app/bisq/bisq-market/bisq-market.component.scss deleted file mode 100644 index 232405ff4..000000000 --- a/frontend/src/app/bisq/bisq-market/bisq-market.component.scss +++ /dev/null @@ -1,46 +0,0 @@ -.priceheader { - font-size: 24px; - @media(min-width: 576px){ - float: left; - } -} - -.radio-form { - @media(min-width: 576px){ - float: right; - } -} - -.loadingChart { - z-index: 100; - position: absolute; - top: 50%; - left: 50%; -} - -#graphHolder { - height: 550px; - overflow: hidden; -} - -.col { - &:last-child{ - margin-top: 50px; - @media(min-width: 576px){ - margin-top: 0px; - } - } -} - -.table-container { - overflow: scroll; - -ms-overflow-style: none; - scrollbar-width: none; - font-size: 13px; - @media(min-width: 576px){ - font-size: 16px; - } - &::-webkit-scrollbar { - display: none; - } -} diff --git a/frontend/src/app/bisq/bisq-market/bisq-market.component.ts b/frontend/src/app/bisq/bisq-market/bisq-market.component.ts deleted file mode 100644 index f81b4d891..000000000 --- a/frontend/src/app/bisq/bisq-market/bisq-market.component.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; -import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { ActivatedRoute, Router } from '@angular/router'; -import { combineLatest, merge, Observable, of } from 'rxjs'; -import { map, switchMap } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { BisqApiService } from '../bisq-api.service'; -import { OffersMarket, Trade } from '../bisq.interfaces'; - -@Component({ - selector: 'app-bisq-market', - templateUrl: './bisq-market.component.html', - styleUrls: ['./bisq-market.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class BisqMarketComponent implements OnInit, OnDestroy { - hlocData$: Observable; - currency$: Observable; - offers$: Observable; - trades$: Observable; - radioGroupForm: UntypedFormGroup; - defaultInterval = 'day'; - - isLoadingGraph = false; - - constructor( - private websocketService: WebsocketService, - private route: ActivatedRoute, - private bisqApiService: BisqApiService, - private formBuilder: UntypedFormBuilder, - private seoService: SeoService, - private router: Router, - ) { } - - ngOnInit(): void { - this.radioGroupForm = this.formBuilder.group({ - interval: [this.defaultInterval], - }); - - if (['half_hour', 'hour', 'half_day', 'day', 'week', 'month', 'year', 'auto'].indexOf(this.route.snapshot.fragment) > -1) { - this.radioGroupForm.controls.interval.setValue(this.route.snapshot.fragment, { emitEvent: false }); - } - - this.currency$ = this.bisqApiService.getMarkets$() - .pipe( - switchMap((markets) => combineLatest([of(markets), this.route.paramMap])), - map(([markets, routeParams]) => { - const pair = routeParams.get('pair'); - const pairUpperCase = pair.replace('_', '/').toUpperCase(); - this.seoService.setTitle($localize`:@@meta.title.bisq.market:Bisq market: ${pairUpperCase}`); - this.seoService.setDescription($localize`:@@meta.description.bisq.market:See price history, current buy/sell offers, and latest trades for the ${pairUpperCase} market on Bisq.`); - - return { - pair: pairUpperCase, - market: markets[pair], - }; - }) - ); - - this.trades$ = this.route.paramMap - .pipe( - map(routeParams => routeParams.get('pair')), - switchMap((marketPair) => this.bisqApiService.getMarketTrades$(marketPair)), - ); - - this.offers$ = this.route.paramMap - .pipe( - map(routeParams => routeParams.get('pair')), - switchMap((marketPair) => this.bisqApiService.getMarketOffers$(marketPair)), - map((offers) => offers[Object.keys(offers)[0]]) - ); - - this.hlocData$ = combineLatest([ - this.route.paramMap, - merge(this.radioGroupForm.get('interval').valueChanges, of(this.radioGroupForm.get('interval').value)), - ]) - .pipe( - switchMap(([routeParams, interval]) => { - this.isLoadingGraph = true; - const pair = routeParams.get('pair'); - return this.bisqApiService.getMarketsHloc$(pair, interval); - }), - map((hlocData) => { - this.isLoadingGraph = false; - - hlocData = hlocData.map((h) => { - h.time = h.period_start; - return h; - }); - - const hlocVolume = hlocData.map((h) => { - return { - time: h.time, - value: h.volume_right, - color: h.close > h.avg ? 'rgba(0, 41, 74, 0.7)' : 'rgba(0, 41, 74, 1)', - }; - }); - - // Add whitespace - if (hlocData.length > 1) { - const newHloc = []; - newHloc.push(hlocData[0]); - - const period = this.getUnixTimestampFromInterval(this.radioGroupForm.get('interval').value); // temp - let periods = 0; - const startingDate = hlocData[0].period_start; - let index = 1; - while (true) { - periods++; - if (hlocData[index].period_start > startingDate + period * periods) { - newHloc.push({ - time: startingDate + period * periods, - }); - } else { - newHloc.push(hlocData[index]); - index++; - if (!hlocData[index]) { - break; - } - } - } - hlocData = newHloc; - } - - return { - hloc: hlocData, - volume: hlocVolume, - }; - }), - ); - } - - setFragment(fragment: string) { - this.router.navigate([], { - relativeTo: this.route, - queryParamsHandling: 'merge', - fragment: fragment - }); - } - - ngOnDestroy(): void { - this.websocketService.stopTrackingBisqMarket(); - } - - getUnixTimestampFromInterval(interval: string): number { - switch (interval) { - case 'minute': return 60; - case 'half_hour': return 1800; - case 'hour': return 3600; - case 'half_day': return 43200; - case 'day': return 86400; - case 'week': return 604800; - case 'month': return 2592000; - case 'year': return 31579200; - } - } - -} diff --git a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.html b/frontend/src/app/bisq/bisq-stats/bisq-stats.component.html deleted file mode 100644 index 765d24f9f..000000000 --- a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.html +++ /dev/null @@ -1,86 +0,0 @@ -
-

BSQ statistics

-
- -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Existing amount{{ (stats.minted - stats.burnt) | number: '1.2-2' }} BSQ
Minted amount{{ stats.minted | number: '1.2-2' }} BSQ
Burnt amount{{ stats.burnt | number: '1.2-2' }} BSQ
Addresses{{ stats.addresses | number }}
Unspent TXOs{{ stats.unspent_txos | number }}
Spent TXOs{{ stats.spent_txos | number }}
Price
Market cap
- -
-
-
-
- - - - - Existing amount - - - - Minted amount - - - - Burnt amount - - - - Addresses - - - - Unspent TXOs - - - - Spent TXOs - - - - Price - - - - Market cap - - - - diff --git a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.scss b/frontend/src/app/bisq/bisq-stats/bisq-stats.component.scss deleted file mode 100644 index 69cf8224e..000000000 --- a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.scss +++ /dev/null @@ -1,18 +0,0 @@ -.td-width { - width: 250px; -} - -@media (max-width: 767.98px) { - .td-width { - width: 175px; - } -} - -.fiat { - display: block; - font-size: 13px; - @media (min-width: 768px) { - font-size: 14px; - display: inline-block; - } -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.ts b/frontend/src/app/bisq/bisq-stats/bisq-stats.component.ts deleted file mode 100644 index 58819d9cf..000000000 --- a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { BisqApiService } from '../bisq-api.service'; -import { BisqStats } from '../bisq.interfaces'; -import { SeoService } from '../../services/seo.service'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; - -@Component({ - selector: 'app-bisq-stats', - templateUrl: './bisq-stats.component.html', - styleUrls: ['./bisq-stats.component.scss'] -}) -export class BisqStatsComponent implements OnInit { - isLoading = true; - stats: BisqStats; - price: number; - - constructor( - private websocketService: WebsocketService, - private bisqApiService: BisqApiService, - private seoService: SeoService, - private stateService: StateService, - ) { } - - ngOnInit() { - this.websocketService.want(['blocks']); - - this.seoService.setTitle($localize`:@@2a30a4cdb123a03facc5ab8c5b3e6d8b8dbbc3d4:BSQ statistics`); - this.seoService.setDescription($localize`:@@meta.description.bisq.stats:See high-level stats on the BSQ economy: supply metrics, number of addresses, BSQ price, market cap, and more.`); - this.stateService.bsqPrice$ - .subscribe((bsqPrice) => { - this.price = bsqPrice; - }); - - this.bisqApiService.getStats$() - .subscribe((stats) => { - this.isLoading = false; - this.stats = stats; - }); - } - -} diff --git a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.html b/frontend/src/app/bisq/bisq-trades/bisq-trades.component.html deleted file mode 100644 index d64fc1ac5..000000000 --- a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.html +++ /dev/null @@ -1,46 +0,0 @@ -
- - - - - - - - - - - - - - - - - - - - - -
DatePrice - - Amount -
- ‎{{ trade.trade_date | date:'yyyy-MM-dd HH:mm' }} - - {{ trade.price | currency: (trade._market || market).rsymbol }} - {{ trade.price | number: '1.2-' + (trade._market || market).rprecision }} {{ (trade._market || market).rsymbol }} - - {{ trade.amount | currency: (trade._market || market).rsymbol }} - {{ trade.amount | number: '1.2-' + (trade._market || market).lprecision }} {{ (trade._market || market).lsymbol }} - - {{ trade.volume | currency: (trade._market || market).rsymbol }} - {{ trade.volume | number: '1.2-' + (trade._market || market).rprecision }} {{ (trade._market || market).rsymbol }} -
-
- - - - - - - -Amount ({{ i }}) diff --git a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.scss b/frontend/src/app/bisq/bisq-trades/bisq-trades.component.scss deleted file mode 100644 index a7c82834d..000000000 --- a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.scss +++ /dev/null @@ -1,38 +0,0 @@ - -.table-container { - overflow: scroll; - scrollbar-width: none; - font-size: 13px; - &::-webkit-scrollbar { - display: none; - } - @media(min-width: 576px){ - font-size: 16px; - } - thead th{ - text-align: right; - &:first-child{ - text-align: left; - } - &:nth-child(2) { - display: none; - @media(min-width: 1100px){ - display: table-cell; - } - } - } - tr { - td { - text-align: right; - &:first-child{ - text-align: left; - } - &:nth-child(2) { - display: none; - @media(min-width: 1100px){ - display: table-cell; - } - } - } - } -} diff --git a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.ts b/frontend/src/app/bisq/bisq-trades/bisq-trades.component.ts deleted file mode 100644 index b984e715c..000000000 --- a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'app-bisq-trades', - templateUrl: './bisq-trades.component.html', - styleUrls: ['./bisq-trades.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class BisqTradesComponent implements OnChanges { - @Input() trades$: Observable; - @Input() market: any; - @Input() view: 'all' | 'small' = 'all'; - - loadingColumns = [1, 2, 3, 4]; - - ngOnChanges() { - if (this.view === 'small') { - this.loadingColumns = [1, 2, 3]; - } - } -} diff --git a/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.html b/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.html deleted file mode 100644 index a4033dcda..000000000 --- a/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.html +++ /dev/null @@ -1,36 +0,0 @@ -
-
-
- - - - - - - - - - - - - - - -
Inputs{{ totalInput / 100 | number: '1.2-2' }} BSQ
Outputs{{ totalOutput / 100 | number: '1.2-2' }} BSQ
Issued amount{{ totalIssued / 100 | number: '1.2-2' }} BSQ
-
-
- - - - - - - - - - - -
Type {{ tx.txTypeDisplayString }}
Version{{ tx.txVersion }}
-
-
-
\ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.scss b/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.scss deleted file mode 100644 index f73dfea53..000000000 --- a/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.scss +++ /dev/null @@ -1,22 +0,0 @@ -@media (max-width: 767.98px) { - .td-width { - width: 150px; - } - .mobile-even tr:nth-of-type(even) { - background-color: #181b2d; - } - .mobile-even tr:nth-of-type(odd) { - background-color: inherit; - } -} - -.table { - tr td { - &:last-child{ - text-align: right; - @media(min-width: 768px){ - text-align: left; - } - } - } -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.ts b/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.ts deleted file mode 100644 index 9728372c3..000000000 --- a/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Component, ChangeDetectionStrategy, Input, OnChanges } from '@angular/core'; -import { BisqTransaction } from '../../bisq/bisq.interfaces'; - -@Component({ - selector: 'app-bisq-transaction-details', - templateUrl: './bisq-transaction-details.component.html', - styleUrls: ['./bisq-transaction-details.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class BisqTransactionDetailsComponent implements OnChanges { - @Input() tx: BisqTransaction; - - totalInput: number; - totalOutput: number; - totalIssued: number; - - constructor() { } - - ngOnChanges() { - this.totalInput = this.tx.inputs.filter((input) => input.isVerified).reduce((acc, input) => acc + input.bsqAmount, 0); - this.totalOutput = this.tx.outputs.filter((output) => output.isVerified).reduce((acc, output) => acc + output.bsqAmount, 0); - this.totalIssued = this.tx.outputs - .filter((output) => output.isVerified && output.txOutputType === 'ISSUANCE_CANDIDATE_OUTPUT') - .reduce((acc, output) => acc + output.bsqAmount, 0); - } -} diff --git a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html deleted file mode 100644 index 85abafee0..000000000 --- a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html +++ /dev/null @@ -1,216 +0,0 @@ -
- - -
-
-

Transaction

-
- - - - - - - - - -
-
- -
-
-
- -
- -
-
-
- - - - - - - - - - - - - - - -
Timestamp - ‎{{ bisqTx.time | date:'yyyy-MM-dd HH:mm' }} -
- () -
-
Included in block - {{ bisqTx.blockHeight }} -
Features - - - - -
-
-
- - - - - - - - - - - - - - -
Burnt amount - {{ bisqTx.burntFee / 100 | number: '1.2-2' }} BSQ -
Fee per vByteFee per weight unit - -   - -
-
- -
-
- -
- -
-

Details

-
- - - -
- -
-

Inputs & Outputs

-
- - - -
-
- - - -
- -
-
-

Transaction

-
-
- -
-
-
- - - - - - - - - - - - - - - -
-
-
- - - - - - - - - - - -
-
-
-
- -
- -
-

Details

-
- -
- - - - - - - - - - - - - - -
-
- -
- -
-

Inputs & Outputs

-
- -
-
-
- - - - - - -
-
-
- - - - - - -
-
-
-
- -
- - -
- -
- Error loading Bisq transaction -

- {{ error.status }}: {{ error.statusText }} -
-
- -
diff --git a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.scss b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.scss deleted file mode 100644 index 477c27c1d..000000000 --- a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.scss +++ /dev/null @@ -1 +0,0 @@ -@import "./../../components/transaction/transaction.component.scss"; \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts deleted file mode 100644 index 1818e105f..000000000 --- a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute, ParamMap, Router } from '@angular/router'; -import { BisqTransaction } from '../../bisq/bisq.interfaces'; -import { switchMap, map, catchError } from 'rxjs/operators'; -import { of, Observable, Subscription } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { Block, Transaction } from '../../interfaces/electrs.interface'; -import { BisqApiService } from '../bisq-api.service'; -import { SeoService } from '../../services/seo.service'; -import { ElectrsApiService } from '../../services/electrs-api.service'; -import { HttpErrorResponse } from '@angular/common/http'; -import { WebsocketService } from '../../services/websocket.service'; - -@Component({ - selector: 'app-bisq-transaction', - templateUrl: './bisq-transaction.component.html', - styleUrls: ['./bisq-transaction.component.scss'] -}) -export class BisqTransactionComponent implements OnInit, OnDestroy { - bisqTx: BisqTransaction; - tx: Transaction; - latestBlock$: Observable; - txId: string; - price: number; - isLoading = true; - isLoadingTx = true; - error = null; - subscription: Subscription; - - constructor( - private websocketService: WebsocketService, - private route: ActivatedRoute, - private bisqApiService: BisqApiService, - private electrsApiService: ElectrsApiService, - private stateService: StateService, - private seoService: SeoService, - private router: Router, - ) { } - - ngOnInit(): void { - this.websocketService.want(['blocks']); - - this.subscription = this.route.paramMap.pipe( - switchMap((params: ParamMap) => { - this.isLoading = true; - this.isLoadingTx = true; - this.error = null; - document.body.scrollTo(0, 0); - this.txId = params.get('id') || ''; - this.seoService.setTitle($localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`); - this.seoService.setDescription($localize`:@@meta.description.bisq.transaction:See inputs, outputs, transaction type, burnt amount, and more for transaction with txid ${this.txId}:INTERPOLATION:.`); - if (history.state.data) { - return of(history.state.data); - } - return this.bisqApiService.getTransaction$(this.txId) - .pipe( - catchError((bisqTxError: HttpErrorResponse) => { - if (bisqTxError.status === 404) { - return this.electrsApiService.getTransaction$(this.txId) - .pipe( - map((tx) => { - if (tx.status.confirmed) { - this.error = { - status: 200, - statusText: 'Transaction is confirmed but not available in the Bisq database, please try reloading this page.' - }; - return null; - } - return tx; - }), - catchError((txError: HttpErrorResponse) => { - console.log(txError); - this.error = txError; - this.seoService.logSoft404(); - return of(null); - }) - ); - } - this.error = bisqTxError; - this.seoService.logSoft404(); - return of(null); - }) - ); - }), - switchMap((tx) => { - if (!tx) { - return of(null); - } - - if (tx.version) { - if (this.stateService.env.BASE_MODULE === 'bisq') { - window.location.replace('https://mempool.space/tx/' + this.txId); - } else { - this.router.navigate(['/tx/', this.txId], { state: { data: tx, bsqTx: true }}); - } - return of(null); - } - - this.bisqTx = tx; - this.isLoading = false; - - return this.electrsApiService.getTransaction$(this.txId); - }), - ) - .subscribe((tx) => { - this.isLoadingTx = false; - - if (!tx) { - this.seoService.logSoft404(); - return; - } - - this.tx = tx; - }, - (error) => { - this.error = error; - }); - - this.latestBlock$ = this.stateService.blocks$.pipe(map((blocks) => blocks[0])); - - this.stateService.bsqPrice$ - .subscribe((bsqPrice) => { - this.price = bsqPrice; - }); - } - - ngOnDestroy() { - this.subscription.unsubscribe(); - } -} diff --git a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html b/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html deleted file mode 100644 index bc22414ca..000000000 --- a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html +++ /dev/null @@ -1,56 +0,0 @@ -
-

BSQ Transactions

- -
-
- -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - -
TXIDTypeAmountConfirmedHeight
{{ tx.id | slice : 0 : 8 }} - - {{ getStringByTxType(tx.txType) }} - - - - {{ tx.burntFee / 100 | number: '1.2-2' }} BSQ - - - {{ calculateTotalOutput(tx.outputs) / 100 | number: '1.2-2' }} BSQ - - {{ tx.blockHeight }}
- -
- - -
-
- - - - - - diff --git a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.scss b/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.scss deleted file mode 100644 index a42d55e5e..000000000 --- a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -label { - padding: 0.25rem 1rem; - white-space: nowrap; -} - -:host ::ng-deep .dropdown-menu { - right: 0px; - left: inherit; -} - -.pagination-container { - float: none; - @media(min-width: 400px){ - float: right; - } -} - -.container-xl { - padding-bottom: 60px; - @media(min-width: 400px){ - padding-bottom: 100px; - } -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.ts b/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.ts deleted file mode 100644 index be5455639..000000000 --- a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy } from '@angular/core'; -import { BisqTransaction, BisqOutput } from '../bisq.interfaces'; - -import { Observable, Subscription } from 'rxjs'; -import { switchMap, map, tap } from 'rxjs/operators'; -import { BisqApiService } from '../bisq-api.service'; -import { SeoService } from '../../services/seo.service'; -import { UntypedFormGroup, UntypedFormBuilder } from '@angular/forms'; -import { Router, ActivatedRoute } from '@angular/router'; -import { IMultiSelectOption, IMultiSelectSettings, IMultiSelectTexts } from '../../components/ngx-bootstrap-multiselect/types' -import { WebsocketService } from '../../services/websocket.service'; - -@Component({ - selector: 'app-bisq-transactions', - templateUrl: './bisq-transactions.component.html', - styleUrls: ['./bisq-transactions.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class BisqTransactionsComponent implements OnInit, OnDestroy { - transactions$: Observable<[BisqTransaction[], number]>; - page = 1; - itemsPerPage = 50; - fiveItemsPxSize = 250; - isLoading = true; - loadingItems: number[]; - radioGroupForm: UntypedFormGroup; - types: string[] = []; - radioGroupSubscription: Subscription; - - txTypeOptions: IMultiSelectOption[] = [ - { id: 1, name: $localize`Asset listing fee` }, - { id: 2, name: $localize`Blind vote` }, - { id: 3, name: $localize`Compensation request` }, - { id: 4, name: $localize`Genesis` }, - { id: 13, name: $localize`Irregular` }, - { id: 5, name: $localize`Lockup` }, - { id: 6, name: $localize`Pay trade fee` }, - { id: 7, name: $localize`Proof of burn` }, - { id: 8, name: $localize`Proposal` }, - { id: 9, name: $localize`Reimbursement request` }, - { id: 10, name: $localize`Transfer BSQ` }, - { id: 11, name: $localize`Unlock` }, - { id: 12, name: $localize`Vote reveal` }, - ]; - txTypesDefaultChecked = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; - - txTypeDropdownSettings: IMultiSelectSettings = { - buttonClasses: 'btn btn-primary btn-sm', - displayAllSelectedText: true, - showCheckAll: true, - showUncheckAll: true, - maxHeight: '500px', - fixedTitle: true, - }; - - txTypeDropdownTexts: IMultiSelectTexts = { - defaultTitle: $localize`:@@bisq-transactions.filter:Filter`, - checkAll: $localize`:@@bisq-transactions.selectall:Select all`, - uncheckAll: $localize`:@@bisq-transactions.unselectall:Unselect all`, - }; - - // @ts-ignore - paginationSize: 'sm' | 'lg' = 'md'; - paginationMaxSize = 5; - - txTypes = ['ASSET_LISTING_FEE', 'BLIND_VOTE', 'COMPENSATION_REQUEST', 'GENESIS', 'LOCKUP', 'PAY_TRADE_FEE', - 'PROOF_OF_BURN', 'PROPOSAL', 'REIMBURSEMENT_REQUEST', 'TRANSFER_BSQ', 'UNLOCK', 'VOTE_REVEAL', 'IRREGULAR']; - - constructor( - private websocketService: WebsocketService, - private bisqApiService: BisqApiService, - private seoService: SeoService, - private formBuilder: UntypedFormBuilder, - private route: ActivatedRoute, - private router: Router, - private cd: ChangeDetectorRef, - ) { } - - ngOnInit(): void { - this.websocketService.want(['blocks']); - this.seoService.setTitle($localize`:@@add4cd82e3e38a3110fe67b3c7df56e9602644ee:Transactions`); - this.seoService.setDescription($localize`:@@meta.description.bisq.transactions:See recent BSQ transactions: amount, txid, associated Bitcoin block, transaction type, and more.`); - - this.radioGroupForm = this.formBuilder.group({ - txTypes: [this.txTypesDefaultChecked], - }); - - this.loadingItems = Array(this.itemsPerPage); - - if (document.body.clientWidth < 670) { - this.paginationSize = 'sm'; - this.paginationMaxSize = 3; - } - - this.transactions$ = this.route.queryParams - .pipe( - tap((queryParams) => { - if (queryParams.page) { - const newPage = parseInt(queryParams.page, 10); - this.page = newPage; - } else { - this.page = 1; - } - if (queryParams.types) { - const types = queryParams.types.split(',').map((str: string) => parseInt(str, 10)); - this.types = types.map((id: number) => this.txTypes[id - 1]); - this.radioGroupForm.get('txTypes').setValue(types, { emitEvent: false }); - } else { - this.types = []; - this.radioGroupForm.get('txTypes').setValue([], { emitEvent: false }); - } - this.cd.markForCheck(); - }), - switchMap(() => this.bisqApiService.listTransactions$((this.page - 1) * this.itemsPerPage, this.itemsPerPage, this.types)), - map((response) => [response.body, parseInt(response.headers.get('x-total-count'), 10)]) - ); - - this.radioGroupSubscription = this.radioGroupForm.valueChanges - .subscribe((data) => { - this.types = data.txTypes.map((id: number) => this.txTypes[id - 1]); - if (this.types.length === this.txTypes.length) { - this.types = []; - } - this.page = 1; - this.typesChanged(data.txTypes); - this.cd.markForCheck(); - }); - } - - pageChange(page: number) { - this.router.navigate([], { - relativeTo: this.route, - queryParams: { page: page }, - queryParamsHandling: 'merge', - }); - } - - typesChanged(types: number[]) { - this.router.navigate([], { - relativeTo: this.route, - queryParams: { types: types.join(','), page: 1 }, - queryParamsHandling: 'merge', - }); - } - - calculateTotalOutput(outputs: BisqOutput[]): number { - return outputs.reduce((acc: number, output: BisqOutput) => acc + output.bsqAmount, 0); - } - - getStringByTxType(type: string) { - const id = this.txTypes.indexOf(type) + 1; - return this.txTypeOptions.find((type) => id === type.id).name; - } - - trackByFn(index: number) { - return index; - } - - onResize(event: any) { - this.paginationMaxSize = event.target.innerWidth < 670 ? 3 : 5; - } - - ngOnDestroy(): void { - this.radioGroupSubscription.unsubscribe(); - } -} diff --git a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.html b/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.html deleted file mode 100644 index dc4993e90..000000000 --- a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.html +++ /dev/null @@ -1,83 +0,0 @@ -
- - -
-
- Burnt amount: {{ tx.burntFee / 100 | number: '1.2-2' }} BSQ -
- -
- - -   - - -
-
-
- -
\ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.scss b/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.scss deleted file mode 100644 index bffd646d8..000000000 --- a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.scss +++ /dev/null @@ -1,103 +0,0 @@ - -.arrow-td { - width: 20px; -} -.green, .grey, .red { - font-size: 16px; - top: -2px; - position: relative; - @media( min-width: 576px){ - font-size: 19px; - } -} - -.green { - color:#28a745; -} - -.red { - color:#dc3545; -} - -.grey { - color:#6c757d; -} - -@media (max-width: 767.98px) { - .mobile-bottomcol { - margin-top: 15px; - } - .details-table td:first-child { - white-space: pre-wrap; - } -} - - -.details-table { - margin-top: 5px; -} - -.details-table td { - padding: 0.75rem; -} - -.details-table td:nth-child(2) { - word-break: break-all; - white-space: normal; - font-family: "Courier New", Courier, monospace; - font-size: 12px; -} - -.smaller-text { - font-size: 12px; - @media (min-width: 576px) { - font-size: 14px !important; - } -} - -.longer { - max-width: 100% !important; - width: 200px; - display: inline-block; -} - -.row{ - flex-direction: column; - @media (min-width: 992px) { - flex-direction: row; - } -} - -.extra-info { - display: inline-table; - .fiat { - font-size: 14px; - display: block; - text-align: right; - } -} - -.transaction-fee { - display: block; - margin: 0px auto 5px; - @media (min-width: 576px) { - display: inline-table; - } -} - - -.fiat { - margin-left: 10px; - font-size: 13px; - @media (min-width: 576px) { - font-size: 14px; - } -} - -.btn-container { - text-align: right; - @media (min-width: 576px) { - display: inline-table; - float: right; - } -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.ts b/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.ts deleted file mode 100644 index a46cbf07f..000000000 --- a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Component, OnInit, ChangeDetectionStrategy, Input, OnChanges } from '@angular/core'; -import { BisqTransaction } from '../../bisq/bisq.interfaces'; -import { StateService } from '../../services/state.service'; -import { map } from 'rxjs/operators'; -import { Observable } from 'rxjs'; -import { Block } from '../../interfaces/electrs.interface'; - -@Component({ - selector: 'app-bisq-transfers', - templateUrl: './bisq-transfers.component.html', - styleUrls: ['./bisq-transfers.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class BisqTransfersComponent implements OnInit, OnChanges { - @Input() tx: BisqTransaction; - @Input() showConfirmations = false; - - totalOutput: number; - latestBlock$: Observable; - - constructor( - private stateService: StateService, - ) { } - - trackByIndexFn(index: number) { - return index; - } - - ngOnInit() { - this.latestBlock$ = this.stateService.blocks$.pipe(map((blocks) => blocks[0])); - } - - ngOnChanges() { - this.totalOutput = this.tx.outputs.filter((output) => output.isVerified).reduce((acc, output) => acc + output.bsqAmount, 0); - } - - switchCurrency() { - const oldvalue = !this.stateService.viewFiat$.value; - this.stateService.viewFiat$.next(oldvalue); - } - -} diff --git a/frontend/src/app/bisq/bisq.interfaces.ts b/frontend/src/app/bisq/bisq.interfaces.ts deleted file mode 100644 index fb04667eb..000000000 --- a/frontend/src/app/bisq/bisq.interfaces.ts +++ /dev/null @@ -1,261 +0,0 @@ - -export interface BisqBlocks { - chainHeight: number; - blocks: BisqBlock[]; -} - -export interface BisqBlock { - height: number; - time: number; - hash: string; - previousBlockHash: string; - txs: BisqTransaction[]; -} - -export interface BisqTransaction { - txVersion: string; - id: string; - blockHeight: number; - blockHash: string; - time: number; - inputs: BisqInput[]; - outputs: BisqOutput[]; - txType: string; - txTypeDisplayString: string; - burntFee: number; - invalidatedBsq: number; - unlockBlockHeight: number; -} - -interface BisqInput { - spendingTxOutputIndex: number; - spendingTxId: string; - bsqAmount: number; - isVerified: boolean; - address: string; - time: number; -} - -export interface BisqOutput { - txVersion: string; - txId: string; - index: number; - bsqAmount: number; - btcAmount: number; - height: number; - isVerified: boolean; - burntFee: number; - invalidatedBsq: number; - address: string; - scriptPubKey: BisqScriptPubKey; - spentInfo?: SpentInfo; - time: any; - txType: string; - txTypeDisplayString: string; - txOutputType: string; - txOutputTypeDisplayString: string; - lockTime: number; - isUnspent: boolean; - opReturn?: string; -} - -export interface BisqStats { - minted: number; - burnt: number; - addresses: number; - unspent_txos: number; - spent_txos: number; -} - -interface BisqScriptPubKey { - addresses: string[]; - asm: string; - hex: string; - reqSigs?: number; - type: string; -} - -interface SpentInfo { - height: number; - inputIndex: number; - txId: string; -} - - -export interface BisqTrade { - direction: string; - price: string; - amount: string; - volume: string; - payment_method: string; - trade_id: string; - trade_date: number; - market?: string; -} - -export interface Currencies { [txid: string]: Currency; } - -export interface Currency { - code: string; - name: string; - precision: number; - - _type: string; -} - -export interface Depth { [market: string]: Market; } - -interface Market { - 'buys': string[]; - 'sells': string[]; -} - -export interface HighLowOpenClose { - period_start: number | string; - open: string; - high: string; - low: string; - close: string; - volume_left: string; - volume_right: string; - avg: string; -} - -export interface Markets { [txid: string]: Pair; } - -interface Pair { - pair: string; - lname: string; - rname: string; - lsymbol: string; - rsymbol: string; - lprecision: number; - rprecision: number; - ltype: string; - rtype: string; - name: string; -} - -export interface Offers { [market: string]: OffersMarket; } - -export interface OffersMarket { - buys: Offer[] | null; - sells: Offer[] | null; -} - -export interface OffersData { - direction: string; - currencyCode: string; - minAmount: number; - amount: number; - price: number; - date: number; - useMarketBasedPrice: boolean; - marketPriceMargin: number; - paymentMethod: string; - id: string; - currencyPair: string; - primaryMarketDirection: string; - priceDisplayString: string; - primaryMarketAmountDisplayString: string; - primaryMarketMinAmountDisplayString: string; - primaryMarketVolumeDisplayString: string; - primaryMarketMinVolumeDisplayString: string; - primaryMarketPrice: number; - primaryMarketAmount: number; - primaryMarketMinAmount: number; - primaryMarketVolume: number; - primaryMarketMinVolume: number; -} - -export interface Offer { - offer_id: string; - offer_date: number; - direction: string; - min_amount: string; - amount: string; - price: string; - volume: string; - payment_method: string; - offer_fee_txid: any; -} - -export interface Tickers { [market: string]: Ticker | null; } - -export interface Ticker { - last: string; - high: string; - low: string; - volume_left: string; - volume_right: string; - buy: string | null; - sell: string | null; -} - -export interface Trade { - market?: string; - price: string; - amount: string; - volume: string; - payment_method: string; - trade_id: string; - trade_date: number; - _market: Pair; -} - -export interface TradesData { - currency: string; - direction: string; - tradePrice: number; - tradeAmount: number; - tradeDate: number; - paymentMethod: string; - offerDate: number; - useMarketBasedPrice: boolean; - marketPriceMargin: number; - offerAmount: number; - offerMinAmount: number; - offerId: string; - depositTxId?: string; - currencyPair: string; - primaryMarketDirection: string; - primaryMarketTradePrice: number; - primaryMarketTradeAmount: number; - primaryMarketTradeVolume: number; - - _market: string; - _tradePriceStr: string; - _tradeAmountStr: string; - _tradeVolumeStr: string; - _offerAmountStr: string; - _tradePrice: number; - _tradeAmount: number; - _tradeVolume: number; - _offerAmount: number; -} - -export interface MarketVolume { - period_start: number; - num_trades: number; - volume: string; -} - -export interface MarketsApiError { - success: number; - error: string; -} - -export type Interval = 'minute' | 'half_hour' | 'hour' | 'half_day' | 'day' | 'week' | 'month' | 'year' | 'auto'; - -export interface SummarizedIntervals { [market: string]: SummarizedInterval; } -export interface SummarizedInterval { - period_start: number; - open: number; - close: number; - high: number; - low: number; - avg: number; - volume_right: number; - volume_left: number; - time?: number; -} diff --git a/frontend/src/app/bisq/bisq.module.ts b/frontend/src/app/bisq/bisq.module.ts deleted file mode 100644 index f7f71156b..000000000 --- a/frontend/src/app/bisq/bisq.module.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { NgModule } from '@angular/core'; -import { BisqRoutingModule } from './bisq.routing.module'; -import { SharedModule } from '../shared/shared.module'; - -import { LightweightChartsComponent } from './lightweight-charts/lightweight-charts.component'; -import { LightweightChartsAreaComponent } from './lightweight-charts-area/lightweight-charts-area.component'; -import { BisqMarketComponent } from './bisq-market/bisq-market.component'; -import { BisqTransactionsComponent } from './bisq-transactions/bisq-transactions.component'; -import { BisqTransactionComponent } from './bisq-transaction/bisq-transaction.component'; -import { BisqBlockComponent } from './bisq-block/bisq-block.component'; -import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.component'; -import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component'; -import { BisqIconComponent } from './bisq-icon/bisq-icon.component'; -import { BisqTransactionDetailsComponent } from './bisq-transaction-details/bisq-transaction-details.component'; -import { BisqTransfersComponent } from './bisq-transfers/bisq-transfers.component'; -import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; -import { faLeaf, faQuestion, faExclamationTriangle, faRocket, faRetweet, faFileAlt, faMoneyBill, - faEye, faEyeSlash, faLock, faLockOpen, faExclamationCircle } from '@fortawesome/free-solid-svg-icons'; -import { BisqBlocksComponent } from './bisq-blocks/bisq-blocks.component'; -import { BisqApiService } from './bisq-api.service'; -import { BisqAddressComponent } from './bisq-address/bisq-address.component'; -import { BisqStatsComponent } from './bisq-stats/bisq-stats.component'; -import { BsqAmountComponent } from './bsq-amount/bsq-amount.component'; -import { BisqTradesComponent } from './bisq-trades/bisq-trades.component'; -import { CommonModule } from '@angular/common'; -import { AutofocusDirective } from '../components/ngx-bootstrap-multiselect/autofocus.directive'; -import { MultiSelectSearchFilter } from '../components/ngx-bootstrap-multiselect/search-filter.pipe'; -import { OffClickDirective } from '../components/ngx-bootstrap-multiselect/off-click.directive'; -import { NgxDropdownMultiselectComponent } from '../components/ngx-bootstrap-multiselect/ngx-bootstrap-multiselect.component'; -import { BisqMasterPageComponent } from '../components/bisq-master-page/bisq-master-page.component'; - -@NgModule({ - declarations: [ - BisqMasterPageComponent, - BisqTransactionsComponent, - BisqTransactionComponent, - BisqBlockComponent, - BisqTransactionComponent, - BisqIconComponent, - BisqTransactionDetailsComponent, - BisqTransfersComponent, - BisqBlocksComponent, - BisqAddressComponent, - BisqStatsComponent, - BsqAmountComponent, - LightweightChartsComponent, - LightweightChartsAreaComponent, - BisqDashboardComponent, - BisqMarketComponent, - BisqTradesComponent, - BisqMainDashboardComponent, - NgxDropdownMultiselectComponent, - AutofocusDirective, - OffClickDirective, - ], - imports: [ - CommonModule, - BisqRoutingModule, - SharedModule, - FontAwesomeModule, - ], - providers: [ - BisqApiService, - MultiSelectSearchFilter, - AutofocusDirective, - OffClickDirective, - ] -}) -export class BisqModule { - constructor(library: FaIconLibrary) { - library.addIcons(faQuestion); - library.addIcons(faExclamationCircle); - library.addIcons(faExclamationTriangle); - library.addIcons(faRocket); - library.addIcons(faRetweet); - library.addIcons(faLeaf); - library.addIcons(faFileAlt); - library.addIcons(faMoneyBill); - library.addIcons(faEye); - library.addIcons(faEyeSlash); - library.addIcons(faLock); - library.addIcons(faLockOpen); - } -} diff --git a/frontend/src/app/bisq/bisq.routing.module.ts b/frontend/src/app/bisq/bisq.routing.module.ts deleted file mode 100644 index 7c6d2ee1b..000000000 --- a/frontend/src/app/bisq/bisq.routing.module.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { BisqMasterPageComponent } from '../components/bisq-master-page/bisq-master-page.component'; -import { BisqTransactionsComponent } from './bisq-transactions/bisq-transactions.component'; -import { BisqTransactionComponent } from './bisq-transaction/bisq-transaction.component'; -import { BisqBlockComponent } from './bisq-block/bisq-block.component'; -import { BisqBlocksComponent } from './bisq-blocks/bisq-blocks.component'; -import { BisqAddressComponent } from './bisq-address/bisq-address.component'; -import { BisqStatsComponent } from './bisq-stats/bisq-stats.component'; -import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.component'; -import { BisqMarketComponent } from './bisq-market/bisq-market.component'; -import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component'; -import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; - -const routes: Routes = [ - { - path: '', - component: BisqMasterPageComponent, - children: [ - { - path: '', - component: BisqMainDashboardComponent, - }, - { - path: 'markets', - data: { networks: ['bisq'] }, - component: BisqDashboardComponent, - }, - { - path: 'transactions', - data: { networks: ['bisq'] }, - component: BisqTransactionsComponent - }, - { - path: 'market/:pair', - data: { networkSpecific: true }, - component: BisqMarketComponent, - }, - { - path: 'tx/push', - component: PushTransactionComponent, - }, - { - path: 'tx/:id', - data: { networkSpecific: true }, - component: BisqTransactionComponent - }, - { - path: 'blocks', - children: [], - component: BisqBlocksComponent - }, - { - path: 'block/:id', - data: { networkSpecific: true }, - component: BisqBlockComponent, - }, - { - path: 'address/:id', - data: { networkSpecific: true }, - component: BisqAddressComponent, - }, - { - path: 'stats', - data: { networks: ['bisq'] }, - component: BisqStatsComponent, - }, - { - path: 'about', - loadChildren: () => import('../components/about/about.module').then(m => m.AboutModule), - }, - { - path: 'docs', - loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'api', - loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'terms-of-service', - loadChildren: () => import('../components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), - }, - { - path: '**', - redirectTo: '' - } - ] - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], -}) -export class BisqRoutingModule { } diff --git a/frontend/src/app/bisq/bsq-amount/bsq-amount.component.html b/frontend/src/app/bisq/bsq-amount/bsq-amount.component.html deleted file mode 100644 index 654adcb47..000000000 --- a/frontend/src/app/bisq/bsq-amount/bsq-amount.component.html +++ /dev/null @@ -1,6 +0,0 @@ - - {{ conversions.USD * bsq / 100 * (bsqPrice$ | async) / 100000000 | currency:'USD':'symbol':'1.2-2' }} - - - {{ bsq / 100 | number : digitsInfo }} BSQ - diff --git a/frontend/src/app/bisq/bsq-amount/bsq-amount.component.scss b/frontend/src/app/bisq/bsq-amount/bsq-amount.component.scss deleted file mode 100644 index 843bd58b6..000000000 --- a/frontend/src/app/bisq/bsq-amount/bsq-amount.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.green-color { - color: #3bcc49; -} diff --git a/frontend/src/app/bisq/bsq-amount/bsq-amount.component.ts b/frontend/src/app/bisq/bsq-amount/bsq-amount.component.ts deleted file mode 100644 index a3dd10e81..000000000 --- a/frontend/src/app/bisq/bsq-amount/bsq-amount.component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; -import { StateService } from '../../services/state.service'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'app-bsq-amount', - templateUrl: './bsq-amount.component.html', - styleUrls: ['./bsq-amount.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class BsqAmountComponent implements OnInit { - conversions$: Observable; - viewFiat$: Observable; - bsqPrice$: Observable; - - @Input() bsq: number; - @Input() digitsInfo = '1.2-2'; - @Input() forceFiat = false; - @Input() green = false; - - constructor( - private stateService: StateService, - ) { } - - ngOnInit() { - this.viewFiat$ = this.stateService.viewFiat$.asObservable(); - this.conversions$ = this.stateService.conversions$.asObservable(); - this.bsqPrice$ = this.stateService.bsqPrice$; - } -} diff --git a/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.scss b/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.scss deleted file mode 100644 index 56fe6ab0e..000000000 --- a/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.scss +++ /dev/null @@ -1,25 +0,0 @@ -:host ::ng-deep .floating-tooltip-2 { - width: 160px; - height: 80px; - position: absolute; - display: none; - padding: 8px; - box-sizing: border-box; - font-size: 12px; - color:rgba(255, 255, 255, 1); - background-color: #131722; - text-align: left; - z-index: 1000; - top: 12px; - left: 12px; - pointer-events: none; - border-radius: 2px; -} - -:host ::ng-deep .volumeText { - color: rgba(33, 150, 243, 0.7); -} - -:host ::ng-deep .tradesText { - color: rgba(37, 177, 53, 1); -} diff --git a/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.ts b/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.ts deleted file mode 100644 index 714d95eec..000000000 --- a/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { createChart, CrosshairMode, isBusinessDay } from 'lightweight-charts'; -import { ChangeDetectionStrategy, Component, ElementRef, HostListener, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; - -@Component({ - selector: 'app-lightweight-charts-area', - template: '', - styleUrls: ['./lightweight-charts-area.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class LightweightChartsAreaComponent implements OnInit, OnChanges, OnDestroy { - @Input() data: any; - @Input() lineData: any; - @Input() precision: number; - @Input() height = 500; - - areaSeries: any; - volumeSeries: any; - chart: any; - lineSeries: any; - container: any; - - width: number; - - constructor( - private element: ElementRef, - ) { } - - @HostListener('window:resize', ['$event']) - resizeCanvas(): void { - this.width = this.element.nativeElement.parentElement.offsetWidth; - this.chart.applyOptions({ - width: this.width, - height: this.height, - }); - } - - ngOnInit() { - this.width = this.element.nativeElement.parentElement.offsetWidth; - this.container = document.createElement('div'); - const chartholder = this.element.nativeElement.appendChild(this.container); - - this.chart = createChart(chartholder, { - width: this.width, - height: this.height, - crosshair: { - mode: CrosshairMode.Normal, - }, - layout: { - backgroundColor: '#000', - textColor: 'rgba(255, 255, 255, 0.8)', - }, - grid: { - vertLines: { - color: 'rgba(255, 255, 255, 0.1)', - }, - horzLines: { - color: 'rgba(255, 255, 255, 0.1)', - }, - }, - rightPriceScale: { - borderColor: 'rgba(255, 255, 255, 0.2)', - }, - timeScale: { - borderColor: 'rgba(255, 255, 255, 0.2)', - }, - }); - - this.lineSeries = this.chart.addLineSeries({ - color: 'rgba(37, 177, 53, 1)', - lineColor: 'rgba(216, 27, 96, 1)', - lineWidth: 2, - }); - - this.areaSeries = this.chart.addAreaSeries({ - topColor: 'rgba(33, 150, 243, 0.7)', - bottomColor: 'rgba(33, 150, 243, 0.1)', - lineColor: 'rgba(33, 150, 243, 0.1)', - lineWidth: 2, - }); - - const toolTip = document.createElement('div'); - toolTip.className = 'floating-tooltip-2'; - chartholder.appendChild(toolTip); - - this.chart.subscribeCrosshairMove((param) => { - if (!param.time || param.point.x < 0 || param.point.x > this.width || param.point.y < 0 || param.point.y > this.height) { - toolTip.style.display = 'none'; - return; - } - - const dateStr = isBusinessDay(param.time) - ? this.businessDayToString(param.time) - : new Date(param.time * 1000).toLocaleDateString(); - - toolTip.style.display = 'block'; - const price = param.seriesPrices.get(this.areaSeries); - const line = param.seriesPrices.get(this.lineSeries); - - const tradesText = $localize`:@@bisq-graph-trades:Trades`; - const volumeText = $localize`:@@bisq-graph-volume:Volume`; - - toolTip.innerHTML = ` - - -
${tradesText}:${Math.round(line * 100) / 100}
${volumeText}:${Math.round(price * 100) / 100} BTC
-
${dateStr}
`; - - const y = param.point.y; - - const toolTipWidth = 100; - const toolTipHeight = 80; - const toolTipMargin = 15; - - let left = param.point.x + toolTipMargin; - if (left > this.width - toolTipWidth) { - left = param.point.x - toolTipMargin - toolTipWidth; - } - - let top = y + toolTipMargin; - if (top > this.height - toolTipHeight) { - top = y - toolTipHeight - toolTipMargin; - } - - toolTip.style.left = left + 'px'; - toolTip.style.top = top + 'px'; - }); - - this.updateData(); - } - - businessDayToString(businessDay) { - return businessDay.year + '-' + businessDay.month + '-' + businessDay.day; - } - - ngOnChanges(changes: SimpleChanges) { - if (!changes.data || changes.data.isFirstChange()){ - return; - } - this.updateData(); - } - - updateData() { - this.areaSeries.setData(this.data); - this.lineSeries.setData(this.lineData); - } - - ngOnDestroy() { - this.chart.remove(); - } - -} diff --git a/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.scss b/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts b/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts deleted file mode 100644 index 620b61782..000000000 --- a/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { createChart, CrosshairMode } from 'lightweight-charts'; -import { ChangeDetectionStrategy, Component, ElementRef, HostListener, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; - -@Component({ - selector: 'app-lightweight-charts', - template: '', - styleUrls: ['./lightweight-charts.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class LightweightChartsComponent implements OnInit, OnChanges, OnDestroy { - @Input() data: any; - @Input() volumeData: any; - @Input() precision: number; - @Input() height = 500; - - lineSeries: any; - volumeSeries: any; - chart: any; - - constructor( - private element: ElementRef, - ) { } - - @HostListener('window:resize', ['$event']) - resizeCanvas(): void { - this.chart.applyOptions({ - width: this.element.nativeElement.parentElement.offsetWidth, - height: this.height, - }); - } - - ngOnInit() { - this.chart = createChart(this.element.nativeElement, { - width: this.element.nativeElement.parentElement.offsetWidth, - height: this.height, - layout: { - backgroundColor: '#000000', - textColor: '#d1d4dc', - }, - crosshair: { - mode: CrosshairMode.Normal, - }, - grid: { - vertLines: { - visible: true, - color: 'rgba(42, 46, 57, 0.5)', - }, - horzLines: { - color: 'rgba(42, 46, 57, 0.5)', - }, - }, - }); - this.lineSeries = this.chart.addCandlestickSeries(); - - this.volumeSeries = this.chart.addHistogramSeries({ - color: '#26a69a', - priceFormat: { - type: 'volume', - }, - priceScaleId: '', - scaleMargins: { - top: 0.85, - bottom: 0, - }, - }); - - this.updateData(); - } - - ngOnChanges(changes: SimpleChanges) { - if (!changes.data || changes.data.isFirstChange()){ - return; - } - this.updateData(); - } - - ngOnDestroy() { - this.chart.remove(); - } - - updateData() { - this.lineSeries.setData(this.data); - this.volumeSeries.setData(this.volumeData); - - this.lineSeries.applyOptions({ - priceFormat: { - type: 'price', - precision: this.precision, - minMove: 0.0000001, - }, - }); - } - -} diff --git a/frontend/src/app/components/about/about-sponsors.component.html b/frontend/src/app/components/about/about-sponsors.component.html index f76d74f8b..9471fc78f 100644 --- a/frontend/src/app/components/about/about-sponsors.component.html +++ b/frontend/src/app/components/about/about-sponsors.component.html @@ -1,16 +1,16 @@ -
+

If you're an individual...

- Become a Community Sponsor + Become a Community Sponsor

If you're a business...

- Become an Enterprise Sponsor + Become an Enterprise Sponsor
-
\ No newline at end of file +
diff --git a/frontend/src/app/components/about/about-sponsors.component.scss b/frontend/src/app/components/about/about-sponsors.component.scss index f3e675fd4..7c01bb9a3 100644 --- a/frontend/src/app/components/about/about-sponsors.component.scss +++ b/frontend/src/app/components/about/about-sponsors.component.scss @@ -6,6 +6,11 @@ align-items: center; gap: 20px; margin: 68px auto; + text-align: center; +} + +#become-sponsor-container.account { + margin: 20px auto; } .become-sponsor { diff --git a/frontend/src/app/components/about/about-sponsors.component.ts b/frontend/src/app/components/about/about-sponsors.component.ts index 31863cd8f..6a47c3bd4 100644 --- a/frontend/src/app/components/about/about-sponsors.component.ts +++ b/frontend/src/app/components/about/about-sponsors.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, Input } from '@angular/core'; import { EnterpriseService } from '../../services/enterprise.service'; @Component({ @@ -7,6 +7,9 @@ import { EnterpriseService } from '../../services/enterprise.service'; styleUrls: ['./about-sponsors.component.scss'], }) export class AboutSponsorsComponent { + @Input() host = 'https://mempool.space'; + @Input() context = 'about'; + constructor(private enterpriseService: EnterpriseService) { } diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html index 054f26fe6..009040889 100644 --- a/frontend/src/app/components/about/about.component.html +++ b/frontend/src/app/components/about/about.component.html @@ -416,7 +416,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the full license terms for more details.

- This program incorporates software and other components licensed from third parties. See the full list of Third-Party Licenses for legal notices from those projects. + This program incorporates software and other components licensed from third parties. See the full list of Third-Party Licenses for legal notices from those projects.

Trademark Notice
@@ -429,10 +429,6 @@

- -
diff --git a/frontend/src/app/components/about/about.component.ts b/frontend/src/app/components/about/about.component.ts index 3fea849a1..44bee5828 100644 --- a/frontend/src/app/components/about/about.component.ts +++ b/frontend/src/app/components/about/about.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, ElementRef, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core'; import { WebsocketService } from '../../services/websocket.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; import { StateService } from '../../services/state.service'; import { Observable } from 'rxjs'; import { ApiService } from '../../services/api.service'; @@ -33,6 +34,7 @@ export class AboutComponent implements OnInit { constructor( private websocketService: WebsocketService, private seoService: SeoService, + private ogService: OpenGraphService, public stateService: StateService, private enterpriseService: EnterpriseService, private apiService: ApiService, @@ -46,6 +48,7 @@ export class AboutComponent implements OnInit { this.backendInfo$ = this.stateService.backendInfo$; this.seoService.setTitle($localize`:@@004b222ff9ef9dd4771b777950ca1d0e4cd4348a:About`); this.seoService.setDescription($localize`:@@meta.description.about:Learn more about The Mempool Open Source Project®\: enterprise sponsors, individual sponsors, integrations, who contributes, FOSS licensing, and more.`); + this.ogService.setManualOgImage('about.jpg'); this.websocketService.want(['blocks']); this.profiles$ = this.apiService.getAboutPageProfiles$().pipe( diff --git a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.html b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.html index 2d2c9c3f3..a848a645b 100644 --- a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.html +++ b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.html @@ -129,7 +129,7 @@ - mempool.space fee + Accelerator Service Fee +{{ estimate.mempoolBaseFee | number }} @@ -141,7 +141,7 @@ - Transaction vsize fee + Transaction Size Surcharge +{{ estimate.vsizeFee | number }} @@ -219,7 +219,7 @@ - + @@ -228,6 +228,15 @@ + + + + + + Accelerate on mempool.space + + + diff --git a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts index d8ebb3830..3e8dbb6ff 100644 --- a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts +++ b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts @@ -4,7 +4,9 @@ import { Subscription, catchError, of, tap } from 'rxjs'; import { StorageService } from '../../services/storage.service'; import { Transaction } from '../../interfaces/electrs.interface'; import { nextRoundNumber } from '../../shared/common.utils'; +import { ServicesApiServices } from '../../services/services-api.service'; import { AudioService } from '../../services/audio.service'; +import { StateService } from '../../services/state.service'; export type AccelerationEstimate = { txSummary: TxSummary; @@ -62,7 +64,8 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges maxRateOptions: RateOption[] = []; constructor( - private apiService: ApiService, + public stateService: StateService, + private servicesApiService: ServicesApiServices, private storageService: StorageService, private audioService: AudioService, private cd: ChangeDetectorRef @@ -83,7 +86,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges ngOnInit() { this.user = this.storageService.getAuth()?.user ?? null; - this.estimateSubscription = this.apiService.estimate$(this.tx.txid).pipe( + this.estimateSubscription = this.servicesApiService.estimate$(this.tx.txid).pipe( tap((response) => { if (response.status === 204) { this.estimate = undefined; @@ -183,7 +186,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges if (this.accelerationSubscription) { this.accelerationSubscription.unsubscribe(); } - this.accelerationSubscription = this.apiService.accelerate$( + this.accelerationSubscription = this.servicesApiService.accelerate$( this.tx.txid, this.userBid ).subscribe({ @@ -213,4 +216,4 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges onResize(): void { this.isMobile = window.innerWidth <= 767.98; } -} \ No newline at end of file +} diff --git a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.html b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.html index 98095aa07..cbebb0f86 100644 --- a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.html +++ b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.html @@ -9,34 +9,46 @@ -
+
-
-
-
-
Total Bid Boost
-
-
- -
-
+
diff --git a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss index c4b4335ee..11b468a24 100644 --- a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss +++ b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss @@ -25,7 +25,8 @@ flex-direction: column; padding: 0px 15px; width: 100%; - height: calc(100vh - 250px); + height: calc(100vh - 225px); + min-height: 400px; @media (min-width: 992px) { height: calc(100vh - 150px); } @@ -35,6 +36,7 @@ display: flex; flex: 1; width: 100%; + height: 100%; padding-bottom: 20px; padding-right: 10px; @media (max-width: 992px) { @@ -53,11 +55,6 @@ padding-bottom: 55px; } } -.chart-widget { - width: 100%; - height: 100%; - max-height: 290px; -} h5 { margin-bottom: 10px; diff --git a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts index 59bca27d4..91de5ca03 100644 --- a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts +++ b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts @@ -1,8 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnDestroy, OnInit } from '@angular/core'; -import { EChartsOption, graphic } from 'echarts'; -import { Observable, Subscription, combineLatest } from 'rxjs'; -import { map, max, startWith, switchMap, tap } from 'rxjs/operators'; -import { ApiService } from '../../../services/api.service'; +import { EChartsOption } from '../../../graphs/echarts'; +import { Observable, Subscription, combineLatest, fromEvent, share } from 'rxjs'; +import { startWith, switchMap, tap } from 'rxjs/operators'; import { SeoService } from '../../../services/seo.service'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; @@ -11,6 +10,8 @@ import { StorageService } from '../../../services/storage.service'; import { MiningService } from '../../../services/mining.service'; import { ActivatedRoute } from '@angular/router'; import { Acceleration } from '../../../interfaces/node-api.interface'; +import { ServicesApiServices } from '../../../services/services-api.service'; +import { StateService } from '../../../services/state.service'; @Component({ selector: 'app-acceleration-fees-graph', @@ -28,6 +29,7 @@ import { Acceleration } from '../../../interfaces/node-api.interface'; }) export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { @Input() widget: boolean = false; + @Input() height: number = 300; @Input() right: number | string = 45; @Input() left: number | string = 75; @Input() accelerations$: Observable; @@ -40,8 +42,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { renderer: 'svg', }; - hrStatsObservable$: Observable; - statsObservable$: Observable; + aggregatedHistory$: Observable; statsSubscription: Subscription; isLoading = true; formatNumber = formatNumber; @@ -49,15 +50,17 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { chartInstance: any = undefined; currency: string; + daysAvailable: number = 0; constructor( @Inject(LOCALE_ID) public locale: string, private seoService: SeoService, - private apiService: ApiService, + private servicesApiService: ServicesApiServices, private formBuilder: UntypedFormBuilder, private storageService: StorageService, private miningService: MiningService, private route: ActivatedRoute, + public stateService: StateService, private cd: ChangeDetectorRef, ) { this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' }); @@ -66,103 +69,59 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { } ngOnInit(): void { - this.seoService.setTitle($localize`:@@bcf34abc2d9ed8f45a2f65dd464c46694e9a181e:Acceleration Fees`); - this.isLoading = true; if (this.widget) { - this.miningWindowPreference = '1m'; - this.timespan = this.miningWindowPreference; - - this.statsObservable$ = combineLatest([ - (this.accelerations$ || this.apiService.getAccelerationHistory$({ timeframe: this.miningWindowPreference })), - this.apiService.getHistoricalBlockFees$(this.miningWindowPreference), - ]).pipe( - tap(([accelerations, blockFeesResponse]) => { - this.prepareChartOptions(accelerations, blockFeesResponse.body); - }), - map(([accelerations, blockFeesResponse]) => { - return { - avgFeesPaid: accelerations.filter(acc => acc.status === 'completed').reduce((total, acc) => total + (acc.feePaid - acc.baseFee - acc.vsizeFee), 0) / accelerations.length - }; - }), - ); + this.miningWindowPreference = '3m'; } else { - this.miningWindowPreference = this.miningService.getDefaultTimespan('1w'); - this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); - this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); - this.route.fragment.subscribe((fragment) => { - if (['24h', '3d', '1w', '1m'].indexOf(fragment) > -1) { - this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false }); - } - }); - this.statsObservable$ = combineLatest([ - this.radioGroupForm.get('dateSpan').valueChanges.pipe( - startWith(this.radioGroupForm.controls.dateSpan.value), - switchMap((timespan) => { - this.isLoading = true; - this.storageService.setValue('miningWindowPreference', timespan); - this.timespan = timespan; - return this.apiService.getAccelerationHistory$({}); - }) - ), - this.radioGroupForm.get('dateSpan').valueChanges.pipe( - startWith(this.radioGroupForm.controls.dateSpan.value), - switchMap((timespan) => { - return this.apiService.getHistoricalBlockFees$(timespan); - }) - ) - ]).pipe( - tap(([accelerations, blockFeesResponse]) => { - this.prepareChartOptions(accelerations, blockFeesResponse.body); - }) - ); + this.seoService.setTitle($localize`:@@bcf34abc2d9ed8f45a2f65dd464c46694e9a181e:Acceleration Fees`); + this.miningWindowPreference = this.miningService.getDefaultTimespan('3m'); } - this.statsSubscription = this.statsObservable$.subscribe(() => { - this.isLoading = false; - this.cd.markForCheck(); + this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); + this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); + + this.route.fragment.subscribe((fragment) => { + if (['24h', '3d', '1w', '1m', '3m'].indexOf(fragment) > -1) { + this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false }); + } }); + this.aggregatedHistory$ = combineLatest([ + this.radioGroupForm.get('dateSpan').valueChanges.pipe( + startWith(this.radioGroupForm.controls.dateSpan.value), + switchMap((timespan) => { + if (!this.widget) { + this.storageService.setValue('miningWindowPreference', timespan); + } + this.isLoading = true; + this.timespan = timespan; + return this.servicesApiService.getAggregatedAccelerationHistory$({timeframe: this.timespan}); + }) + ), + fromEvent(window, 'resize').pipe(startWith(null)), + ]).pipe( + tap(([response]) => { + const history: Acceleration[] = response.body; + this.daysAvailable = (new Date().getTime() / 1000 - response.headers.get('x-oldest-accel')) / (24 * 3600); + this.isLoading = false; + this.prepareChartOptions(history); + this.cd.markForCheck(); + }), + share(), + ); + + this.aggregatedHistory$.subscribe(); } - prepareChartOptions(accelerations, blockFees) { + prepareChartOptions(data) { let title: object; - - const blockAccelerations = {}; - - for (const acceleration of accelerations) { - if (acceleration.status === 'completed') { - if (!blockAccelerations[acceleration.blockHeight]) { - blockAccelerations[acceleration.blockHeight] = []; - } - blockAccelerations[acceleration.blockHeight].push(acceleration); - } - } - - let last = null; - let minValue = Infinity; - let maxValue = 0; - const data = []; - for (const val of blockFees) { - if (last == null) { - last = val.avgHeight; - } - let totalFeeDelta = 0; - let totalFeePaid = 0; - let totalCount = 0; - let blockCount = 0; - while (last <= val.avgHeight) { - blockCount++; - totalFeeDelta += (blockAccelerations[last] || []).reduce((total, acc) => total + acc.feeDelta, 0); - totalFeePaid += (blockAccelerations[last] || []).reduce((total, acc) => total + (acc.feePaid - acc.baseFee - acc.vsizeFee), 0); - totalCount += (blockAccelerations[last] || []).length; - last++; - } - minValue = Math.min(minValue, val.avgFees); - maxValue = Math.max(maxValue, val.avgFees); - data.push({ - ...val, - feeDelta: totalFeeDelta, - avgFeePaid: (totalFeePaid / blockCount), - accelerations: totalCount / blockCount, - }); + if (data.length === 0) { + title = { + textStyle: { + color: 'grey', + fontSize: 15 + }, + text: $localize`No accelerated transaction for this timeframe`, + left: 'center', + top: 'center' + }; } this.chartOptions = { @@ -173,10 +132,11 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { ], animation: false, grid: { + height: (this.widget && this.height) ? this.height - 30 : undefined, + top: this.widget ? 20 : 40, + bottom: this.widget ? 30 : 80, right: this.right, left: this.left, - bottom: this.widget ? 30 : 80, - top: this.widget ? 20 : (this.isMobile() ? 10 : 50), }, tooltip: { show: !this.isMobile(), @@ -192,29 +152,23 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { align: 'left', }, borderColor: '#000', - formatter: function (data) { - if (data.length <= 0) { - return ''; - } - let tooltip = ` - ${formatterXAxis(this.locale, this.timespan, parseInt(data[0].axisValue, 10))}
`; + formatter: (ticks) => { + let tooltip = `${formatterXAxis(this.locale, this.timespan, parseInt(ticks[0].axisValue, 10))}
`; - for (const tick of data.reverse()) { - if (tick.data[1] >= 1_000_000) { - tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1] / 100_000_000, this.locale, '1.0-3')} BTC
`; - } else { - tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.0-0')} sats
`; - } + if (ticks[0].data[1] > 10_000_000) { + tooltip += `${ticks[0].marker} ${ticks[0].seriesName}: ${formatNumber(ticks[0].data[1] / 100_000_000, this.locale, '1.0-8')} BTC
`; + } else { + tooltip += `${ticks[0].marker} ${ticks[0].seriesName}: ${formatNumber(ticks[0].data[1], this.locale, '1.0-0')} sats
`; } if (['24h', '3d'].includes(this.timespan)) { - tooltip += `` + $localize`At block: ${data[0].data[2]}` + ``; + tooltip += `` + $localize`At block: ${ticks[0].data[2]}` + ``; } else { - tooltip += `` + $localize`Around block: ${data[0].data[2]}` + ``; + tooltip += `` + $localize`Around block: ${ticks[0].data[2]}` + ``; } return tooltip; - }.bind(this) + } }, xAxis: data.length === 0 ? undefined : { @@ -223,11 +177,11 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { nameTextStyle: { padding: [10, 0, 0, 0], }, - type: 'category', - boundaryGap: false, + type: 'time', + boundaryGap: [0, 0], axisLine: { onZero: true }, axisLabel: { - formatter: val => formatterXAxisTimeCategory(this.locale, this.timespan, parseInt(val, 10)), + formatter: (val): string => formatterXAxisTimeCategory(this.locale, this.timespan, val), align: 'center', fontSize: 11, lineHeight: 12, @@ -238,15 +192,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { legend: { data: [ { - name: 'In-band fees per block', - inactiveColor: 'rgb(110, 112, 121)', - textStyle: { - color: 'white', - }, - icon: 'roundRect', - }, - { - name: 'Total bid boost per block', + name: 'Total bid boost', inactiveColor: 'rgb(110, 112, 121)', textStyle: { color: 'white', @@ -255,8 +201,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { }, ], selected: { - 'In-band fees per block': false, - 'Total bid boost per block': true, + 'Total bid boost': true, }, show: !this.widget, }, @@ -299,22 +244,15 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { { legendHoverLink: false, zlevel: 1, - name: 'Total bid boost per block', - data: data.map(block => [block.timestamp * 1000, block.avgFeePaid, block.avgHeight]), + name: 'Total bid boost', + data: data.map(h => { + return [h.timestamp * 1000, h.sumBidBoost, h.avgHeight] + }), stack: 'Total', type: 'bar', - barWidth: '100%', - large: true, - }, - { - legendHoverLink: false, - zlevel: 0, - name: 'In-band fees per block', - data: data.map(block => [block.timestamp * 1000, block.avgFees, block.avgHeight]), - stack: 'Total', - type: 'bar', - barWidth: '100%', + barWidth: '90%', large: true, + barMinHeight: 1, }, ], dataZoom: (this.widget || data.length === 0 )? undefined : [{ @@ -342,17 +280,6 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { } }, }], - visualMap: { - type: 'continuous', - min: minValue, - max: maxValue, - dimension: 1, - seriesIndex: 1, - show: false, - inRange: { - color: ['#F4511E7f', '#FB8C007f', '#FFB3007f', '#FDD8357f', '#7CB3427f'].reverse() // Gradient color range - } - }, }; } diff --git a/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html index 5e8aa729a..fef91acc0 100644 --- a/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html +++ b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html @@ -3,16 +3,16 @@
Requests
-
{{ stats.count }}
+
{{ stats.totalRequested }}
accelerated
Total Bid Boost
-
{{ stats.totalFeesPaid / 100_000_000 | amountShortener: 4 }} BTC
+
{{ stats.totalBidBoost / 100_000_000 | amountShortener: 4 }} BTC
- +
diff --git a/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts index 0a6ef065c..29909ff39 100644 --- a/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts +++ b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts @@ -1,9 +1,12 @@ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; -import { Observable, of } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; -import { ApiService } from '../../../services/api.service'; -import { StateService } from '../../../services/state.service'; -import { Acceleration } from '../../../interfaces/node-api.interface'; +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { ServicesApiServices } from '../../../services/services-api.service'; + +export type AccelerationStats = { + totalRequested: number; + totalBidBoost: number; + successRate: number; +} @Component({ selector: 'app-acceleration-stats', @@ -12,35 +15,13 @@ import { Acceleration } from '../../../interfaces/node-api.interface'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class AccelerationStatsComponent implements OnInit { - @Input() timespan: '24h' | '1w' | '1m' = '24h'; - @Input() accelerations$: Observable; - public accelerationStats$: Observable; + accelerationStats$: Observable; constructor( - private apiService: ApiService, - private stateService: StateService, + private servicesApiService: ServicesApiServices ) { } ngOnInit(): void { - this.accelerationStats$ = this.accelerations$.pipe( - switchMap(accelerations => { - let totalFeesPaid = 0; - let totalSucceeded = 0; - let totalCanceled = 0; - for (const acc of accelerations) { - if (acc.status === 'completed') { - totalSucceeded++; - totalFeesPaid += (acc.feePaid - acc.baseFee - acc.vsizeFee) || 0; - } else if (acc.status === 'failed') { - totalCanceled++; - } - } - return of({ - count: totalSucceeded, - totalFeesPaid, - successRate: (totalSucceeded + totalCanceled > 0) ? ((totalSucceeded / (totalSucceeded + totalCanceled)) * 100) : 0.0, - }); - }) - ); + this.accelerationStats$ = this.servicesApiService.getAccelerationStats$(); } } diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html index 9a919ca54..8d217cb6b 100644 --- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html @@ -1,4 +1,4 @@ -
+

Accelerations

@@ -17,6 +17,7 @@ Bid Boost Block Status + Requested @@ -45,12 +46,16 @@ ~ - {{ acceleration.blockHeight }} + {{ acceleration.blockHeight }} + ~ Pending - Mined - Canceled + Completed 🔄 + Failed 🔄 + + + @@ -75,6 +80,11 @@ + + +

diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss index 4182abb68..85e655b25 100644 --- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss @@ -63,66 +63,82 @@ tr, td, th { } .txid { - width: 25%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 30%; - @media (max-width: 1060px) and (min-width: 768px) { - display: none; - } - @media (max-width: 500px) { - display: none; - } -} - -.fee-rate { width: 20%; - @media (max-width: 1060px) and (min-width: 768px) { - text-align: start !important; - } - @media (max-width: 500px) { - text-align: start !important; - } - @media (max-width: 840px) and (min-width: 768px) { - display: none; - } - @media (max-width: 410px) { - display: none; - } -} - -.bid { - width: 30%; - min-width: 150px; - @media (max-width: 840px) and (min-width: 768px) { - text-align: start !important; - } - @media (max-width: 410px) { - text-align: start !important; - } -} - -.time { - width: 25%; } .fee { - width: 35%; - @media (max-width: 1060px) and (min-width: 768px) { - text-align: start !important; - } - @media (max-width: 500px) { - text-align: start !important; - } + width: 15%; } .block { - width: 20%; + width: 15%; + @media (max-width: 700px) { + display: none; + } } .status { - width: 20% + width: 13%; +} + +.date { + width: 20%; + @media (max-width: 600px) { + display: none; + } +} + +.widget { + .txid { + width: 30%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 30%; + } + + .fee-rate { + width: 20%; + text-align: end !important; + @media (max-width: 975px) and (min-width: 768px) { + display: none; + } + @media (max-width: 410px) { + display: none; + } + } + + .bid { + text-align: end !important; + width: 30%; + min-width: 150px; + } + + .time { + width: 25%; + @media (max-width: 600px) { + display: none; + } + @media (max-width: 1200px) and (min-width: 768px) { + display: none; + } + } + + .fee { + width: 30%; + text-align: end !important; + } + + .block { + width: 20%; + @media (max-width: 1200px) and (min-width: 768px) { + display: none; + } + } + + .status { + width: 20% + } } /* Tooltip text */ diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts index 69af8b966..1a0aacbb6 100644 --- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core'; -import { Observable, catchError, of, switchMap, tap } from 'rxjs'; +import { combineLatest, BehaviorSubject, Observable, catchError, of, switchMap, tap } from 'rxjs'; import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface'; -import { ApiService } from '../../../services/api.service'; import { StateService } from '../../../services/state.service'; import { WebsocketService } from '../../../services/websocket.service'; +import { ServicesApiServices } from '../../../services/services-api.service'; @Component({ selector: 'app-accelerations-list', @@ -21,12 +21,13 @@ export class AccelerationsListComponent implements OnInit { isLoading = true; paginationMaxSize: number; page = 1; - lastPage = 1; + accelerationCount: number; maxSize = window.innerWidth <= 767.98 ? 3 : 5; skeletonLines: number[] = []; + pageSubject: BehaviorSubject = new BehaviorSubject(this.page); constructor( - private apiService: ApiService, + private servicesApiService: ServicesApiServices, private websocketService: WebsocketService, public stateService: StateService, private cd: ChangeDetectorRef, @@ -40,34 +41,47 @@ export class AccelerationsListComponent implements OnInit { this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; - - const accelerationObservable$ = this.accelerations$ || (this.pending ? this.apiService.getAccelerations$() : this.apiService.getAccelerationHistory$({ timeframe: '1m' })); - this.accelerationList$ = accelerationObservable$.pipe( - switchMap(accelerations => { - if (this.pending) { - for (const acceleration of accelerations) { - acceleration.status = acceleration.status || 'accelerating'; - } - } - for (const acc of accelerations) { - acc.boost = acc.feePaid - acc.baseFee - acc.vsizeFee; - } - if (this.widget) { - return of(accelerations.slice(-6).reverse()); - } else { - return of(accelerations.reverse()); - } - }), - catchError((err) => { - this.isLoading = false; - return of([]); - }), - tap(() => { - this.isLoading = false; + + this.accelerationList$ = this.pageSubject.pipe( + switchMap((page) => { + const accelerationObservable$ = this.accelerations$ || (this.pending ? this.servicesApiService.getAccelerations$() : this.servicesApiService.getAccelerationHistoryObserveResponse$({ page: page })); + return accelerationObservable$.pipe( + switchMap(response => { + let accelerations = response; + if (response.body) { + accelerations = response.body; + this.accelerationCount = parseInt(response.headers.get('x-total-count'), 10); + } + if (this.pending) { + for (const acceleration of accelerations) { + acceleration.status = acceleration.status || 'accelerating'; + } + } + for (const acc of accelerations) { + acc.boost = acc.feePaid - acc.baseFee - acc.vsizeFee; + } + if (this.widget) { + return of(accelerations.slice(0, 6)); + } else { + return of(accelerations); + } + }), + catchError((err) => { + this.isLoading = false; + return of([]); + }), + tap(() => { + this.isLoading = false; + }) + ); }) ); } + pageChange(page: number): void { + this.pageSubject.next(page); + } + trackByBlock(index: number, block: BlockExtended): number { return block.height; } diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html index 243a48939..147e07e69 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html @@ -22,12 +22,12 @@
Acceleration stats  - (1 month) + (3 months)
- +
@@ -37,7 +37,12 @@
- @@ -48,22 +53,19 @@
- +
Total Bid Boost
+
+ +
- - -
@@ -71,7 +73,7 @@ - +
@@ -80,7 +82,7 @@
- +
Recent Accelerations
  diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss index 145569342..c8755c94e 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss @@ -17,6 +17,16 @@ } } +.mempool-graph { + height: 295px; + @media (min-width: 768px) { + height: 325px; + } + @media (min-width: 992px) { + height: 409px; + } +} + .card-title { font-size: 1rem; color: #4a68b9; @@ -135,7 +145,12 @@ } .card { - height: 385px; + @media (min-width: 768px) { + height: 420px; + } + @media (min-width: 992px) { + height: 510px; + } } .list-card { height: 410px; @@ -145,7 +160,16 @@ } .mempool-block-wrapper { - max-height: 380px; - max-width: 380px; + max-height: 430px; + max-width: 430px; margin: auto; + + @media (min-width: 768px) { + max-height: 344px; + max-width: 344px; + } + @media (min-width: 992px) { + max-height: 430px; + max-width: 430px; + } } \ No newline at end of file diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts index 79a77a600..d4df57020 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts @@ -1,14 +1,16 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostListener, Inject, OnInit, PLATFORM_ID } from '@angular/core'; import { SeoService } from '../../../services/seo.service'; +import { OpenGraphService } from '../../../services/opengraph.service'; import { WebsocketService } from '../../../services/websocket.service'; import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface'; import { StateService } from '../../../services/state.service'; -import { Observable, Subject, catchError, combineLatest, distinctUntilChanged, interval, map, of, share, startWith, switchMap, tap } from 'rxjs'; -import { ApiService } from '../../../services/api.service'; +import { Observable, catchError, combineLatest, distinctUntilChanged, interval, map, of, share, startWith, switchMap, tap } from 'rxjs'; import { Color } from '../../block-overview-graph/sprite-types'; import { hexToColor } from '../../block-overview-graph/utils'; import TxView from '../../block-overview-graph/tx-view'; import { feeLevels, mempoolFeeColors } from '../../../app.constants'; +import { ServicesApiServices } from '../../../services/services-api.service'; +import { detectWebGL } from '../../../shared/graphs.utils'; const acceleratedColor: Color = hexToColor('8F5FF6'); const normalColors = mempoolFeeColors.map(hex => hexToColor(hex + '5F')); @@ -29,44 +31,54 @@ export class AcceleratorDashboardComponent implements OnInit { pendingAccelerations$: Observable; minedAccelerations$: Observable; loadingBlocks: boolean = true; + webGlEnabled = true; + + graphHeight: number = 300; constructor( private seoService: SeoService, + private ogService: OpenGraphService, private websocketService: WebsocketService, - private apiService: ApiService, + private serviceApiServices: ServicesApiServices, private stateService: StateService, + @Inject(PLATFORM_ID) private platformId: Object, ) { + this.webGlEnabled = this.stateService.isBrowser && detectWebGL(); this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Accelerator Dashboard`); + this.ogService.setManualOgImage('accelerator.jpg'); } ngOnInit(): void { + this.onResize(); this.websocketService.want(['blocks', 'mempool-blocks', 'stats']); - this.pendingAccelerations$ = interval(30000).pipe( + this.pendingAccelerations$ = (this.stateService.isBrowser ? interval(30000) : of(null)).pipe( startWith(true), switchMap(() => { - return this.apiService.getAccelerations$(); - }), - catchError((e) => { - return of([]); + return this.serviceApiServices.getAccelerations$().pipe( + catchError(() => { + return of([]); + }), + ); }), share(), ); this.accelerations$ = this.stateService.chainTip$.pipe( distinctUntilChanged(), - switchMap((chainTip) => { - return this.apiService.getAccelerationHistory$({ timeframe: '1m' }); - }), - catchError((e) => { - return of([]); + switchMap(() => { + return this.serviceApiServices.getAccelerationHistory$({}).pipe( + catchError(() => { + return of([]); + }), + ); }), share(), ); this.minedAccelerations$ = this.accelerations$.pipe( map(accelerations => { - return accelerations.filter(acc => ['mined', 'completed'].includes(acc.status)) + return accelerations.filter(acc => ['completed_provisional', 'completed'].includes(acc.status)); }) ); @@ -95,7 +107,7 @@ export class AcceleratorDashboardComponent implements OnInit { } const accelerationsByBlock: { [ hash: string ]: Acceleration[] } = {}; for (const acceleration of accelerations) { - if (['mined', 'completed'].includes(acceleration.status) && acceleration.pools.includes(blockMap[acceleration.blockHash]?.extras.pool.id)) { + if (['completed_provisional', 'failed_provisional', 'completed'].includes(acceleration.status) && acceleration.pools.includes(blockMap[acceleration.blockHash]?.extras.pool.id)) { if (!accelerationsByBlock[acceleration.blockHash]) { accelerationsByBlock[acceleration.blockHash] = []; } @@ -119,4 +131,15 @@ export class AcceleratorDashboardComponent implements OnInit { return normalColors[feeLevelIndex] || normalColors[mempoolFeeColors.length - 1]; } } + + @HostListener('window:resize', ['$event']) + onResize(): void { + if (window.innerWidth >= 992) { + this.graphHeight = 380; + } else if (window.innerWidth >= 768) { + this.graphHeight = 300; + } else { + this.graphHeight = 270; + } + } } diff --git a/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts index f344c37a0..ed7061156 100644 --- a/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts +++ b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { Observable, of } from 'rxjs'; import { switchMap } from 'rxjs/operators'; -import { ApiService } from '../../../services/api.service'; import { Acceleration } from '../../../interfaces/node-api.interface'; +import { ServicesApiServices } from '../../../services/services-api.service'; @Component({ selector: 'app-pending-stats', @@ -15,11 +15,11 @@ export class PendingStatsComponent implements OnInit { public accelerationStats$: Observable; constructor( - private apiService: ApiService, + private servicesApiService: ServicesApiServices, ) { } ngOnInit(): void { - this.accelerationStats$ = (this.accelerations$ || this.apiService.getAccelerations$()).pipe( + this.accelerationStats$ = (this.accelerations$ || this.servicesApiService.getAccelerations$()).pipe( switchMap(accelerations => { let totalAccelerations = 0; let totalFeeDelta = 0; diff --git a/frontend/src/app/components/address-graph/address-graph.component.html b/frontend/src/app/components/address-graph/address-graph.component.html new file mode 100644 index 000000000..35808cb14 --- /dev/null +++ b/frontend/src/app/components/address-graph/address-graph.component.html @@ -0,0 +1,23 @@ + + +
+
+
+ Balance History +
+
+ + +
+
+
+
+
+
+ +
+

{{ error }}

+
+
+
diff --git a/frontend/src/app/components/address-graph/address-graph.component.scss b/frontend/src/app/components/address-graph/address-graph.component.scss new file mode 100644 index 000000000..d23b95d8d --- /dev/null +++ b/frontend/src/app/components/address-graph/address-graph.component.scss @@ -0,0 +1,75 @@ +.card-header { + border-bottom: 0; + font-size: 18px; + @media (min-width: 465px) { + font-size: 20px; + } + @media (min-width: 992px) { + height: 40px; + } +} + +.main-title { + position: relative; + color: #ffffff91; + margin-top: -13px; + font-size: 10px; + text-transform: uppercase; + font-weight: 500; + text-align: center; + padding-bottom: 3px; +} + +.full-container { + display: flex; + flex-direction: column; + padding: 0px; + width: 100%; + height: 400px; +} + +.error-wrapper { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + align-items: center; + justify-content: center; + + font-size: 15px; + color: grey; + font-weight: bold; +} + +.chart { + display: flex; + flex: 1; + width: 100%; + padding-bottom: 20px; + padding-right: 10px; + @media (max-width: 992px) { + padding-bottom: 25px; + } + @media (max-width: 829px) { + padding-bottom: 50px; + } + @media (max-width: 767px) { + padding-bottom: 25px; + } + @media (max-width: 629px) { + padding-bottom: 55px; + } + @media (max-width: 567px) { + padding-bottom: 55px; + } +} +.chart-widget { + width: 100%; + height: 100%; + max-height: 270px; +} + +.disabled { + pointer-events: none; + opacity: 0.5; +} \ No newline at end of file diff --git a/frontend/src/app/components/address-graph/address-graph.component.ts b/frontend/src/app/components/address-graph/address-graph.component.ts new file mode 100644 index 000000000..a73229495 --- /dev/null +++ b/frontend/src/app/components/address-graph/address-graph.component.ts @@ -0,0 +1,198 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnChanges, SimpleChanges } from '@angular/core'; +import { echarts, EChartsOption } from '../../graphs/echarts'; +import { of } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { ChainStats } from '../../interfaces/electrs.interface'; +import { ElectrsApiService } from '../../services/electrs-api.service'; +import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-address-graph', + templateUrl: './address-graph.component.html', + styleUrls: ['./address-graph.component.scss'], + styles: [` + .loadingGraphs { + position: absolute; + top: 50%; + left: calc(50% - 15px); + z-index: 100; + } + `], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AddressGraphComponent implements OnChanges { + @Input() address: string; + @Input() isPubkey: boolean = false; + @Input() stats: ChainStats; + @Input() right: number | string = 10; + @Input() left: number | string = 70; + + data: any[] = []; + hoverData: any[] = []; + + chartOptions: EChartsOption = {}; + chartInitOptions = { + renderer: 'svg', + }; + + error: any; + isLoading = true; + chartInstance: any = undefined; + + constructor( + @Inject(LOCALE_ID) public locale: string, + private electrsApiService: ElectrsApiService, + private router: Router, + private amountShortenerPipe: AmountShortenerPipe, + private cd: ChangeDetectorRef, + ) {} + + ngOnChanges(changes: SimpleChanges): void { + this.isLoading = true; + (this.isPubkey + ? this.electrsApiService.getScriptHashSummary$((this.address.length === 66 ? '21' : '41') + this.address + 'ac') + : this.electrsApiService.getAddressSummary$(this.address)).pipe( + catchError(e => { + this.error = `Failed to fetch address balance history: ${e?.status || ''} ${e?.statusText || 'unknown error'}`; + return of(null); + }), + ).subscribe(addressSummary => { + if (addressSummary) { + this.error = null; + this.prepareChartOptions(addressSummary); + } + this.isLoading = false; + this.cd.markForCheck(); + }); + } + + prepareChartOptions(summary): void { + let total = (this.stats.funded_txo_sum - this.stats.spent_txo_sum); // + (summary[0]?.value || 0); + this.data = summary.map(d => { + const balance = total; + total -= d.value; + return [d.time * 1000, balance, d]; + }).reverse(); + + const maxValue = this.data.reduce((acc, d) => Math.max(acc, Math.abs(d[1])), 0); + + this.chartOptions = { + color: [ + new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: '#FDD835' }, + { offset: 1, color: '#FB8C00' }, + ]), + ], + animation: false, + grid: { + top: 20, + bottom: 20, + right: this.right, + left: this.left, + }, + tooltip: { + show: !this.isMobile(), + trigger: 'axis', + axisPointer: { + type: 'line' + }, + backgroundColor: 'rgba(17, 19, 31, 1)', + borderRadius: 4, + shadowColor: 'rgba(0, 0, 0, 0.5)', + textStyle: { + color: '#b1b1b1', + align: 'left', + }, + borderColor: '#000', + formatter: function (data): string { + const header = data.length === 1 + ? `${data[0].data[2].txid.slice(0, 6)}...${data[0].data[2].txid.slice(-6)}` + : `${data.length} transactions`; + const date = new Date(data[0].data[0]).toLocaleTimeString(this.locale, { year: 'numeric', month: 'short', day: 'numeric' }); + const val = data.reduce((total, d) => total + d.data[2].value, 0); + const color = val === 0 ? '' : (val > 0 ? '#1a9436' : '#dc3545'); + const symbol = val > 0 ? '+' : ''; + return ` +
+ ${header} +
+ ${symbol} ${(val / 100_000_000).toFixed(8)} BTC
+ ${(data[0].data[1] / 100_000_000).toFixed(8)} BTC +
+ ${date} +
+ `; + }.bind(this) + }, + xAxis: { + type: 'time', + splitNumber: this.isMobile() ? 5 : 10, + axisLabel: { + hideOverlap: true, + } + }, + yAxis: [ + { + type: 'value', + position: 'left', + axisLabel: { + color: 'rgb(110, 112, 121)', + formatter: (val): string => { + if (maxValue > 1_000_000_000) { + return `${this.amountShortenerPipe.transform(Math.round(val / 100_000_000), 0)} BTC`; + } else if (maxValue > 100_000_000) { + return `${(val / 100_000_000).toFixed(1)} BTC`; + } else if (maxValue > 10_000_000) { + return `${(val / 100_000_000).toFixed(2)} BTC`; + } else if (maxValue > 1_000_000) { + return `${(val / 100_000_000).toFixed(3)} BTC`; + } else { + return `${this.amountShortenerPipe.transform(val, 0)} sats`; + } + } + }, + splitLine: { + show: false, + }, + }, + ], + series: [ + { + name: $localize`Balance:Balance`, + showSymbol: false, + symbol: 'circle', + symbolSize: 8, + data: this.data, + areaStyle: { + opacity: 0.5, + }, + triggerLineEvent: true, + type: 'line', + smooth: false, + step: 'end' + } + ], + }; + } + + onChartClick(e) { + if (this.hoverData?.length && this.hoverData[0]?.[2]?.txid) { + this.router.navigate(['/tx/', this.hoverData[0][2].txid]); + } + } + + onTooltip(e) { + this.hoverData = (e?.dataByCoordSys?.[0]?.dataByAxis?.[0]?.seriesDataIndices || []).map(indices => this.data[indices.dataIndex]); + } + + onChartInit(ec) { + this.chartInstance = ec; + this.chartInstance.on('showTip', this.onTooltip.bind(this)); + this.chartInstance.on('click', 'series', this.onChartClick.bind(this)); + } + + isMobile() { + return (window.innerWidth <= 767.98); + } +} diff --git a/frontend/src/app/components/address-group/address-group.component.html b/frontend/src/app/components/address-group/address-group.component.html new file mode 100644 index 000000000..174853600 --- /dev/null +++ b/frontend/src/app/components/address-group/address-group.component.html @@ -0,0 +1,24 @@ +
+
+ +

Balances

+
+
+ + + + + + + + + + + +
Total
+ +
+ +
diff --git a/frontend/src/app/components/address-group/address-group.component.scss b/frontend/src/app/components/address-group/address-group.component.scss new file mode 100644 index 000000000..0ac00b85b --- /dev/null +++ b/frontend/src/app/components/address-group/address-group.component.scss @@ -0,0 +1,101 @@ +.frame { + position: relative; + background: #24273e; + padding: 0.5rem; + height: calc(100% + 60px); +} + +.heading { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: flex-start; + + & > * { + flex-basis: 0; + flex-grow: 1; + } + + h3 { + text-align: center; + margin: 0 1em; + } +} + +.pagination { + position: absolute; + bottom: 0.5rem; + right: 0.5rem; +} + +.table { + margin-top: 0.5em; + + td, th { + padding: 0.15rem 0.5rem; + + &.address { + width: auto; + } + &.btc { + width: 140px; + text-align: right; + } + &.fiat { + width: 142px; + text-align: right; + } + } + + tr { + border-collapse: collapse; + + &:first-child { + border-bottom: solid 1px white; + td, th { + padding-bottom: 0.3rem; + } + } + &:nth-child(2) { + td, th { + padding-top: 0.3rem; + } + } + &:nth-child(even) { + background: #181b2d; + } + } + + @media (min-width: 528px) { + td, th { + &.btc { + width: 160px; + } + &.fiat { + width: 140px; + } + } + } + + @media (min-width: 576px) { + td, th { + &.btc { + width: 170px; + } + &.fiat { + width: 140px; + } + } + } + + @media (min-width: 992px) { + td, th { + &.btc { + width: 210px; + } + &.fiat { + width: 140px; + } + } + } +} \ No newline at end of file diff --git a/frontend/src/app/components/address-group/address-group.component.ts b/frontend/src/app/components/address-group/address-group.component.ts new file mode 100644 index 000000000..30bee7543 --- /dev/null +++ b/frontend/src/app/components/address-group/address-group.component.ts @@ -0,0 +1,212 @@ +import { Component, OnInit, OnDestroy, ChangeDetectorRef, HostListener } from '@angular/core'; +import { ActivatedRoute, ParamMap } from '@angular/router'; +import { ElectrsApiService } from '../../services/electrs-api.service'; +import { switchMap, catchError } from 'rxjs/operators'; +import { Address, Transaction } from '../../interfaces/electrs.interface'; +import { WebsocketService } from '../../services/websocket.service'; +import { StateService } from '../../services/state.service'; +import { AudioService } from '../../services/audio.service'; +import { ApiService } from '../../services/api.service'; +import { of, Subscription, forkJoin } from 'rxjs'; +import { SeoService } from '../../services/seo.service'; +import { AddressInformation } from '../../interfaces/node-api.interface'; + +@Component({ + selector: 'app-address-group', + templateUrl: './address-group.component.html', + styleUrls: ['./address-group.component.scss'] +}) +export class AddressGroupComponent implements OnInit, OnDestroy { + network = ''; + + balance = 0; + confirmed = 0; + mempool = 0; + addresses: { [address: string]: number | null }; + addressStrings: string[] = []; + addressInfo: { [address: string]: AddressInformation | null }; + seenTxs: { [txid: string ]: boolean } = {}; + isLoadingAddress = true; + error: any; + mainSubscription: Subscription; + wsSubscription: Subscription; + + page: string[] = []; + pageIndex: number = 1; + itemsPerPage: number = 10; + + screenSize: 'lg' | 'md' | 'sm' = 'lg'; + digitsInfo: string = '1.8-8'; + + constructor( + private route: ActivatedRoute, + private electrsApiService: ElectrsApiService, + private websocketService: WebsocketService, + private stateService: StateService, + private audioService: AudioService, + private apiService: ApiService, + private seoService: SeoService, + private cd: ChangeDetectorRef, + ) { } + + ngOnInit(): void { + this.onResize(); + this.stateService.networkChanged$.subscribe((network) => this.network = network); + this.websocketService.want(['blocks']); + + this.mainSubscription = this.route.queryParamMap + .pipe( + switchMap((params: ParamMap) => { + this.error = undefined; + this.isLoadingAddress = true; + this.addresses = {}; + this.addressInfo = {}; + this.balance = 0; + + this.addressStrings = params.get('addresses').split(',').map(address => { + if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}$/.test(address)) { + return address.toLowerCase(); + } else { + return address; + } + }); + + return forkJoin(this.addressStrings.map(address => { + const getLiquidInfo = ((this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') && /^([a-zA-HJ-NP-Z1-9]{26,35}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[a-km-zA-HJ-NP-Z1-9]{80})$/.test(address)); + return forkJoin([ + of(address), + this.electrsApiService.getAddress$(address), + (getLiquidInfo ? this.apiService.validateAddress$(address) : of(null)), + ]); + })); + }), + catchError(e => { + this.error = e; + return of([]); + }) + ).subscribe((addresses) => { + for (const addressData of addresses) { + const address = addressData[0]; + const addressBalance = addressData[1] as Address; + if (addressBalance) { + this.addresses[address] = addressBalance.chain_stats.funded_txo_sum + + addressBalance.mempool_stats.funded_txo_sum + - addressBalance.chain_stats.spent_txo_sum + - addressBalance.mempool_stats.spent_txo_sum; + this.balance += this.addresses[address]; + this.confirmed += (addressBalance.chain_stats.funded_txo_sum - addressBalance.chain_stats.spent_txo_sum); + } + this.addressInfo[address] = addressData[2] ? addressData[2] as AddressInformation : null; + } + this.websocketService.startTrackAddresses(this.addressStrings); + this.isLoadingAddress = false; + this.pageChange(this.pageIndex); + }); + + this.wsSubscription = this.stateService.multiAddressTransactions$.subscribe(update => { + for (const address of Object.keys(update)) { + for (const tx of update[address].mempool) { + this.addTransaction(tx, false, false); + } + for (const tx of update[address].confirmed) { + this.addTransaction(tx, true, false); + } + for (const tx of update[address].removed) { + this.removeTransaction(tx, tx.status.confirmed); + } + } + }); + } + + pageChange(index): void { + this.page = this.addressStrings.slice((index - 1) * this.itemsPerPage, index * this.itemsPerPage); + } + + addTransaction(transaction: Transaction, confirmed = false, playSound: boolean = true): boolean { + if (this.seenTxs[transaction.txid]) { + this.removeTransaction(transaction, false); + } + this.seenTxs[transaction.txid] = true; + + let balance = 0; + transaction.vin.forEach((vin) => { + if (this.addressStrings.includes(vin?.prevout?.scriptpubkey_address)) { + this.addresses[vin?.prevout?.scriptpubkey_address] -= vin.prevout.value; + balance -= vin.prevout.value; + this.balance -= vin.prevout.value; + if (confirmed) { + this.confirmed -= vin.prevout.value; + } + } + }); + transaction.vout.forEach((vout) => { + if (this.addressStrings.includes(vout?.scriptpubkey_address)) { + this.addresses[vout?.scriptpubkey_address] += vout.value; + balance += vout.value; + this.balance += vout.value; + if (confirmed) { + this.confirmed += vout.value; + } + } + }); + + if (playSound) { + if (balance > 0) { + this.audioService.playSound('cha-ching'); + } else { + this.audioService.playSound('chime'); + } + } + + return true; + } + + removeTransaction(transaction: Transaction, confirmed = false): boolean { + transaction.vin.forEach((vin) => { + if (this.addressStrings.includes(vin?.prevout?.scriptpubkey_address)) { + this.addresses[vin?.prevout?.scriptpubkey_address] += vin.prevout.value; + this.balance += vin.prevout.value; + if (confirmed) { + this.confirmed += vin.prevout.value; + } + } + }); + transaction.vout.forEach((vout) => { + if (this.addressStrings.includes(vout?.scriptpubkey_address)) { + this.addresses[vout?.scriptpubkey_address] -= vout.value; + this.balance -= vout.value; + if (confirmed) { + this.confirmed -= vout.value; + } + } + }); + + return true; + } + + @HostListener('window:resize', ['$event']) + onResize(): void { + if (window.innerWidth >= 992) { + this.screenSize = 'lg'; + this.digitsInfo = '1.8-8'; + } else if (window.innerWidth >= 528) { + this.screenSize = 'md'; + this.digitsInfo = '1.4-4'; + } else { + this.screenSize = 'sm'; + this.digitsInfo = '1.2-2'; + } + const newItemsPerPage = Math.floor((window.innerHeight - 150) / 30); + if (newItemsPerPage !== this.itemsPerPage) { + this.itemsPerPage = newItemsPerPage; + this.pageIndex = 1; + this.pageChange(this.pageIndex); + } + } + + ngOnDestroy(): void { + this.mainSubscription.unsubscribe(); + this.wsSubscription.unsubscribe(); + this.websocketService.stopTrackingAddresses(); + } +} diff --git a/frontend/src/app/components/address-labels/address-labels.component.ts b/frontend/src/app/components/address-labels/address-labels.component.ts index f4b3d0ca5..2365c167f 100644 --- a/frontend/src/app/components/address-labels/address-labels.component.ts +++ b/frontend/src/app/components/address-labels/address-labels.component.ts @@ -43,7 +43,7 @@ export class AddressLabelsComponent implements OnChanges { handleVin() { if (this.vin.inner_witnessscript_asm) { - if (this.vin.inner_witnessscript_asm.indexOf('OP_DEPTH OP_PUSHNUM_12 OP_EQUAL OP_IF OP_PUSHNUM_11') === 0) { + if (this.vin.inner_witnessscript_asm.indexOf('OP_DEPTH OP_PUSHNUM_12 OP_EQUAL OP_IF OP_PUSHNUM_11') === 0 || this.vin.inner_witnessscript_asm.indexOf('OP_PUSHNUM_15 OP_CHECKMULTISIG OP_IFDUP OP_NOTIF OP_PUSHBYTES_2') === 1259) { if (this.vin.witness.length > 11) { this.label = 'Liquid Peg Out'; } else { diff --git a/frontend/src/app/components/address/address.component.html b/frontend/src/app/components/address/address.component.html index ae2d7ba9c..531b97464 100644 --- a/frontend/src/app/components/address/address.component.html +++ b/frontend/src/app/components/address/address.component.html @@ -49,9 +49,19 @@
-
+ +
+
+
+
+ +
+
+
+
+
diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts index 0e10b207f..614be930c 100644 --- a/frontend/src/app/components/address/address.component.ts +++ b/frontend/src/app/components/address/address.component.ts @@ -31,8 +31,7 @@ export class AddressComponent implements OnInit, OnDestroy { addressLoadingStatus$: Observable; addressInfo: null | AddressInformation = null; - totalConfirmedTxCount = 0; - loadedConfirmedTxCount = 0; + fullyLoaded = false; txCount = 0; received = 0; sent = 0; @@ -45,7 +44,7 @@ export class AddressComponent implements OnInit, OnDestroy { private route: ActivatedRoute, private electrsApiService: ElectrsApiService, private websocketService: WebsocketService, - private stateService: StateService, + public stateService: StateService, private audioService: AudioService, private apiService: ApiService, private seoService: SeoService, @@ -66,7 +65,7 @@ export class AddressComponent implements OnInit, OnDestroy { switchMap((params: ParamMap) => { this.error = undefined; this.isLoadingAddress = true; - this.loadedConfirmedTxCount = 0; + this.fullyLoaded = false; this.address = null; this.isLoadingTransactions = true; this.transactions = null; @@ -105,7 +104,7 @@ export class AddressComponent implements OnInit, OnDestroy { .pipe( filter((address) => !!address), tap((address: Address) => { - if ((this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') && /^([m-zA-HJ-NP-Z1-9]{26,35}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[a-km-zA-HJ-NP-Z1-9]{80})$/.test(address.address)) { + if ((this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') && /^([a-zA-HJ-NP-Z1-9]{26,35}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[a-km-zA-HJ-NP-Z1-9]{80})$/.test(address.address)) { this.apiService.validateAddress$(address.address) .subscribe((addressInfo) => { this.addressInfo = addressInfo; @@ -128,7 +127,6 @@ export class AddressComponent implements OnInit, OnDestroy { this.tempTransactions = transactions; if (transactions.length) { this.lastTransactionTxId = transactions[transactions.length - 1].txid; - this.loadedConfirmedTxCount += transactions.filter((tx) => tx.status.confirmed).length; } const fetchTxs: string[] = []; @@ -142,10 +140,22 @@ export class AddressComponent implements OnInit, OnDestroy { if (!fetchTxs.length) { return of([]); } - return this.apiService.getTransactionTimes$(fetchTxs); + return this.apiService.getTransactionTimes$(fetchTxs).pipe( + catchError((err) => { + this.isLoadingAddress = false; + this.isLoadingTransactions = false; + this.error = err; + this.seoService.logSoft404(); + console.log(err); + return of([]); + }) + ); }) ) - .subscribe((times: number[]) => { + .subscribe((times: number[] | null) => { + if (!times) { + return; + } times.forEach((time, index) => { this.tempTransactions[this.timeTxIndexes[index]].firstSeen = time; }); @@ -191,8 +201,6 @@ export class AddressComponent implements OnInit, OnDestroy { this.audioService.playSound('magic'); } } - this.totalConfirmedTxCount++; - this.loadedConfirmedTxCount++; }); } @@ -252,16 +260,21 @@ export class AddressComponent implements OnInit, OnDestroy { } loadMore() { - if (this.isLoadingTransactions || !this.totalConfirmedTxCount || this.loadedConfirmedTxCount >= this.totalConfirmedTxCount) { + if (this.isLoadingTransactions || this.fullyLoaded) { return; } this.isLoadingTransactions = true; this.retryLoadMore = false; - this.electrsApiService.getAddressTransactions$(this.address.address, this.lastTransactionTxId) + (this.address.is_pubkey + ? this.electrsApiService.getScriptHashTransactions$((this.address.address.length === 66 ? '21' : '41') + this.address.address + 'ac', this.lastTransactionTxId) + : this.electrsApiService.getAddressTransactions$(this.address.address, this.lastTransactionTxId)) .subscribe((transactions: Transaction[]) => { - this.lastTransactionTxId = transactions[transactions.length - 1].txid; - this.loadedConfirmedTxCount += transactions.length; - this.transactions = this.transactions.concat(transactions); + if (transactions && transactions.length) { + this.lastTransactionTxId = transactions[transactions.length - 1].txid; + this.transactions = this.transactions.concat(transactions); + } else { + this.fullyLoaded = true; + } this.isLoadingTransactions = false; }, (error) => { @@ -278,7 +291,6 @@ export class AddressComponent implements OnInit, OnDestroy { this.received = this.address.chain_stats.funded_txo_sum + this.address.mempool_stats.funded_txo_sum; this.sent = this.address.chain_stats.spent_txo_sum + this.address.mempool_stats.spent_txo_sum; this.txCount = this.address.chain_stats.tx_count + this.address.mempool_stats.tx_count; - this.totalConfirmedTxCount = this.address.chain_stats.tx_count; } ngOnDestroy() { diff --git a/frontend/src/app/components/amount/amount.component.html b/frontend/src/app/components/amount/amount.component.html index 29f61ca41..aba600380 100644 --- a/frontend/src/app/components/amount/amount.component.html +++ b/frontend/src/app/components/amount/amount.component.html @@ -8,9 +8,12 @@ }} - {{ addPlus && satoshis >= 0 ? '+' : '' }}{{ (conversions[currency] > -1 ? conversions[currency] : 0) * satoshis / 100000000 | fiatCurrency : digitsInfo : currency }} + {{ addPlus && satoshis >= 0 ? '+' : '' }}{{ (conversions[currency] > -1 ? conversions[currency] : 0) * satoshis / 100000000 | fiatCurrency : digitsInfo : currency }} + + {{ 0 | fiatCurrency : digitsInfo : currency }} + @@ -19,7 +22,7 @@ ‎{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ satoshis / 100000000 | number : digitsInfo }} - L- + L- tL- t sBTC diff --git a/frontend/src/app/components/amount/amount.component.ts b/frontend/src/app/components/amount/amount.component.ts index 479ae4791..9d0337574 100644 --- a/frontend/src/app/components/amount/amount.component.ts +++ b/frontend/src/app/components/amount/amount.component.ts @@ -23,6 +23,8 @@ export class AmountComponent implements OnInit, OnDestroy { @Input() noFiat = false; @Input() addPlus = false; @Input() blockConversion: Price; + @Input() forceBtc: boolean = false; + @Input() forceBlockConversion: boolean = false; // true = displays fiat price as 0 if blockConversion is undefined instead of falling back to conversions constructor( private stateService: StateService, diff --git a/frontend/src/app/components/app/app.component.ts b/frontend/src/app/components/app/app.component.ts index c7ca798ae..ace0122f0 100644 --- a/frontend/src/app/components/app/app.component.ts +++ b/frontend/src/app/components/app/app.component.ts @@ -55,8 +55,6 @@ export class AppComponent implements OnInit { let domain = 'mempool.space'; if (this.stateService.env.BASE_MODULE === 'liquid') { domain = 'liquid.network'; - } else if (this.stateService.env.BASE_MODULE === 'bisq') { - domain = 'bisq.markets'; } this.link.setAttribute('href', 'https://' + domain + this.location.path()); } diff --git a/frontend/src/app/components/asset/asset.component.html b/frontend/src/app/components/asset/asset.component.html index 862055f22..a7f4d3118 100644 --- a/frontend/src/app/components/asset/asset.component.html +++ b/frontend/src/app/components/asset/asset.component.html @@ -146,13 +146,10 @@ -
+ Error loading asset data. -
- {{ error.error }} -
+
-

diff --git a/frontend/src/app/components/assets/assets-featured/assets-featured.component.html b/frontend/src/app/components/assets/assets-featured/assets-featured.component.html index 7893fda2c..52573c437 100644 --- a/frontend/src/app/components/assets/assets-featured/assets-featured.component.html +++ b/frontend/src/app/components/assets/assets-featured/assets-featured.component.html @@ -1,5 +1,7 @@
- +
+
No featured assets
+
diff --git a/frontend/src/app/components/assets/assets-featured/assets-featured.component.scss b/frontend/src/app/components/assets/assets-featured/assets-featured.component.scss index 0ff049d16..32c560af4 100644 --- a/frontend/src/app/components/assets/assets-featured/assets-featured.component.scss +++ b/frontend/src/app/components/assets/assets-featured/assets-featured.component.scss @@ -47,3 +47,9 @@ .ticker { color: grey; } + +.symbol { + color: grey; + font-size: 1.5rem; + font-style: italic; +} \ No newline at end of file diff --git a/frontend/src/app/components/assets/assets-featured/assets-featured.component.ts b/frontend/src/app/components/assets/assets-featured/assets-featured.component.ts index d80cb6636..a9bf305f6 100644 --- a/frontend/src/app/components/assets/assets-featured/assets-featured.component.ts +++ b/frontend/src/app/components/assets/assets-featured/assets-featured.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { ApiService } from '../../../services/api.service'; +import { StateService } from '../../../services/state.service'; @Component({ selector: 'app-assets-featured', @@ -12,10 +13,11 @@ export class AssetsFeaturedComponent implements OnInit { constructor( private apiService: ApiService, + private stateService: StateService, ) { } ngOnInit(): void { - this.featuredAssets$ = this.apiService.listFeaturedAssets$(); + this.featuredAssets$ = this.apiService.listFeaturedAssets$(this.stateService.network); } } diff --git a/frontend/src/app/components/assets/assets.component.html b/frontend/src/app/components/assets/assets.component.html index 51a2f7696..c279af2ab 100644 --- a/frontend/src/app/components/assets/assets.component.html +++ b/frontend/src/app/components/assets/assets.component.html @@ -46,9 +46,7 @@ -
- Error loading assets data. -
- {{ error.error }} -
+ + Error loading assets data. +
diff --git a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html b/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html deleted file mode 100644 index 19c5ab715..000000000 --- a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html +++ /dev/null @@ -1,91 +0,0 @@ -
- -
- -
- -
- - - -
diff --git a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.scss b/frontend/src/app/components/bisq-master-page/bisq-master-page.component.scss deleted file mode 100644 index a7274c165..000000000 --- a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.scss +++ /dev/null @@ -1,172 +0,0 @@ -.sticky-header { - position: sticky; - position: -webkit-sticky; - top: 0; - width: 100%; - z-index: 100; -} - -li.nav-item.active { - background-color: #653b9c; -} - -fa-icon { - font-size: 1.66em; -} - -.navbar { - z-index: 100; - min-height: 64px; -} - -li.nav-item { - margin: auto 5px; - padding-left: 10px; - padding-right: 10px; -} - -@media (max-width: 992px) { - footer > .container-fluid { - padding-bottom: 35px; - } -} - -@media (min-width: 992px) { - .navbar { - padding: 0rem 2rem; - } - fa-icon { - font-size: 1.2em; - } - .dropdown-container { - margin-right: 16px; - } - li.nav-item { - margin: auto 0px; - padding: 10px; - } -} - -.navbar-nav { - background: #212121; - bottom: 0; - box-shadow: 0px 0px 15px 0px #000; - flex-direction: row; - left: 0; - justify-content: center; - position: fixed; - width: 100%; - @media (min-width: 992px) { - background: transparent; - box-shadow: none; - position: relative; - width: auto; - } - a { - font-size: 0.8em; - @media (min-width: 375px) { - font-size: 1em; - } - } -} - - -.navbar-collapse { - flex-basis: auto; - justify-content: flex-end; -} - -@media (min-width: 992px) { - .navbar-collapse { - justify-content: space-between; - } -} - -.navbar-brand { - width: 60%; -} - -@media (min-width: 576px) { - .navbar-brand { - width: 140px; - } -} - -nav { - box-shadow: 0px 0px 15px 0px #000; -} - -.connection-badge { - position: absolute; - top: 13px; - left: 0px; - width: 140px; -} - -.badge { - margin: 0 auto; - display: table; -} - -.mainnet.active { - background-color: #653b9c; -} - -.liquid.active { - background-color: #116761; -} - -.liquidtestnet.active { - background-color: #494a4a; -} - -.testnet.active { - background-color: #1d486f; -} - -.signet.active { - background-color: #6f1d5d; -} - -.dropdown-divider { - border-top: 1px solid #121420; -} - -.dropdown-toggle::after { - vertical-align: 0.1em; -} - -.dropdown-item { - display: flex; - align-items: center; -} - -@media (min-width: 992px) { - .search-form-container { - width: 100%; - max-width: 500px; - padding-left: 15px; - } -} -.navbar-dark .navbar-nav .nav-link { - color: #f1f1f1; -} - -.navbar-brand { - margin-right: 5px; -} - -.current-network-svg { - width: 20px; - height: 20px; - margin-right: 5px; -} - -:host-context(.rtl-layout) { - .current-network-svg { - width: 20px; - height: 20px; - margin-left: 5px; - margin-right: 0px; - } -} \ No newline at end of file diff --git a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.ts b/frontend/src/app/components/bisq-master-page/bisq-master-page.component.ts deleted file mode 100644 index f849998b1..000000000 --- a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Env, StateService } from '../../services/state.service'; -import { Observable } from 'rxjs'; -import { LanguageService } from '../../services/language.service'; -import { EnterpriseService } from '../../services/enterprise.service'; -import { NavigationService } from '../../services/navigation.service'; - -@Component({ - selector: 'app-bisq-master-page', - templateUrl: './bisq-master-page.component.html', - styleUrls: ['./bisq-master-page.component.scss'], -}) -export class BisqMasterPageComponent implements OnInit { - connectionState$: Observable; - navCollapsed = false; - env: Env; - isMobile = window.innerWidth <= 767.98; - urlLanguage: string; - networkPaths: { [network: string]: string }; - footerVisible = true; - - constructor( - private stateService: StateService, - private languageService: LanguageService, - private enterpriseService: EnterpriseService, - private navigationService: NavigationService, - ) { } - - ngOnInit() { - this.env = this.stateService.env; - this.connectionState$ = this.stateService.connectionState$; - this.urlLanguage = this.languageService.getLanguageForUrl(); - this.navigationService.subnetPaths.subscribe((paths) => { - this.networkPaths = paths; - if (paths.mainnet.indexOf('docs') > -1) { - this.footerVisible = false; - } else { - this.footerVisible = true; - } - }); - } - - collapse(): void { - this.navCollapsed = !this.navCollapsed; - } - - onResize(event: any) { - this.isMobile = window.innerWidth <= 767.98; - } -} diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.html b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.html index 8676b8a44..9c27ecb83 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.html +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.html @@ -62,10 +62,10 @@
-
-
+
diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts index 0283b2d00..0abc55aa7 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core'; -import { EChartsOption, graphic } from 'echarts'; +import { echarts, EChartsOption } from '../../graphs/echarts'; import { Observable, combineLatest, of } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; @@ -55,7 +55,7 @@ export class BlockFeeRatesGraphComponent implements OnInit { private formBuilder: UntypedFormBuilder, private storageService: StorageService, private miningService: MiningService, - private stateService: StateService, + public stateService: StateService, private router: Router, private zone: NgZone, private route: ActivatedRoute, @@ -209,7 +209,7 @@ export class BlockFeeRatesGraphComponent implements OnInit { prepareChartOptions(data, weightMode) { this.chartOptions = { - color: this.widget ? ['#6b6b6b', new graphic.LinearGradient(0, 0, 0, 0.65, [ + color: this.widget ? ['#6b6b6b', new echarts.graphic.LinearGradient(0, 0, 0, 0.65, [ { offset: 0, color: '#F4511E' }, { offset: 0.25, color: '#FB8C00' }, { offset: 0.5, color: '#FFB300' }, @@ -282,7 +282,7 @@ export class BlockFeeRatesGraphComponent implements OnInit { legend: (this.widget || data.series.length === 0) ? undefined : { padding: [10, 75], data: data.legends, - selected: JSON.parse(this.storageService.getValue('fee_rates_legend')) ?? { + selected: JSON.parse(this.storageService.getValue('fee_rates_legend') || 'null') ?? { 'Min': true, '10th': true, '25th': true, diff --git a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.html b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.html index 76071be96..4fcbc3595 100644 --- a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.html +++ b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.html @@ -36,10 +36,10 @@
-
-
+
diff --git a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts index fc71fb575..895a6f33f 100644 --- a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts +++ b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts @@ -12,6 +12,7 @@ import { MiningService } from '../../services/mining.service'; import { ActivatedRoute } from '@angular/router'; import { FiatShortenerPipe } from '../../shared/pipes/fiat-shortener.pipe'; import { FiatCurrencyPipe } from '../../shared/pipes/fiat-currency.pipe'; +import { StateService } from '../../services/state.service'; @Component({ selector: 'app-block-fees-graph', @@ -54,6 +55,7 @@ export class BlockFeesGraphComponent implements OnInit { private formBuilder: UntypedFormBuilder, private storageService: StorageService, private miningService: MiningService, + public stateService: StateService, private route: ActivatedRoute, private fiatShortenerPipe: FiatShortenerPipe, private fiatCurrencyPipe: FiatCurrencyPipe, diff --git a/frontend/src/app/components/block-filters/block-filters.component.html b/frontend/src/app/components/block-filters/block-filters.component.html index f60b04cdd..8c79cd438 100644 --- a/frontend/src/app/components/block-filters/block-filters.component.html +++ b/frontend/src/app/components/block-filters/block-filters.component.html @@ -1,4 +1,4 @@ -
+
Match
+
+ + +
{{ group.label }}
diff --git a/frontend/src/app/components/block-filters/block-filters.component.scss b/frontend/src/app/components/block-filters/block-filters.component.scss index 6406a1d93..1009efd72 100644 --- a/frontend/src/app/components/block-filters/block-filters.component.scss +++ b/frontend/src/app/components/block-filters/block-filters.component.scss @@ -77,6 +77,49 @@ } } + &.any-mode { + .filter-tag { + border: solid 1px #1a9436; + &.selected { + background-color: #1a9436; + } + } + } + + .btn-group { + font-size: 0.9em; + margin-right: 0.25em; + } + + .mode-toggle { + padding: 0.2em 0.5em; + pointer-events: all; + line-height: 1.5; + background: #181b2daf; + + &:first-child { + border-top-left-radius: 0.2rem; + border-bottom-left-radius: 0.2rem; + } + &:last-child { + border-top-right-radius: 0.2rem; + border-bottom-right-radius: 0.2rem; + } + + &.blue { + border: solid 1px #105fb0; + &.active { + background: #105fb0; + } + } + &.green { + border: solid 1px #1a9436; + &.active { + background: #1a9436; + } + } + } + :host-context(.block-overview-graph:hover) &, &:hover, &:active { .menu-toggle { opacity: 0.5; @@ -132,6 +175,11 @@ .filter-tag { font-size: 0.7em; } + .mode-toggle { + font-size: 0.7em; + margin-bottom: 5px; + margin-top: 2px; + } } &.tiny { diff --git a/frontend/src/app/components/block-filters/block-filters.component.ts b/frontend/src/app/components/block-filters/block-filters.component.ts index 9951984df..a16475c23 100644 --- a/frontend/src/app/components/block-filters/block-filters.component.ts +++ b/frontend/src/app/components/block-filters/block-filters.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Output, HostListener, Input, ChangeDetectorRef, OnChanges, SimpleChanges, OnInit, OnDestroy } from '@angular/core'; -import { FilterGroups, TransactionFilters } from '../../shared/filters.utils'; +import { ActiveFilter, FilterGroups, FilterMode, TransactionFilters } from '../../shared/filters.utils'; import { StateService } from '../../services/state.service'; import { Subscription } from 'rxjs'; @@ -12,7 +12,7 @@ import { Subscription } from 'rxjs'; export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { @Input() cssWidth: number = 800; @Input() excludeFilters: string[] = []; - @Output() onFilterChanged: EventEmitter = new EventEmitter(); + @Output() onFilterChanged: EventEmitter = new EventEmitter(); filterSubscription: Subscription; @@ -21,6 +21,7 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { disabledFilters: { [key: string]: boolean } = {}; activeFilters: string[] = []; filterFlags: { [key: string]: boolean } = {}; + filterMode: FilterMode = 'and'; menuOpen: boolean = false; constructor( @@ -29,15 +30,16 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { ) {} ngOnInit(): void { - this.filterSubscription = this.stateService.activeGoggles$.subscribe((activeFilters: string[]) => { + this.filterSubscription = this.stateService.activeGoggles$.subscribe((active: ActiveFilter) => { + this.filterMode = active.mode; for (const key of Object.keys(this.filterFlags)) { this.filterFlags[key] = false; } - for (const key of activeFilters) { + for (const key of active.filters) { this.filterFlags[key] = !this.disabledFilters[key]; } - this.activeFilters = [...activeFilters.filter(key => !this.disabledFilters[key])]; - this.onFilterChanged.emit(this.getBooleanFlags()); + this.activeFilters = [...active.filters.filter(key => !this.disabledFilters[key])]; + this.onFilterChanged.emit({ mode: active.mode, filters: this.activeFilters }); }); } @@ -53,6 +55,12 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { } } + setFilterMode(mode): void { + this.filterMode = mode; + this.onFilterChanged.emit({ mode: this.filterMode, filters: this.activeFilters }); + this.stateService.activeGoggles$.next({ mode: this.filterMode, filters: [...this.activeFilters] }); + } + toggleFilter(key): void { const filter = this.filters[key]; this.filterFlags[key] = !this.filterFlags[key]; @@ -73,8 +81,8 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { this.activeFilters = this.activeFilters.filter(f => f != key); } const booleanFlags = this.getBooleanFlags(); - this.onFilterChanged.emit(booleanFlags); - this.stateService.activeGoggles$.next([...this.activeFilters]); + this.onFilterChanged.emit({ mode: this.filterMode, filters: this.activeFilters }); + this.stateService.activeGoggles$.next({ mode: this.filterMode, filters: [...this.activeFilters] }); } getBooleanFlags(): bigint | null { @@ -90,7 +98,7 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { @HostListener('document:click', ['$event']) onClick(event): boolean { // click away from menu - if (!event.target.closest('button')) { + if (!event.target.closest('button') && !event.target.closest('label')) { this.menuOpen = false; } return true; diff --git a/frontend/src/app/components/block-health-graph/block-health-graph.component.html b/frontend/src/app/components/block-health-graph/block-health-graph.component.html index 4cd10f2dd..cfbee79b6 100644 --- a/frontend/src/app/components/block-health-graph/block-health-graph.component.html +++ b/frontend/src/app/components/block-health-graph/block-health-graph.component.html @@ -45,10 +45,10 @@
-
-
+
diff --git a/frontend/src/app/components/block-health-graph/block-health-graph.component.ts b/frontend/src/app/components/block-health-graph/block-health-graph.component.ts index 4fea6f245..1e105d5e7 100644 --- a/frontend/src/app/components/block-health-graph/block-health-graph.component.ts +++ b/frontend/src/app/components/block-health-graph/block-health-graph.component.ts @@ -52,7 +52,7 @@ export class BlockHealthGraphComponent implements OnInit { private storageService: StorageService, private zone: NgZone, private route: ActivatedRoute, - private stateService: StateService, + public stateService: StateService, private router: Router, ) { this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' }); diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html index 9d27d8d90..2ef07d12c 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html @@ -1,9 +1,9 @@ -
+
- +
-
+
not available
- + +
+ Your browser does not support this feature. +
diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.scss b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.scss index d30dd3305..c468cf792 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.scss +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.scss @@ -7,11 +7,27 @@ justify-content: center; align-items: center; grid-column: 1/-1; + + .placeholder { + display: flex; + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + height: 100%; + width: 100%; + align-items: center; + justify-content: center; + } +} + +.graph-alignment { + position: relative; + width: 100%; } .grid-align { - position: relative; - width: 100%; display: grid; grid-template-columns: repeat(auto-fit, 75px); justify-content: center; @@ -47,6 +63,5 @@ &.hidden { opacity: 0; - transition: opacity 500ms; } } diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts index d6000e27b..003531fce 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts @@ -1,5 +1,5 @@ import { Component, ElementRef, ViewChild, HostListener, Input, Output, EventEmitter, NgZone, AfterViewInit, OnDestroy, OnChanges } from '@angular/core'; -import { TransactionStripped } from '../../interfaces/websocket.interface'; +import { TransactionStripped } from '../../interfaces/node-api.interface'; import { FastVertexArray } from './fast-vertex-array'; import BlockScene from './block-scene'; import TxSprite from './tx-sprite'; @@ -9,6 +9,8 @@ import { Price } from '../../services/price.service'; import { StateService } from '../../services/state.service'; import { Subscription } from 'rxjs'; import { defaultColorFunction, setOpacity, defaultFeeColors, defaultAuditFeeColors, defaultMarginalFeeColors, defaultAuditColors } from './utils'; +import { ActiveFilter, FilterMode, toFlags } from '../../shared/filters.utils'; +import { detectWebGL } from '../../shared/graphs.utils'; const unmatchedOpacity = 0.2; const unmatchedFeeColors = defaultFeeColors.map(c => setOpacity(c, unmatchedOpacity)); @@ -18,7 +20,7 @@ const unmatchedAuditColors = { censored: setOpacity(defaultAuditColors.censored, unmatchedOpacity), missing: setOpacity(defaultAuditColors.missing, unmatchedOpacity), added: setOpacity(defaultAuditColors.added, unmatchedOpacity), - selected: setOpacity(defaultAuditColors.selected, unmatchedOpacity), + prioritized: setOpacity(defaultAuditColors.prioritized, unmatchedOpacity), accelerated: setOpacity(defaultAuditColors.accelerated, unmatchedOpacity), }; @@ -30,6 +32,7 @@ const unmatchedAuditColors = { export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, OnChanges { @Input() isLoading: boolean; @Input() resolution: number; + @Input() autofit: boolean = false; @Input() blockLimit: number; @Input() orientation = 'left'; @Input() flip = true; @@ -42,6 +45,8 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On @Input() showFilters: boolean = false; @Input() excludeFilters: string[] = []; @Input() filterFlags: bigint | null = null; + @Input() filterMode: FilterMode = 'and'; + @Input() relativeTime: number | null; @Input() blockConversion: Price; @Input() overrideColors: ((tx: TxView) => Color) | null = null; @Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>(); @@ -75,11 +80,14 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On filtersAvailable: boolean = true; activeFilterFlags: bigint | null = null; + webGlEnabled = true; + constructor( readonly ngZone: NgZone, readonly elRef: ElementRef, - private stateService: StateService, + public stateService: StateService, ) { + this.webGlEnabled = this.stateService.isBrowser && detectWebGL(); this.vertexArray = new FastVertexArray(512, TxSprite.dataSize); this.searchSubscription = this.stateService.searchText$.subscribe((text) => { this.searchText = text; @@ -88,13 +96,15 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On } ngAfterViewInit(): void { - this.canvas.nativeElement.addEventListener('webglcontextlost', this.handleContextLost, false); - this.canvas.nativeElement.addEventListener('webglcontextrestored', this.handleContextRestored, false); - this.gl = this.canvas.nativeElement.getContext('webgl'); + if (this.canvas) { + this.canvas.nativeElement.addEventListener('webglcontextlost', this.handleContextLost, false); + this.canvas.nativeElement.addEventListener('webglcontextrestored', this.handleContextRestored, false); + this.gl = this.canvas.nativeElement.getContext('webgl'); - if (this.gl) { - this.initCanvas(); - this.resizeCanvas(); + if (this.gl) { + this.initCanvas(); + this.resizeCanvas(); + } } } @@ -113,16 +123,17 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On if (changes.overrideColor && this.scene) { this.scene.setColorFunction(this.overrideColors); } - if ((changes.filterFlags || changes.showFilters)) { + if ((changes.filterFlags || changes.showFilters || changes.filterMode)) { this.setFilterFlags(); } } - setFilterFlags(flags?: bigint | null): void { - this.activeFilterFlags = this.filterFlags || flags || null; + setFilterFlags(goggle?: ActiveFilter): void { + this.filterMode = goggle?.mode || this.filterMode; + this.activeFilterFlags = goggle?.filters ? toFlags(goggle.filters) : this.filterFlags; if (this.scene) { - if (flags != null) { - this.scene.setColorFunction(this.getFilterColorFunction(flags)); + if (this.activeFilterFlags != null && this.filtersAvailable) { + this.scene.setColorFunction(this.getFilterColorFunction(this.activeFilterFlags)); } else { this.scene.setColorFunction(this.overrideColors); } @@ -135,8 +146,10 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On cancelAnimationFrame(this.animationFrameRequest); clearTimeout(this.animationHeartBeat); } - this.canvas.nativeElement.removeEventListener('webglcontextlost', this.handleContextLost); - this.canvas.nativeElement.removeEventListener('webglcontextrestored', this.handleContextRestored); + if (this.canvas) { + this.canvas.nativeElement.removeEventListener('webglcontextlost', this.handleContextLost); + this.canvas.nativeElement.removeEventListener('webglcontextrestored', this.handleContextRestored); + } } clear(direction): void { @@ -156,7 +169,11 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On // initialize the scene without any entry transition setup(transactions: TransactionStripped[]): void { - this.filtersAvailable = transactions.reduce((flagSet, tx) => flagSet || tx.flags > 0, false); + const filtersAvailable = transactions.reduce((flagSet, tx) => flagSet || tx.flags > 0, false); + if (filtersAvailable !== this.filtersAvailable) { + this.setFilterFlags(); + } + this.filtersAvailable = filtersAvailable; if (this.scene) { this.scene.setup(transactions); this.readyNextFrame = true; @@ -191,6 +208,10 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On update(add: TransactionStripped[], remove: string[], change: { txid: string, rate: number | undefined, acc: boolean | undefined }[], direction: string = 'left', resetLayout: boolean = false): void { if (this.scene) { + add = add.filter(tx => !this.scene.txs[tx.txid]); + remove = remove.filter(txid => this.scene.txs[txid]); + change = change.filter(tx => this.scene.txs[tx.txid]); + this.scene.update(add, remove, change, direction, resetLayout); this.start(); this.updateSearchHighlight(); @@ -198,6 +219,10 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On } initCanvas(): void { + if (!this.canvas || !this.gl) { + return; + } + this.gl.clearColor(0.0, 0.0, 0.0, 0.0); this.gl.clear(this.gl.COLOR_BUFFER_BIT); @@ -251,24 +276,26 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On @HostListener('window:resize', ['$event']) resizeCanvas(): void { - this.cssWidth = this.canvas.nativeElement.offsetParent.clientWidth; - this.cssHeight = this.canvas.nativeElement.offsetParent.clientHeight; - this.displayWidth = window.devicePixelRatio * this.cssWidth; - this.displayHeight = window.devicePixelRatio * this.cssHeight; - this.canvas.nativeElement.width = this.displayWidth; - this.canvas.nativeElement.height = this.displayHeight; - if (this.gl) { - this.gl.viewport(0, 0, this.displayWidth, this.displayHeight); - } - if (this.scene) { - this.scene.resize({ width: this.displayWidth, height: this.displayHeight, animate: false }); - this.start(); - } else { - this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: this.resolution, - blockLimit: this.blockLimit, orientation: this.orientation, flip: this.flip, vertexArray: this.vertexArray, - highlighting: this.auditHighlighting, animationDuration: this.animationDuration, animationOffset: this.animationOffset, + if (this.canvas) { + this.cssWidth = this.canvas.nativeElement.offsetParent.clientWidth; + this.cssHeight = this.canvas.nativeElement.offsetParent.clientHeight; + this.displayWidth = window.devicePixelRatio * this.cssWidth; + this.displayHeight = window.devicePixelRatio * this.cssHeight; + this.canvas.nativeElement.width = this.displayWidth; + this.canvas.nativeElement.height = this.displayHeight; + if (this.gl) { + this.gl.viewport(0, 0, this.displayWidth, this.displayHeight); + } + if (this.scene) { + this.scene.resize({ width: this.displayWidth, height: this.displayHeight, animate: false }); + this.start(); + } else { + this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: this.resolution, + blockLimit: this.blockLimit, orientation: this.orientation, flip: this.flip, vertexArray: this.vertexArray, + highlighting: this.auditHighlighting, animationDuration: this.animationDuration, animationOffset: this.animationOffset, colorFunction: this.getColorFunction() }); - this.start(); + this.start(); + } } } @@ -395,6 +422,9 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On @HostListener('pointerup', ['$event']) onClick(event) { + if (!this.canvas) { + return; + } if (event.target === this.canvas.nativeElement && event.pointerType === 'touch') { this.setPreviewTx(event.offsetX, event.offsetY, true); } else if (event.target === this.canvas.nativeElement) { @@ -406,6 +436,9 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On @HostListener('pointermove', ['$event']) onPointerMove(event) { + if (!this.canvas) { + return; + } if (event.target === this.canvas.nativeElement) { this.setPreviewTx(event.offsetX, event.offsetY, false); } else { @@ -499,11 +532,13 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On } onTxClick(cssX: number, cssY: number, keyModifier: boolean = false) { - const x = cssX * window.devicePixelRatio; - const y = cssY * window.devicePixelRatio; - const selected = this.scene.getTxAt({ x, y }); - if (selected && selected.txid) { - this.txClickEvent.emit({ tx: selected, keyModifier }); + if (this.scene) { + const x = cssX * window.devicePixelRatio; + const y = cssY * window.devicePixelRatio; + const selected = this.scene.getTxAt({ x, y }); + if (selected && selected.txid) { + this.txClickEvent.emit({ tx: selected, keyModifier }); + } } } @@ -523,7 +558,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On getFilterColorFunction(flags: bigint): ((tx: TxView) => Color) { return (tx: TxView) => { - if ((tx.bigintFlags & flags) === flags) { + if ((this.filterMode === 'and' && (tx.bigintFlags & flags) === flags) || (this.filterMode === 'or' && (flags === 0n || (tx.bigintFlags & flags) > 0n))) { return defaultColorFunction(tx); } else { return defaultColorFunction( diff --git a/frontend/src/app/components/block-overview-graph/block-scene.ts b/frontend/src/app/components/block-overview-graph/block-scene.ts index adcf736fc..5d2196f1e 100644 --- a/frontend/src/app/components/block-overview-graph/block-scene.ts +++ b/frontend/src/app/components/block-overview-graph/block-scene.ts @@ -1,6 +1,6 @@ import { FastVertexArray } from './fast-vertex-array'; import TxView from './tx-view'; -import { TransactionStripped } from '../../interfaces/websocket.interface'; +import { TransactionStripped } from '../../interfaces/node-api.interface'; import { Color, Position, Square, ViewUpdateParams } from './sprite-types'; import { defaultColorFunction } from './utils'; diff --git a/frontend/src/app/components/block-overview-graph/tx-view.ts b/frontend/src/app/components/block-overview-graph/tx-view.ts index da36b9880..742c305f5 100644 --- a/frontend/src/app/components/block-overview-graph/tx-view.ts +++ b/frontend/src/app/components/block-overview-graph/tx-view.ts @@ -4,6 +4,7 @@ import { SpriteUpdateParams, Square, Color, ViewUpdateParams } from './sprite-ty import { hexToColor } from './utils'; import BlockScene from './block-scene'; import { TransactionStripped } from '../../interfaces/node-api.interface'; +import { TransactionFlags } from '../../shared/filters.utils'; const hoverTransitionTime = 300; const defaultHoverColor = hexToColor('1bd8f4'); @@ -29,8 +30,10 @@ export default class TxView implements TransactionStripped { feerate: number; acc?: boolean; rate?: number; + flags: number; bigintFlags?: bigint | null = 0b00000100_00000000_00000000_00000000n; - status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; + time?: number; + status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'prioritized' | 'censored' | 'selected' | 'rbf' | 'accelerated'; context?: 'projected' | 'actual'; scene?: BlockScene; @@ -51,6 +54,7 @@ export default class TxView implements TransactionStripped { this.scene = scene; this.context = tx.context; this.txid = tx.txid; + this.time = tx.time || 0; this.fee = tx.fee; this.vsize = tx.vsize; this.value = tx.value; @@ -58,7 +62,8 @@ export default class TxView implements TransactionStripped { this.acc = tx.acc; this.rate = tx.rate; this.status = tx.status; - this.bigintFlags = tx.flags ? BigInt(tx.flags) : 0n; + this.flags = tx.flags || 0; + this.bigintFlags = tx.flags ? (BigInt(tx.flags) | (this.acc ? TransactionFlags.acceleration : 0n)): 0n; this.initialised = false; this.vertexArray = scene.vertexArray; diff --git a/frontend/src/app/components/block-overview-graph/utils.ts b/frontend/src/app/components/block-overview-graph/utils.ts index 9c800ad85..b6c8ccf5e 100644 --- a/frontend/src/app/components/block-overview-graph/utils.ts +++ b/frontend/src/app/components/block-overview-graph/utils.ts @@ -45,7 +45,7 @@ export const defaultAuditColors = { censored: hexToColor('f344df'), missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7), added: hexToColor('0099ff'), - selected: darken(desaturate(hexToColor('0099ff'), 0.3), 0.7), + prioritized: darken(desaturate(hexToColor('0099ff'), 0.3), 0.7), accelerated: hexToColor('8F5FF6'), }; @@ -81,6 +81,8 @@ export function defaultColorFunction( return auditColors.missing; case 'added': return auditColors.added; + case 'prioritized': + return auditColors.prioritized; case 'selected': return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1]; case 'accelerated': diff --git a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.html b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.html index 9e3e94111..20b7fc8c4 100644 --- a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.html +++ b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.html @@ -6,61 +6,91 @@ [style.left]="tooltipPosition.x + 'px'" [style.top]="tooltipPosition.y + 'px'" > - +
- - + - - - + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - - - - + + + - - + + - - + + - - - - - - - - - - - - - + + - - - + {{ activeFilters.rbf }} + +
Transaction + Transaction {{ txid | shortenString : 16}}
Amount
First seenFirst seenFirst seenConfirmed
Fee{{ fee | number }} sat   Amount
Fee rate + Fee{{ fee | number }} sat   +
Fee rate
Effective fee rateAccelerated fee rate +
Effective fee rateAccelerated fee rate
Virtual sizeVirtual size
WeightWeight
Audit statusMatchRemovedMarginal fee rateHigh sigop countRecently broadcastedRecently CPFP'dAddedMarginal fee rateConflictingAcceleratedAudit status + + Match + Removed + Marginal fee rate + High sigop count + Recently broadcasted + Recently CPFP'd + Added + Prioritized + Marginal fee rate + Conflicting + Accelerated + +
Accelerated
+
+ Accelerated + + {{ filter.label }} + +
+
diff --git a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.scss b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.scss index 9ad50fc30..51edee1e8 100644 --- a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.scss +++ b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.scss @@ -9,16 +9,24 @@ justify-content: space-between; padding: 10px 15px; text-align: left; - min-width: 320px; + min-width: 340px; + max-width: 340px; pointer-events: none; + z-index: 11; &.clickable { pointer-events: all; } } -.td-width { - padding-right: 10px; +th, td { + &.label { + width: 30%; + } + &.value { + width: 70%; + text-align: end; + } } .badge.badge-accelerated { @@ -28,6 +36,40 @@ animation: acceleratePulse 1s infinite; } +.tags { + display: flex; + flex-wrap: wrap; + row-gap: 0.25em; + margin-top: 0.2em; + max-width: 100%; + + .badge { + border-radius: 0.2rem; + padding: 0.2em 0.5em; + margin-right: 0.25em; + } + + .filter-tag { + background: #181b2daf; + border: solid 1px #105fb0; + color: white; + transition: background-color 300ms; + + &.matching { + background-color: #105fb0; + } + } + + &.any-mode { + .filter-tag { + border: solid 1px #1a9436; + &.matching { + background-color: #1a9436; + } + } + } +} + @keyframes acceleratePulse { 0% { background-color: #653b9c; box-shadow: #ad7de57f 0px 0px 12px -2px; } 50% { background-color: #8457bb; box-shadow: #ad7de5 0px 0px 18px -2px;} diff --git a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts index a6e2a2697..cdb4c7367 100644 --- a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts +++ b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts @@ -1,7 +1,9 @@ -import { Component, ElementRef, ViewChild, Input, OnChanges, ChangeDetectionStrategy } from '@angular/core'; +import { Component, ElementRef, ViewChild, Input, OnChanges, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; import { Position } from '../../components/block-overview-graph/sprite-types.js'; import { Price } from '../../services/price.service'; import { TransactionStripped } from '../../interfaces/node-api.interface.js'; +import { Filter, FilterMode, TransactionFlags, toFilters } from '../../shared/filters.utils'; +import { Block } from '../../interfaces/electrs.interface.js'; @Component({ selector: 'app-block-overview-tooltip', @@ -10,24 +12,34 @@ import { TransactionStripped } from '../../interfaces/node-api.interface.js'; }) export class BlockOverviewTooltipComponent implements OnChanges { @Input() tx: TransactionStripped | void; + @Input() relativeTime?: number; @Input() cursorPosition: Position; @Input() clickable: boolean; @Input() auditEnabled: boolean = false; @Input() blockConversion: Price; + @Input() filterFlags: bigint | null = null; + @Input() filterMode: FilterMode = 'and'; txid = ''; + time: number = 0; fee = 0; value = 0; vsize = 1; feeRate = 0; effectiveRate; acceleration; + hasEffectiveRate: boolean = false; + timeMode: 'mempool' | 'mined' | 'missed' | 'after' = 'mempool'; + filters: Filter[] = []; + activeFilters: { [key: string]: boolean } = {}; tooltipPosition: Position = { x: 0, y: 0 }; @ViewChild('tooltip') tooltipElement: ElementRef; - constructor() {} + constructor( + private cd: ChangeDetectorRef, + ) {} ngOnChanges(changes): void { if (changes.cursorPosition && changes.cursorPosition.currentValue) { @@ -46,15 +58,42 @@ export class BlockOverviewTooltipComponent implements OnChanges { this.tooltipPosition = { x, y }; } - if (changes.tx) { - const tx = changes.tx.currentValue || {}; - this.txid = tx.txid || ''; - this.fee = tx.fee || 0; - this.value = tx.value || 0; - this.vsize = tx.vsize || 1; + if (this.tx && (changes.tx || changes.filterFlags || changes.filterMode)) { + this.txid = this.tx.txid || ''; + this.time = this.tx.time || 0; + this.fee = this.tx.fee || 0; + this.value = this.tx.value || 0; + this.vsize = this.tx.vsize || 1; this.feeRate = this.fee / this.vsize; - this.effectiveRate = tx.rate; - this.acceleration = tx.acc; + this.effectiveRate = this.tx.rate; + const txFlags = BigInt(this.tx.flags) || 0n; + this.acceleration = this.tx.acc || (txFlags & TransactionFlags.acceleration); + this.hasEffectiveRate = Math.abs((this.fee / this.vsize) - this.effectiveRate) > 0.05 + || (txFlags && (txFlags & (TransactionFlags.cpfp_child | TransactionFlags.cpfp_parent)) > 0n); + this.filters = this.tx.flags ? toFilters(txFlags).filter(f => f.tooltip) : []; + this.activeFilters = {} + for (const filter of this.filters) { + if (this.filterFlags && (this.filterFlags & BigInt(filter.flag))) { + this.activeFilters[filter.key] = true; + } + } + + if (!this.relativeTime) { + this.timeMode = 'mempool'; + } else { + if (this.tx?.context === 'actual' || this.tx?.status === 'found') { + this.timeMode = 'mined'; + } else { + const time = this.relativeTime || Date.now(); + if (this.time <= time) { + this.timeMode = 'missed'; + } else { + this.timeMode = 'after'; + } + } + } + + this.cd.markForCheck(); } } } diff --git a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.html b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.html index 198153583..10e6d304d 100644 --- a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.html +++ b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.html @@ -37,10 +37,10 @@
-
-
+
diff --git a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts index 9c987fb57..fe8efa9c7 100644 --- a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts +++ b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts @@ -12,6 +12,7 @@ import { StorageService } from '../../services/storage.service'; import { ActivatedRoute } from '@angular/router'; import { FiatShortenerPipe } from '../../shared/pipes/fiat-shortener.pipe'; import { FiatCurrencyPipe } from '../../shared/pipes/fiat-currency.pipe'; +import { StateService } from '../../services/state.service'; @Component({ selector: 'app-block-rewards-graph', @@ -54,6 +55,7 @@ export class BlockRewardsGraphComponent implements OnInit { private formBuilder: UntypedFormBuilder, private miningService: MiningService, private storageService: StorageService, + public stateService: StateService, private route: ActivatedRoute, private fiatShortenerPipe: FiatShortenerPipe, private fiatCurrencyPipe: FiatCurrencyPipe, diff --git a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.html b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.html index 122b5e7ca..c21a4bebb 100644 --- a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.html +++ b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.html @@ -44,10 +44,10 @@
-
-
+
diff --git a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts index 5d01e48e9..76d6e8216 100644 --- a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts +++ b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts @@ -10,6 +10,7 @@ import { StorageService } from '../../services/storage.service'; import { MiningService } from '../../services/mining.service'; import { ActivatedRoute } from '@angular/router'; import { download, formatterXAxis } from '../../shared/graphs.utils'; +import { StateService } from '../../services/state.service'; @Component({ selector: 'app-block-sizes-weights-graph', @@ -52,6 +53,7 @@ export class BlockSizesWeightsGraphComponent implements OnInit { private formBuilder: UntypedFormBuilder, private storageService: StorageService, private miningService: MiningService, + public stateService: StateService, private route: ActivatedRoute, ) { } diff --git a/frontend/src/app/components/block-view/block-view.component.html b/frontend/src/app/components/block-view/block-view.component.html index 9a2ddf373..f0dc94e2c 100644 --- a/frontend/src/app/components/block-view/block-view.component.html +++ b/frontend/src/app/components/block-view/block-view.component.html @@ -8,6 +8,7 @@ [orientation]="'top'" [flip]="false" [disableSpinner]="true" + [relativeTime]="block?.timestamp" (txClickEvent)="onTxClick($event)" >
diff --git a/frontend/src/app/components/block/block-preview.component.html b/frontend/src/app/components/block/block-preview.component.html index 0c33246a7..02fe62774 100644 --- a/frontend/src/app/components/block/block-preview.component.html +++ b/frontend/src/app/components/block/block-preview.component.html @@ -70,8 +70,9 @@
819500 ? this.apiService.getAccelerationHistory$({ blockHash: block.id }) : of([]) + this.stateService.env.ACCELERATOR === true && block.height > 819500 ? this.servicesApiService.getAccelerationHistory$({ blockHash: block.id }) : of([]) ]); } ), diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 89699a68c..0020f56be 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -39,7 +39,7 @@
- + @@ -109,7 +109,7 @@
@@ -126,16 +127,16 @@
- + - + - + - - - - + + + + + +
Hash {{ block.id | shortenString : 13 }}
Fee span - -
Median fee~ + ~ - @@ -226,13 +227,13 @@ fragment="actual" (click)="changeMode('actual')">Actual
-
+

Expected Block

- + [showFilters]="true" [excludeFilters]="['replacement']" [relativeTime]="block?.timestamp">
@@ -241,13 +242,13 @@
-
+

Actual Block

- + [showFilters]="true" [excludeFilters]="['replacement']" [relativeTime]="block?.timestamp">
@@ -259,7 +260,7 @@
- +

@@ -335,17 +336,15 @@
- + -
-
+
+ Error loading data. -

- {{ transactionsError.status }}: {{ transactionsError.error }} -
-
-
+ +
+
@@ -378,11 +377,9 @@
-
+ Error loading data. -

- {{ error.status }}: {{ error.error }} -
+
@@ -431,8 +428,11 @@
Total fees + + + + + {{ blockAudit.feeDelta < 0 ? '+' : '' }}{{ (-blockAudit.feeDelta * 100) | amountShortener: 2 }}% diff --git a/frontend/src/app/components/block/block.component.scss b/frontend/src/app/components/block/block.component.scss index 6deb2cb4b..bad8fa52e 100644 --- a/frontend/src/app/components/block/block.component.scss +++ b/frontend/src/app/components/block/block.component.scss @@ -70,6 +70,14 @@ } } +.audit-col { + max-width: 50%; + + &.mobile { + max-width: 100%; + } +} + .block-subtitle.actual a { position: absolute; top: -3px; @@ -288,6 +296,10 @@ h1 { @media (max-width: 767.98px) { margin-top: 0.75rem; } + + .oobFees { + color: #905cf4; + } } .graph-col { diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 6a995127b..8fd01340f 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, OnDestroy, ViewChildren, QueryList } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChildren, QueryList, Inject, PLATFORM_ID, ChangeDetectorRef } from '@angular/core'; import { Location } from '@angular/common'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { ElectrsApiService } from '../../services/electrs-api.service'; @@ -9,13 +9,14 @@ import { StateService } from '../../services/state.service'; import { SeoService } from '../../services/seo.service'; import { WebsocketService } from '../../services/websocket.service'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { BlockAudit, BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface'; +import { AccelerationInfo, BlockAudit, BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; import { detectWebGL } from '../../shared/graphs.utils'; import { seoDescriptionNetwork } from '../../shared/common.utils'; import { PriceService, Price } from '../../services/price.service'; import { CacheService } from '../../services/cache.service'; +import { ServicesApiServices } from '../../services/services-api.service'; @Component({ selector: 'app-block', @@ -42,6 +43,7 @@ export class BlockComponent implements OnInit, OnDestroy { latestBlock: BlockExtended; latestBlocks: BlockExtended[] = []; transactions: Transaction[]; + oobFees: number = 0; isLoadingTransactions = true; strippedTransactions: TransactionStripped[]; overviewTransitionDirection: string; @@ -59,6 +61,7 @@ export class BlockComponent implements OnInit, OnDestroy { transactionsError: any = null; overviewError: any = null; webGlEnabled = true; + auditParamEnabled: boolean = false; auditSupported: boolean = this.stateService.env.AUDIT && this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true; auditModeEnabled: boolean = !this.stateService.hideAudit.value; auditAvailable = true; @@ -84,6 +87,7 @@ export class BlockComponent implements OnInit, OnDestroy { timeLtr: boolean; childChangeSubscription: Subscription; auditPrefSubscription: Subscription; + oobSubscription: Subscription; priceSubscription: Subscription; blockConversion: Price; @@ -103,8 +107,11 @@ export class BlockComponent implements OnInit, OnDestroy { private apiService: ApiService, private priceService: PriceService, private cacheService: CacheService, + private servicesApiService: ServicesApiServices, + private cd: ChangeDetectorRef, + @Inject(PLATFORM_ID) private platformId: Object, ) { - this.webGlEnabled = detectWebGL(); + this.webGlEnabled = this.stateService.isBrowser && detectWebGL(); } ngOnInit() { @@ -120,9 +127,15 @@ export class BlockComponent implements OnInit, OnDestroy { this.setAuditAvailable(this.auditSupported); if (this.auditSupported) { - this.auditPrefSubscription = this.stateService.hideAudit.subscribe((hide) => { - this.auditModeEnabled = !hide; - this.showAudit = this.auditAvailable && this.auditModeEnabled; + this.isAuditEnabledFromParam().subscribe(auditParam => { + if (this.auditParamEnabled) { + this.auditModeEnabled = auditParam; + } else { + this.auditPrefSubscription = this.stateService.hideAudit.subscribe(hide => { + this.auditModeEnabled = !hide; + this.showAudit = this.auditAvailable && this.auditModeEnabled; + }); + } }); } @@ -166,6 +179,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.page = 1; this.error = undefined; this.fees = undefined; + this.oobFees = 0; if (history.state.data && history.state.data.blockHeight) { this.blockHeight = history.state.data.blockHeight; @@ -290,8 +304,9 @@ export class BlockComponent implements OnInit, OnDestroy { throttleTime(300, asyncScheduler, { leading: true, trailing: true }), shareReplay(1) ); - this.transactionSubscription = block$.pipe( - switchMap((block) => this.electrsApiService.getBlockTransactions$(block.id) + this.transactionSubscription = combineLatest([block$, this.route.queryParams]).pipe( + tap(([_, queryParams]) => this.page = +queryParams['page'] || 1), + switchMap(([block, _]) => this.electrsApiService.getBlockTransactions$(block.id, (this.page - 1) * this.itemsPerPage) .pipe( catchError((err) => { this.transactionsError = err; @@ -305,6 +320,7 @@ export class BlockComponent implements OnInit, OnDestroy { } this.transactions = transactions; this.isLoadingTransactions = false; + this.cd.markForCheck(); }, (error) => { this.error = error; @@ -329,7 +345,7 @@ export class BlockComponent implements OnInit, OnDestroy { return of(null); }) ), - this.stateService.env.ACCELERATOR === true && block.height > 819500 ? this.apiService.getAccelerationHistory$({ blockHash: block.id }) : of([]) + this.stateService.env.ACCELERATOR === true && block.height > 819500 ? this.servicesApiService.getAccelerationHistory$({ blockHash: block.id }) : of([]) ]); }) ) @@ -355,6 +371,7 @@ export class BlockComponent implements OnInit, OnDestroy { const inTemplate = {}; const inBlock = {}; const isAdded = {}; + const isPrioritized = {}; const isCensored = {}; const isMissing = {}; const isSelected = {}; @@ -378,6 +395,9 @@ export class BlockComponent implements OnInit, OnDestroy { for (const txid of blockAudit.addedTxs) { isAdded[txid] = true; } + for (const txid of blockAudit.prioritizedTxs) { + isPrioritized[txid] = true; + } for (const txid of blockAudit.missingTxs) { isCensored[txid] = true; } @@ -427,6 +447,8 @@ export class BlockComponent implements OnInit, OnDestroy { tx.status = null; } else if (isAdded[tx.txid]) { tx.status = 'added'; + } else if (isPrioritized[tx.txid]) { + tx.status = 'prioritized'; } else if (inTemplate[tx.txid]) { tx.status = 'found'; } else if (isRbf[tx.txid]) { @@ -444,9 +466,9 @@ export class BlockComponent implements OnInit, OnDestroy { inBlock[tx.txid] = true; } - blockAudit.feeDelta = blockAudit.expectedFees > 0 ? (blockAudit.expectedFees - this.block.extras.totalFees) / blockAudit.expectedFees : 0; - blockAudit.weightDelta = blockAudit.expectedWeight > 0 ? (blockAudit.expectedWeight - this.block.weight) / blockAudit.expectedWeight : 0; - blockAudit.txDelta = blockAudit.template.length > 0 ? (blockAudit.template.length - this.block.tx_count) / blockAudit.template.length : 0; + blockAudit.feeDelta = blockAudit.expectedFees > 0 ? (blockAudit.expectedFees - (this.block?.extras.totalFees + this.oobFees)) / blockAudit.expectedFees : 0; + blockAudit.weightDelta = blockAudit.expectedWeight > 0 ? (blockAudit.expectedWeight - this.block?.weight) / blockAudit.expectedWeight : 0; + blockAudit.txDelta = blockAudit.template.length > 0 ? (blockAudit.template.length - this.block?.tx_count) / blockAudit.template.length : 0; this.blockAudit = blockAudit; this.setAuditAvailable(true); } else { @@ -458,6 +480,33 @@ export class BlockComponent implements OnInit, OnDestroy { this.isLoadingOverview = false; this.setupBlockGraphs(); + this.cd.markForCheck(); + }); + + this.oobSubscription = block$.pipe( + switchMap((block) => this.apiService.getAccelerationsByHeight$(block.height) + .pipe( + map(accelerations => { + return { block, accelerations }; + }), + catchError((err) => { + return of({ block, accelerations: [] }); + })) + ), + ).subscribe(({ block, accelerations}) => { + let totalFees = 0; + for (const acc of accelerations) { + totalFees += acc.boost_cost; + } + this.oobFees = totalFees; + if (block && this.block && this.blockAudit && block?.height === this.block?.height) { + this.blockAudit.feeDelta = this.blockAudit.expectedFees > 0 ? (this.blockAudit.expectedFees - (this.block.extras.totalFees + this.oobFees)) / this.blockAudit.expectedFees : 0; + } + }, + (error) => { + this.error = error; + this.isLoadingBlock = false; + this.isLoadingOverview = false; }); this.networkChangedSubscription = this.stateService.networkChanged$ @@ -495,9 +544,9 @@ export class BlockComponent implements OnInit, OnDestroy { if (this.priceSubscription) { this.priceSubscription.unsubscribe(); } - this.priceSubscription = block$.pipe( - switchMap((block) => { - return this.priceService.getBlockPrice$(block.timestamp).pipe( + this.priceSubscription = combineLatest([this.stateService.fiatCurrency$, block$]).pipe( + switchMap(([currency, block]) => { + return this.priceService.getBlockPrice$(block.timestamp, true, currency).pipe( tap((price) => { this.blockConversion = price; }) @@ -527,6 +576,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.unsubscribeNextBlockSubscriptions(); this.childChangeSubscription?.unsubscribe(); this.priceSubscription?.unsubscribe(); + this.oobSubscription?.unsubscribe(); } unsubscribeNextBlockSubscriptions() { @@ -553,19 +603,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.transactions = null; this.transactionsError = null; target.scrollIntoView(); // works for chrome - - this.electrsApiService.getBlockTransactions$(this.block.id, start) - .pipe( - catchError((err) => { - this.transactionsError = err; - return of([]); - }) - ) - .subscribe((transactions) => { - this.transactions = transactions; - this.isLoadingTransactions = false; - target.scrollIntoView(); // works for firefox - }); + this.router.navigate([], { queryParams: { page: page }, queryParamsHandling: 'merge' }); } toggleShowDetails() { @@ -691,6 +729,24 @@ export class BlockComponent implements OnInit, OnDestroy { toggleAuditMode(): void { this.stateService.hideAudit.next(this.auditModeEnabled); + + this.route.queryParams.subscribe(params => { + let queryParams = { ...params }; + delete queryParams['audit']; + + let newUrl = this.router.url.split('?')[0]; + let queryString = new URLSearchParams(queryParams).toString(); + if (queryString) { + newUrl += '?' + queryString; + } + + this.location.replaceState(newUrl); + }); + + this.auditPrefSubscription = this.stateService.hideAudit.subscribe((hide) => { + this.auditModeEnabled = !hide; + this.showAudit = this.auditAvailable && this.auditModeEnabled; + }); } updateAuditAvailableFromBlockHeight(blockHeight: number): void { @@ -699,6 +755,16 @@ export class BlockComponent implements OnInit, OnDestroy { } } + isAuditEnabledFromParam(): Observable { + return this.route.queryParams.pipe( + map(params => { + this.auditParamEnabled = 'audit' in params; + + return this.auditParamEnabled ? !(params['audit'] === 'false') : true; + }) + ); + } + isAuditAvailableFromBlockHeight(blockHeight: number): boolean { if (!this.auditSupported) { return false; @@ -747,4 +813,4 @@ export class BlockComponent implements OnInit, OnDestroy { this.block.canonical = block.id; } } -} +} \ No newline at end of file diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html index 680beb006..ac29524bb 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html @@ -26,7 +26,7 @@
-   +
-   +
-
-
+
+
@@ -56,7 +58,7 @@
-
+
{{ block.extras.pool.name}} diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss index 795e1f4df..c1cc6809d 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss @@ -166,7 +166,7 @@ opacity: 1; } .hide { - opacity: 0; + opacity: 0.4; pointer-events : none; } diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts index 0da8ca7b5..5141f4de9 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, Input, OnChanges, SimpleChanges } from '@angular/core'; -import { Observable, Subscription } from 'rxjs'; +import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { StateService } from '../../services/state.service'; import { specialBlocks } from '../../app.constants'; import { BlockExtended } from '../../interfaces/node-api.interface'; @@ -27,6 +27,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { @Input() minimal: boolean = false; @Input() blockWidth: number = 125; @Input() spotlight: number = 0; + @Input() showPools: boolean = true; @Input() getHref?: (index, block) => string = (index, block) => `/block/${block.id}`; specialBlocks = specialBlocks; @@ -44,6 +45,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { markBlockSubscription: Subscription; txConfirmedSubscription: Subscription; loadingBlocks$: Observable; + showMiningInfo$: BehaviorSubject = new BehaviorSubject(false); blockStyles = []; emptyBlockStyles = []; interval: any; @@ -53,7 +55,6 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { arrowLeftPx = 30; blocksFilled = false; arrowTransition = '1s'; - showMiningInfo = false; timeLtrSubscription: Subscription; timeLtr: boolean; @@ -63,7 +64,6 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { gradientColors = { '': ['#9339f4', '#105fb0'], - bisq: ['#9339f4', '#105fb0'], liquid: ['#116761', '#183550'], 'liquidtestnet': ['#494a4a', '#272e46'], testnet: ['#1d486f', '#183550'], @@ -79,8 +79,11 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { } enabledMiningInfoIfNeeded(url) { - this.showMiningInfo = url.includes('/mining') || url.includes('/acceleration'); - this.cd.markForCheck(); // Need to update the view asap + const urlParts = url.split('/'); + const onDashboard = ['','testnet','signet','mining','acceleration'].includes(urlParts[urlParts.length - 1]); + if (onDashboard) { // Only update showMiningInfo if we are on the main, mining or acceleration dashboards + this.stateService.showMiningInfo$.next(url.includes('/mining') || url.includes('/acceleration')); + } } ngOnInit() { @@ -89,6 +92,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { if (['', 'testnet', 'signet'].includes(this.stateService.network)) { this.enabledMiningInfoIfNeeded(this.location.path()); this.location.onUrlChange((url) => this.enabledMiningInfoIfNeeded(url)); + this.showMiningInfo$ = this.stateService.showMiningInfo$; } this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html index 838c7cb4e..d82472492 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -33,7 +33,7 @@
- + - - + diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.ts b/frontend/src/app/components/blocks-list/blocks-list.component.ts index d18f03c68..518fae25b 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -1,11 +1,13 @@ -import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core'; -import { BehaviorSubject, combineLatest, Observable, timer, of } from 'rxjs'; -import { delayWhen, map, retryWhen, scan, switchMap, tap } from 'rxjs/operators'; +import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef, Inject, LOCALE_ID } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { BehaviorSubject, combineLatest, Observable, timer, of, Subscription } from 'rxjs'; +import { debounceTime, delayWhen, filter, map, retryWhen, scan, skip, switchMap, tap, throttleTime } from 'rxjs/operators'; import { BlockExtended } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; import { WebsocketService } from '../../services/websocket.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; import { seoDescriptionNetwork } from '../../shared/common.utils'; @Component({ @@ -24,6 +26,7 @@ export class BlocksList implements OnInit { auditAvailable = false; isLoading = true; fromBlockHeight = undefined; + lastBlockHeightFetched = -1; paginationMaxSize: number; page = 1; lastPage = 1; @@ -32,6 +35,10 @@ export class BlocksList implements OnInit { fromHeightSubject: BehaviorSubject = new BehaviorSubject(this.fromBlockHeight); skeletonLines: number[] = []; lastBlockHeight = -1; + blocksCountInitialized$: BehaviorSubject = new BehaviorSubject(false); + blocksCountInitializedSubscription: Subscription; + keyNavigationSubscription: Subscription; + dir: 'rtl' | 'ltr' = 'ltr'; constructor( private apiService: ApiService, @@ -39,8 +46,15 @@ export class BlocksList implements OnInit { public stateService: StateService, private cd: ChangeDetectorRef, private seoService: SeoService, + private ogService: OpenGraphService, + private route: ActivatedRoute, + private router: Router, + @Inject(LOCALE_ID) private locale: string, ) { this.isMempoolModule = this.stateService.env.BASE_MODULE === 'mempool'; + if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) { + this.dir = 'rtl'; + } } ngOnInit(): void { @@ -50,26 +64,60 @@ export class BlocksList implements OnInit { if (!this.widget) { this.websocketService.want(['blocks']); + this.seoService.setTitle($localize`:@@meta.title.blocks-list:Blocks`); + this.ogService.setManualOgImage('recent-blocks.jpg'); if( this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet' ) { this.seoService.setDescription($localize`:@@meta.description.liquid.blocks:See the most recent Liquid${seoDescriptionNetwork(this.stateService.network)} blocks along with basic stats such as block height, block size, and more.`); } else { this.seoService.setDescription($localize`:@@meta.description.bitcoin.blocks:See the most recent Bitcoin${seoDescriptionNetwork(this.stateService.network)} blocks along with basic stats such as block height, block reward, block size, and more.`); } + + this.blocksCountInitializedSubscription = combineLatest([this.blocksCountInitialized$, this.route.queryParams]).pipe( + filter(([blocksCountInitialized, _]) => blocksCountInitialized), + tap(([_, params]) => { + this.page = +params['page'] || 1; + this.page === 1 ? this.fromHeightSubject.next(undefined) : this.fromHeightSubject.next((this.blocksCount - 1) - (this.page - 1) * 15); + this.cd.markForCheck(); + }) + ).subscribe(); + + this.keyNavigationSubscription = this.stateService.keyNavigation$ + .pipe( + tap((event) => { + this.isLoading = true; + const prevKey = this.dir === 'ltr' ? 'ArrowLeft' : 'ArrowRight'; + const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft'; + if (event.key === prevKey && this.page > 1) { + this.page--; + this.cd.markForCheck(); + } + if (event.key === nextKey && this.page * 15 < this.blocksCount) { + this.page++; + this.cd.markForCheck(); + } + }), + throttleTime(1000, undefined, { leading: true, trailing: true }), + ).subscribe(() => { + this.pageChange(this.page); + }); } this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; - + this.blocks$ = combineLatest([ this.fromHeightSubject.pipe( + filter(fromBlockHeight => fromBlockHeight !== this.lastBlockHeightFetched), switchMap((fromBlockHeight) => { this.isLoading = true; + this.lastBlockHeightFetched = fromBlockHeight; return this.apiService.getBlocks$(this.page === 1 ? undefined : fromBlockHeight) .pipe( tap(blocks => { if (this.blocksCount === undefined) { this.blocksCount = blocks[0].height + 1; + this.blocksCountInitialized$.next(true); } this.isLoading = false; this.lastBlockHeight = Math.max(...blocks.map(o => o.height)); @@ -107,7 +155,7 @@ export class BlocksList implements OnInit { this.lastPage = this.page; return blocks[0]; } - if (blocks[1]) { + if (blocks[1] && blocks[1].length) { this.blocksCount = Math.max(this.blocksCount, blocks[1][0].height) + 1; if (this.isMempoolModule) { // @ts-ignore: Need to add an extra field for the template @@ -131,7 +179,7 @@ export class BlocksList implements OnInit { } pageChange(page: number): void { - this.fromHeightSubject.next((this.blocksCount - 1) - (page - 1) * 15); + this.router.navigate([], { queryParams: { page: page } }); } trackByBlock(index: number, block: BlockExtended): number { @@ -141,4 +189,9 @@ export class BlocksList implements OnInit { isEllipsisActive(e): boolean { return (e.offsetWidth < e.scrollWidth); } + + ngOnDestroy(): void { + this.blocksCountInitializedSubscription?.unsubscribe(); + this.keyNavigationSubscription?.unsubscribe(); + } } diff --git a/frontend/src/app/components/clock-face/clock-face.component.ts b/frontend/src/app/components/clock-face/clock-face.component.ts index 63d87c436..eec0fa98c 100644 --- a/frontend/src/app/components/clock-face/clock-face.component.ts +++ b/frontend/src/app/components/clock-face/clock-face.component.ts @@ -1,6 +1,5 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { Subscription, tap, timer } from 'rxjs'; -import { WebsocketService } from '../../services/websocket.service'; import { StateService } from '../../services/state.service'; @Component({ @@ -33,17 +32,20 @@ export class ClockFaceComponent implements OnInit, OnChanges, OnDestroy { } ngOnInit(): void { - this.timeSubscription = timer(0, 250).pipe( - tap(() => { - this.updateTime(); - }) - ).subscribe(); - this.blocksSubscription = this.stateService.blocks$ - .subscribe((blocks) => { - this.blockTimes = blocks.map(block => [block.height, new Date(block.timestamp * 1000)]); - this.blockTimes = this.blockTimes.sort((a, b) => a[1].getTime() - b[1].getTime()); - this.updateSegments(); - }); + if (this.stateService.isBrowser) { + this.timeSubscription = timer(0, 250).pipe( + tap(() => { + console.log('face tick'); + this.updateTime(); + }) + ).subscribe(); + this.blocksSubscription = this.stateService.blocks$ + .subscribe((blocks) => { + this.blockTimes = blocks.map(block => [block.height, new Date(block.timestamp * 1000)]); + this.blockTimes = this.blockTimes.sort((a, b) => a[1].getTime() - b[1].getTime()); + this.updateSegments(); + }); + } } ngOnChanges(): void { @@ -54,7 +56,9 @@ export class ClockFaceComponent implements OnInit, OnChanges, OnDestroy { } ngOnDestroy(): void { - this.timeSubscription.unsubscribe(); + if (this.timeSubscription) { + this.timeSubscription.unsubscribe(); + } } updateTime(): void { diff --git a/frontend/src/app/components/clock/clock.component.ts b/frontend/src/app/components/clock/clock.component.ts index 7ae38583a..94ff3e810 100644 --- a/frontend/src/app/components/clock/clock.component.ts +++ b/frontend/src/app/components/clock/clock.component.ts @@ -33,7 +33,6 @@ export class ClockComponent implements OnInit { gradientColors = { '': ['#9339f4', '#105fb0'], - bisq: ['#9339f4', '#105fb0'], liquid: ['#116761', '#183550'], 'liquidtestnet': ['#494a4a', '#272e46'], testnet: ['#1d486f', '#183550'], @@ -110,8 +109,8 @@ export class ClockComponent implements OnInit { @HostListener('window:resize', ['$event']) resizeCanvas(): void { - const windowWidth = this.limitWidth || window.innerWidth; - const windowHeight = this.limitHeight || window.innerHeight; + const windowWidth = this.limitWidth || window.innerWidth || 800; + const windowHeight = this.limitHeight || window.innerHeight || 800; this.chainWidth = windowWidth; this.chainHeight = Math.max(60, windowHeight / 8); this.clockSize = Math.min(800, windowWidth, windowHeight - (1.4 * this.chainHeight)); diff --git a/frontend/src/app/components/clockchain/clockchain.component.html b/frontend/src/app/components/clockchain/clockchain.component.html index 2f299cb3b..b1f64cd74 100644 --- a/frontend/src/app/components/clockchain/clockchain.component.html +++ b/frontend/src/app/components/clockchain/clockchain.component.html @@ -16,6 +16,7 @@ [minimal]="true" [count]="blockchainBlocks" [blockWidth]="blockWidth" + [showPools]="false" [spotlight]="mode === 'mined' ? -index - 1 : 0" [getHref]="getMinedUrl" > diff --git a/frontend/src/app/components/difficulty-mining/difficulty-mining.component.html b/frontend/src/app/components/difficulty-mining/difficulty-mining.component.html index 18e4830c7..0865708af 100644 --- a/frontend/src/app/components/difficulty-mining/difficulty-mining.component.html +++ b/frontend/src/app/components/difficulty-mining/difficulty-mining.component.html @@ -14,7 +14,7 @@
Estimate
-
+
@@ -24,9 +24,6 @@ {{ epochData.change | absolute | number: '1.2-2' }} %
- -
-
Previous: @@ -47,20 +44,30 @@
-
Next Halving
-
- - {{ i }} blocks - {{ i }} block +
Next Halving
+
+ {{ timeUntilHalving | date }} +
+ +
+ +
+ +
+
-
+ + + {{ i }} blocks + {{ i }} block + +
diff --git a/frontend/src/app/components/difficulty-mining/difficulty-mining.component.ts b/frontend/src/app/components/difficulty-mining/difficulty-mining.component.ts index c23d7d4b9..c650e2c02 100644 --- a/frontend/src/app/components/difficulty-mining/difficulty-mining.component.ts +++ b/frontend/src/app/components/difficulty-mining/difficulty-mining.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; -import { combineLatest, Observable, timer } from 'rxjs'; -import { map, switchMap } from 'rxjs/operators'; +import { combineLatest, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; import { StateService } from '../../services/state.service'; interface EpochProgress { @@ -15,6 +15,8 @@ interface EpochProgress { previousRetarget: number; blocksUntilHalving: number; timeUntilHalving: number; + timeAvg: number; + adjustedTimeAvg: number; } @Component({ @@ -26,6 +28,9 @@ interface EpochProgress { export class DifficultyMiningComponent implements OnInit { isLoadingWebSocket$: Observable; difficultyEpoch$: Observable; + blocksUntilHalving: number | null = null; + timeUntilHalving = 0; + now = new Date().getTime(); @Input() showProgress = true; @Input() showHalving = false; @@ -64,8 +69,9 @@ export class DifficultyMiningComponent implements OnInit { colorPreviousAdjustments = '#ffffff66'; } - const blocksUntilHalving = 210000 - (maxHeight % 210000); - const timeUntilHalving = new Date().getTime() + (blocksUntilHalving * 600000); + this.blocksUntilHalving = 210000 - (maxHeight % 210000); + this.timeUntilHalving = new Date().getTime() + (this.blocksUntilHalving * 600000); + this.now = new Date().getTime(); const data = { base: `${da.progressPercent.toFixed(2)}%`, @@ -77,8 +83,10 @@ export class DifficultyMiningComponent implements OnInit { newDifficultyHeight: da.nextRetargetHeight, estimatedRetargetDate: da.estimatedRetargetDate, previousRetarget: da.previousRetarget, - blocksUntilHalving, - timeUntilHalving, + blocksUntilHalving: this.blocksUntilHalving, + timeUntilHalving: this.timeUntilHalving, + timeAvg: da.timeAvg, + adjustedTimeAvg: da.adjustedTimeAvg, }; return data; }) diff --git a/frontend/src/app/components/difficulty/difficulty.component.html b/frontend/src/app/components/difficulty/difficulty.component.html index f08ea06f5..ff31d4f57 100644 --- a/frontend/src/app/components/difficulty/difficulty.component.html +++ b/frontend/src/app/components/difficulty/difficulty.component.html @@ -1,7 +1,15 @@ -
Difficulty Adjustment
+
Difficulty Adjustment
+
Halving Countdown
-
+
+ difficulty + | + halving +
+
@@ -42,7 +50,7 @@
Average block time
-
+
@@ -52,9 +60,6 @@ {{ epochData.change | absolute | number: '1.2-2' }} %
- -
-
Previous: @@ -79,6 +84,52 @@
+ +
+
+
+
+
+
+ {{ ((210000 - epochData.blocksUntilHalving) / 2100).toFixed(2) }}% +
+
+
+
+
+ +
+
+ New subsidy +
+
+
+
+ {{ epochData.blocksUntilHalving | number }} +
+
+ Blocks remaining + Block remaining +
+
+
+
+ {{ epochData.timeUntilHalving | date }} +
+
+ +
+ +
+ +
+
+
+
+
+
+
+
diff --git a/frontend/src/app/components/difficulty/difficulty.component.scss b/frontend/src/app/components/difficulty/difficulty.component.scss index 1da1591d0..3b591dc2d 100644 --- a/frontend/src/app/components/difficulty/difficulty.component.scss +++ b/frontend/src/app/components/difficulty/difficulty.component.scss @@ -168,7 +168,7 @@ white-space: nowrap; } -.epoch-progress { +.epoch-progress, .halving-progress { width: 100%; height: 22px; margin-bottom: 12px; @@ -212,4 +212,43 @@ } .blocks-behind { color: #D81B60; +} + +.halving-progress { + position: relative; + .background, .remaining { + position: absolute; + top: 0; + bottom: 0; + height: 100%; + } + .background { + background: linear-gradient(to right, #105fb0, #9339f4); + left: 0; + right: 0; + } + .remaining { + background: #2d3348; + right: 0; + } + .label { + position: relative; + margin: auto; + } +} + +.widget-toggler { + font-size: 12px; + position: absolute; + top: -20px; + right: 3px; + text-align: right; +} + +.toggler-option { + text-decoration: none; +} + +.inactive { + color: #ffffff66; } \ No newline at end of file diff --git a/frontend/src/app/components/difficulty/difficulty.component.ts b/frontend/src/app/components/difficulty/difficulty.component.ts index 81084f524..a58250653 100644 --- a/frontend/src/app/components/difficulty/difficulty.component.ts +++ b/frontend/src/app/components/difficulty/difficulty.component.ts @@ -19,6 +19,7 @@ interface EpochProgress { blocksUntilHalving: number; timeUntilHalving: number; timeAvg: number; + adjustedTimeAvg: number; } type BlockStatus = 'mined' | 'behind' | 'ahead' | 'next' | 'remaining'; @@ -50,6 +51,10 @@ export class DifficultyComponent implements OnInit { isLoadingWebSocket$: Observable; difficultyEpoch$: Observable; + mode: 'difficulty' | 'halving' = 'difficulty'; + userSelectedMode: boolean = false; + + now: number = Date.now(); epochStart: number; currentHeight: number; currentIndex: number; @@ -57,6 +62,7 @@ export class DifficultyComponent implements OnInit { expectedIndex: number; difference: number; shapes: DiffShape[]; + nextSubsidy: number; tooltipPosition = { x: 0, y: 0 }; hoverSection: DiffShape | void; @@ -100,6 +106,12 @@ export class DifficultyComponent implements OnInit { const timeUntilHalving = new Date().getTime() + (blocksUntilHalving * 600000); const newEpochStart = Math.floor(this.stateService.latestBlockHeight / EPOCH_BLOCK_LENGTH) * EPOCH_BLOCK_LENGTH; const newExpectedHeight = Math.floor(newEpochStart + da.expectedBlocks); + this.now = new Date().getTime(); + this.nextSubsidy = getNextBlockSubsidy(maxHeight); + + if (blocksUntilHalving < da.remainingBlocks && !this.userSelectedMode) { + this.mode = 'halving'; + } if (newEpochStart !== this.epochStart || newExpectedHeight !== this.expectedHeight || this.currentHeight !== this.stateService.latestBlockHeight) { this.epochStart = newEpochStart; @@ -153,6 +165,7 @@ export class DifficultyComponent implements OnInit { blocksUntilHalving, timeUntilHalving, timeAvg: da.timeAvg, + adjustedTimeAvg: da.adjustedTimeAvg, }; return data; }) @@ -192,6 +205,12 @@ export class DifficultyComponent implements OnInit { return shapes; } + setMode(mode: 'difficulty' | 'halving'): boolean { + this.mode = mode; + this.userSelectedMode = true; + return false; + } + @HostListener('pointerdown', ['$event']) onPointerDown(event): void { if (this.epochSvgElement?.nativeElement?.contains(event.target)) { @@ -216,3 +235,16 @@ export class DifficultyComponent implements OnInit { this.hoverSection = null; } } + +function getNextBlockSubsidy(height: number): number { + const halvings = Math.floor(height / 210_000) + 1; + // Force block reward to zero when right shift is undefined. + if (halvings >= 64) { + return 0; + } + + let subsidy = BigInt(50 * 100_000_000); + // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years. + subsidy >>= BigInt(halvings); + return Number(subsidy); +} \ No newline at end of file diff --git a/frontend/src/app/components/eight-blocks/eight-blocks.component.html b/frontend/src/app/components/eight-blocks/eight-blocks.component.html index 59390c953..414a693d3 100644 --- a/frontend/src/app/components/eight-blocks/eight-blocks.component.html +++ b/frontend/src/app/components/eight-blocks/eight-blocks.component.html @@ -12,6 +12,7 @@ [animationDuration]="animationDuration" [animationOffset]="animationOffset" [disableSpinner]="true" + [relativeTime]="blockInfo[i]?.timestamp" (txClickEvent)="onTxClick($event)" >
diff --git a/frontend/src/app/components/eight-blocks/eight-blocks.component.ts b/frontend/src/app/components/eight-blocks/eight-blocks.component.ts index 96ab4dee9..81dcc4c5b 100644 --- a/frontend/src/app/components/eight-blocks/eight-blocks.component.ts +++ b/frontend/src/app/components/eight-blocks/eight-blocks.component.ts @@ -95,7 +95,7 @@ export class EightBlocksComponent implements OnInit, OnDestroy { private apiService: ApiService, private bytesPipe: BytesPipe, ) { - this.webGlEnabled = detectWebGL(); + this.webGlEnabled = this.stateService.isBrowser && detectWebGL(); } ngOnInit(): void { diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.html b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.html index 3465bde35..945113356 100644 --- a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.html +++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.html @@ -1,5 +1,5 @@ -
-
+
+
diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts index 178d87897..ca5b3f452 100644 --- a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts +++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts @@ -1,6 +1,6 @@ import { HostListener, OnChanges, OnDestroy } from '@angular/core'; import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; -import { TransactionStripped } from '../../interfaces/websocket.interface'; +import { TransactionStripped } from '../../interfaces/node-api.interface'; import { StateService } from '../../services/state.service'; import { VbytesPipe } from '../../shared/pipes/bytes-pipe/vbytes.pipe'; import { selectPowerOfTen } from '../../bitcoin.utils'; @@ -36,7 +36,7 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr }; constructor( - private stateService: StateService, + public stateService: StateService, private vbytesPipe: VbytesPipe, ) { } @@ -66,7 +66,7 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr return; } const samples = []; - const txs = this.transactions.filter(tx => !tx.acc).map(tx => { return { vsize: tx.vsize, rate: tx.rate || (tx.fee / tx.vsize) }; }).sort((a, b) => { return b.rate - a.rate; }); + const txs = this.transactions.map(tx => { return { vsize: tx.vsize, rate: tx.rate || (tx.fee / tx.vsize) }; }).sort((a, b) => { return b.rate - a.rate; }); const maxBlockVSize = this.stateService.env.BLOCK_WEIGHT_UNITS / 4; const sampleInterval = maxBlockVSize / this.numSamples; let cumVSize = 0; diff --git a/frontend/src/app/components/fiat-selector/fiat-selector.component.ts b/frontend/src/app/components/fiat-selector/fiat-selector.component.ts index 337ef11f3..f2538fec9 100644 --- a/frontend/src/app/components/fiat-selector/fiat-selector.component.ts +++ b/frontend/src/app/components/fiat-selector/fiat-selector.component.ts @@ -35,6 +35,11 @@ export class FiatSelectorComponent implements OnInit { this.stateService.fiatCurrency$.subscribe((fiat) => { this.fiatForm.get('fiat')?.setValue(fiat); }); + if (!this.stateService.env.ADDITIONAL_CURRENCIES) { + this.currencies = this.currencies.filter((currency: any) => { + return ['AUD', 'CAD', 'EUR', 'JPY', 'GBP', 'CHF', 'USD'].includes(currency[0]); + }); + } } changeFiat() { diff --git a/frontend/src/app/components/footer/footer.component.html b/frontend/src/app/components/footer/footer.component.html index 9c0369827..f89e780ff 100644 --- a/frontend/src/app/components/footer/footer.component.html +++ b/frontend/src/app/components/footer/footer.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/frontend/src/app/components/footer/footer.component.scss b/frontend/src/app/components/footer/footer.component.scss index 700d8f39e..cc0146345 100644 --- a/frontend/src/app/components/footer/footer.component.scss +++ b/frontend/src/app/components/footer/footer.component.scss @@ -6,6 +6,12 @@ background-color: #1d1f31; box-shadow: 15px 15px 15px 15px #000; z-index: 10; + + &.inline-footer { + position: relative; + bottom: unset; + top: -44px; + } } .sub-text { diff --git a/frontend/src/app/components/footer/footer.component.ts b/frontend/src/app/components/footer/footer.component.ts index 5e5b1f52a..a78d1e195 100644 --- a/frontend/src/app/components/footer/footer.component.ts +++ b/frontend/src/app/components/footer/footer.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; import { StateService } from '../../services/state.service'; import { Observable, combineLatest } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -23,6 +23,8 @@ interface MempoolInfoData { changeDetection: ChangeDetectionStrategy.OnPush, }) export class FooterComponent implements OnInit { + @Input() inline = false; + mempoolBlocksData$: Observable; mempoolInfoData$: Observable; vBytesPerSecondLimit = 1667; diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html index 83f8a3a4c..4f0c76b88 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html @@ -54,10 +54,10 @@
-
-
+
diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss index 886608573..32885d5de 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss @@ -57,8 +57,6 @@ } .chart-widget { width: 100%; - height: 100%; - height: 240px; } .pool-distribution { diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts index 9858807a6..f4924f255 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; import { echarts, EChartsOption } from '../../graphs/echarts'; -import { merge, Observable, of } from 'rxjs'; +import { combineLatest, fromEvent, merge, Observable, of } from 'rxjs'; import { map, mergeMap, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; import { SeoService } from '../../services/seo.service'; @@ -31,6 +31,7 @@ import { seoDescriptionNetwork } from '../../shared/common.utils'; export class HashrateChartComponent implements OnInit { @Input() tableOnly = false; @Input() widget = false; + @Input() height: number = 300; @Input() right: number | string = 45; @Input() left: number | string = 75; @@ -59,7 +60,7 @@ export class HashrateChartComponent implements OnInit { private storageService: StorageService, private miningService: MiningService, private route: ActivatedRoute, - private stateService: StateService + public stateService: StateService ) { } @@ -86,28 +87,32 @@ export class HashrateChartComponent implements OnInit { } }); - this.hashrateObservable$ = merge( - this.radioGroupForm.get('dateSpan').valueChanges - .pipe( - startWith(this.radioGroupForm.controls.dateSpan.value), - switchMap((timespan) => { - if (!this.widget && !firstRun) { - this.storageService.setValue('miningWindowPreference', timespan); - } - this.timespan = timespan; - firstRun = false; - this.miningWindowPreference = timespan; - this.isLoading = true; - return this.apiService.getHistoricalHashrate$(this.timespan); - }) - ), - this.stateService.chainTip$ + this.hashrateObservable$ = combineLatest( + merge( + this.radioGroupForm.get('dateSpan').valueChanges .pipe( - switchMap(() => { + startWith(this.radioGroupForm.controls.dateSpan.value), + switchMap((timespan) => { + if (!this.widget && !firstRun) { + this.storageService.setValue('miningWindowPreference', timespan); + } + this.timespan = timespan; + firstRun = false; + this.miningWindowPreference = timespan; + this.isLoading = true; return this.apiService.getHistoricalHashrate$(this.timespan); }) - ) + ), + this.stateService.chainTip$ + .pipe( + switchMap(() => { + return this.apiService.getHistoricalHashrate$(this.timespan); + }) + ) + ), + fromEvent(window, 'resize').pipe(startWith(null)), ).pipe( + map(([response, _]) => response), tap((response: any) => { const data = response.body; @@ -221,6 +226,7 @@ export class HashrateChartComponent implements OnInit { ]), ], grid: { + height: (this.widget && this.height) ? this.height - 30 : undefined, top: this.widget ? 20 : 40, bottom: this.widget ? 30 : 70, right: this.right, @@ -320,7 +326,7 @@ export class HashrateChartComponent implements OnInit { }, }, ], - selected: JSON.parse(this.storageService.getValue('hashrate_difficulty_legend')) ?? { + selected: JSON.parse(this.storageService?.getValue('hashrate_difficulty_legend') || 'null') ?? { '$localize`:@@79a9dc5b1caca3cbeb1733a19515edacc5fc7920:Hashrate`': true, '$localize`::Difficulty`': this.network === '', '$localize`Hashrate (MA)`': true, @@ -333,6 +339,9 @@ export class HashrateChartComponent implements OnInit { const newMin = Math.floor(value.min / selectedPowerOfTen.divider / 10); return newMin * selectedPowerOfTen.divider * 10; }, + max: (value) => { + return value.max; + }, type: 'value', axisLabel: { color: 'rgb(110, 112, 121)', @@ -351,11 +360,18 @@ export class HashrateChartComponent implements OnInit { }, }, { - min: (value) => { - return value.min * 0.9; - }, type: 'value', position: 'right', + min: (_) => { + const firstYAxisMin = this.chartInstance.getModel().getComponent('yAxis', 0).axis.scale.getExtent()[0]; + const selectedPowerOfTen: any = selectPowerOfTen(firstYAxisMin); + const newMin = Math.floor(firstYAxisMin / selectedPowerOfTen.divider / 10) + return 600 / 2 ** 32 * newMin * selectedPowerOfTen.divider * 10; + }, + max: (_) => { + const firstYAxisMax = this.chartInstance.getModel().getComponent('yAxis', 0).axis.scale.getExtent()[1]; + return 600 / 2 ** 32 * firstYAxisMax; + }, axisLabel: { color: 'rgb(110, 112, 121)', formatter: (val): string => { diff --git a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.html b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.html index bbdc745fe..e62cdd4eb 100644 --- a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.html +++ b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.html @@ -31,10 +31,10 @@
-
-
+
diff --git a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts index 901dff858..d938baf15 100644 --- a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts +++ b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts @@ -10,6 +10,14 @@ import { StorageService } from '../../services/storage.service'; import { MiningService } from '../../services/mining.service'; import { download } from '../../shared/graphs.utils'; import { ActivatedRoute } from '@angular/router'; +import { StateService } from '../../services/state.service'; + +interface Hashrate { + timestamp: number; + avgHashRate: number; + share: number; + poolName: string; +} @Component({ selector: 'app-hashrate-chart-pools', @@ -32,6 +40,7 @@ export class HashrateChartPoolsComponent implements OnInit { miningWindowPreference: string; radioGroupForm: UntypedFormGroup; + hashrates: Hashrate[]; chartOptions: EChartsOption = {}; chartInitOptions = { renderer: 'svg', @@ -52,6 +61,7 @@ export class HashrateChartPoolsComponent implements OnInit { private cd: ChangeDetectorRef, private storageService: StorageService, private miningService: MiningService, + public stateService: StateService, private route: ActivatedRoute, ) { this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' }); @@ -88,56 +98,9 @@ export class HashrateChartPoolsComponent implements OnInit { return this.apiService.getHistoricalPoolsHashrate$(timespan) .pipe( tap((response) => { - const hashrates = response.body; + this.hashrates = response.body; // Prepare series (group all hashrates data point by pool) - const grouped = {}; - for (const hashrate of hashrates) { - if (!grouped.hasOwnProperty(hashrate.poolName)) { - grouped[hashrate.poolName] = []; - } - grouped[hashrate.poolName].push(hashrate); - } - - const series = []; - const legends = []; - for (const name in grouped) { - series.push({ - zlevel: 0, - stack: 'Total', - name: name, - showSymbol: false, - symbol: 'none', - data: grouped[name].map((val) => [val.timestamp * 1000, val.share * 100]), - type: 'line', - lineStyle: { width: 0 }, - areaStyle: { opacity: 1 }, - smooth: true, - color: poolsColor[name.replace(/[^a-zA-Z0-9]/g, '').toLowerCase()], - emphasis: { - disabled: true, - scale: false, - }, - }); - - legends.push({ - name: name, - inactiveColor: 'rgb(110, 112, 121)', - textStyle: { - color: 'white', - }, - icon: 'roundRect', - itemStyle: { - color: poolsColor[name.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()], - }, - }); - } - - this.prepareChartOptions({ - legends: legends, - series: series, - }); - this.isLoading = false; - + const series = this.applyHashrates(); if (series.length === 0) { this.cd.markForCheck(); throw new Error(); @@ -157,6 +120,77 @@ export class HashrateChartPoolsComponent implements OnInit { ); } + applyHashrates(): any[] { + const times: { [time: number]: { hashrates: { [pool: string]: Hashrate } } } = {}; + const pools = {}; + for (const hashrate of this.hashrates) { + if (!times[hashrate.timestamp]) { + times[hashrate.timestamp] = { hashrates: {} }; + } + times[hashrate.timestamp].hashrates[hashrate.poolName] = hashrate; + if (!pools[hashrate.poolName]) { + pools[hashrate.poolName] = true; + } + } + + const sortedTimes = Object.keys(times).sort((a,b) => parseInt(a) - parseInt(b)).map(time => ({ time: parseInt(time), hashrates: times[time].hashrates })); + const lastHashrates = sortedTimes[sortedTimes.length - 1].hashrates; + const sortedPools = Object.keys(pools).sort((a,b) => { + if (lastHashrates[b]?.share ?? lastHashrates[a]?.share ?? false) { + // sort by descending share of hashrate in latest period + return (lastHashrates[b]?.share || 0) - (lastHashrates[a]?.share || 0); + } else { + // tiebreak by pool name + b < a; + } + }); + + const series = []; + const legends = []; + for (const name of sortedPools) { + const data = sortedTimes.map(({ time, hashrates }) => { + return [time * 1000, (hashrates[name]?.share || 0) * 100]; + }); + series.push({ + zlevel: 0, + stack: 'Total', + name: name, + showSymbol: false, + symbol: 'none', + data, + type: 'line', + lineStyle: { width: 0 }, + areaStyle: { opacity: 1 }, + smooth: true, + color: poolsColor[name.replace(/[^a-zA-Z0-9]/g, '').toLowerCase()], + emphasis: { + disabled: true, + scale: false, + }, + }); + + legends.push({ + name: name, + inactiveColor: 'rgb(110, 112, 121)', + textStyle: { + color: 'white', + }, + icon: 'roundRect', + itemStyle: { + color: poolsColor[name.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()], + }, + }); + } + + this.prepareChartOptions({ + legends: legends, + series: series, + }); + this.isLoading = false; + + return series; + } + prepareChartOptions(data) { let title: object; if (data.series.length === 0) { @@ -257,6 +291,7 @@ export class HashrateChartPoolsComponent implements OnInit { }, }], }; + this.cd.markForCheck(); } onChartInit(ec) { diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.html b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.html index 05f9d5afb..b3f736d34 100644 --- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.html +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.html @@ -1,6 +1,6 @@ -
-
+
\ No newline at end of file diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts index 703181eba..5c102eadf 100644 --- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts @@ -48,7 +48,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On constructor( @Inject(LOCALE_ID) private locale: string, private storageService: StorageService, - private stateService: StateService, + public stateService: StateService, ) { } ngOnInit() { @@ -265,8 +265,8 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On type: 'value', axisLabel: { fontSize: 11, - formatter: (value) => { - return this.weightMode ? value * 4 : value; + formatter: (value): string => { + return this.weightMode ? (value * 4).toString() : value.toString(); } }, splitLine: { diff --git a/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.html b/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.html index 99fb46934..841d6b45c 100644 --- a/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.html +++ b/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.html @@ -1,4 +1,4 @@ -
-
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.ts b/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.ts index c4e8cbf91..4359d8fa3 100644 --- a/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.ts +++ b/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.ts @@ -1,6 +1,7 @@ import { Component, Inject, LOCALE_ID, ChangeDetectionStrategy, Input, OnChanges, OnInit } from '@angular/core'; import { formatDate, formatNumber } from '@angular/common'; import { EChartsOption } from '../../graphs/echarts'; +import { StateService } from '../../services/state.service'; @Component({ selector: 'app-lbtc-pegs-graph', @@ -18,21 +19,21 @@ import { EChartsOption } from '../../graphs/echarts'; }) export class LbtcPegsGraphComponent implements OnInit, OnChanges { @Input() data: any; + @Input() height: number | string = '360'; pegsChartOptions: EChartsOption; - height: number | string = '200'; right: number | string = '10'; top: number | string = '20'; left: number | string = '50'; template: ('widget' | 'advanced') = 'widget'; isLoading = true; - pegsChartOption: EChartsOption = {}; pegsChartInitOption = { renderer: 'svg' }; constructor( + public stateService: StateService, @Inject(LOCALE_ID) private locale: string, ) { } @@ -41,20 +42,24 @@ export class LbtcPegsGraphComponent implements OnInit, OnChanges { } ngOnChanges() { - if (!this.data) { + if (!this.data?.liquidPegs) { return; } - this.pegsChartOptions = this.createChartOptions(this.data.series, this.data.labels); + if (!this.data.liquidReserves) { + this.pegsChartOptions = this.createChartOptions(this.data.liquidPegs.series, this.data.liquidPegs.labels); + } else { + this.pegsChartOptions = this.createChartOptions(this.data.liquidPegs.series, this.data.liquidPegs.labels, this.data.liquidReserves.series); + } } rendered() { - if (!this.data) { + if (!this.data.liquidPegs) { return; } this.isLoading = false; } - createChartOptions(series: number[], labels: string[]): EChartsOption { + createChartOptions(pegSeries: number[], labels: string[], reservesSeries?: number[],): EChartsOption { return { grid: { height: this.height, @@ -99,17 +104,18 @@ export class LbtcPegsGraphComponent implements OnInit, OnChanges { type: 'line', }, formatter: (params: any) => { - const colorSpan = (color: string) => ``; + const colorSpan = (color: string) => ``; let itemFormatted = '
' + params[0].axisValue + '
'; - params.map((item: any, index: number) => { + for (let index = params.length - 1; index >= 0; index--) { + const item = params[index]; if (index < 26) { itemFormatted += `
${colorSpan(item.color)}
-
-
${formatNumber(item.value, this.locale, '1.2-2')} L-BTC
+
+
${formatNumber(item.value, this.locale, '1.2-2')} ${item.seriesName}
`; } - }); + } return `
${itemFormatted}
`; } }, @@ -138,20 +144,34 @@ export class LbtcPegsGraphComponent implements OnInit, OnChanges { }, series: [ { - data: series, + data: pegSeries, + name: 'L-BTC', + color: '#116761', type: 'line', stack: 'total', - smooth: false, + smooth: true, showSymbol: false, areaStyle: { opacity: 0.2, color: '#116761', }, lineStyle: { - width: 3, + width: 2, color: '#116761', }, }, + { + data: reservesSeries, + name: 'BTC', + color: '#EA983B', + type: 'line', + smooth: true, + showSymbol: false, + lineStyle: { + width: 2, + color: '#EA983B', + }, + }, ], }; } diff --git a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html index 49f05c3a2..7dfe746e7 100644 --- a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html +++ b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html @@ -44,7 +44,7 @@ -
{{ pool.rank }} - + {{ pool.name }} {{ pool.lastEstimatedHashrate }} {{ diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.scss b/frontend/src/app/components/pool-ranking/pool-ranking.component.scss index 970b3a8ea..f5a49678b 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.scss +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.scss @@ -28,7 +28,9 @@ .chart-widget { width: 100%; height: 100%; - height: 240px; + @media (max-width: 767px) { + max-height: 240px; + } @media (max-width: 485px) { max-height: 200px; } diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts index 392cdf8c5..11ee6e506 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts @@ -20,6 +20,7 @@ import { isMobile } from '../../shared/common.utils'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class PoolRankingComponent implements OnInit { + @Input() height: number = 300; @Input() widget = false; miningWindowPreference: string; @@ -40,7 +41,7 @@ export class PoolRankingComponent implements OnInit { miningStatsObservable$: Observable; constructor( - private stateService: StateService, + public stateService: StateService, private storageService: StorageService, private formBuilder: UntypedFormBuilder, private miningService: MiningService, @@ -162,7 +163,7 @@ export class PoolRankingComponent implements OnInit { const i = pool.blockCount.toString(); if (this.miningWindowPreference === '24h') { return `${pool.name} (${pool.share}%)
` + - pool.lastEstimatedHashrate.toString() + ' PH/s' + + pool.lastEstimatedHashrate.toString() + ' ' + miningStats.miningUnits.hashrateUnit + `
` + $localize`${ i }:INTERPOLATION: blocks`; } else { return `${pool.name} (${pool.share}%)
` + @@ -200,7 +201,7 @@ export class PoolRankingComponent implements OnInit { const i = totalBlockOther.toString(); if (this.miningWindowPreference === '24h') { return `` + $localize`Other (${percentage})` + `
` + - totalEstimatedHashrateOther.toString() + ' PH/s' + + totalEstimatedHashrateOther.toString() + ' ' + miningStats.miningUnits.hashrateUnit + `
` + $localize`${ i }:INTERPOLATION: blocks`; } else { return `` + $localize`Other (${percentage})` + `
` + diff --git a/frontend/src/app/components/pool/pool-preview.component.html b/frontend/src/app/components/pool/pool-preview.component.html index ff5201ae0..5ccb415e6 100644 --- a/frontend/src/app/components/pool/pool-preview.component.html +++ b/frontend/src/app/components/pool/pool-preview.component.html @@ -25,7 +25,7 @@
-
+
diff --git a/frontend/src/app/components/pool/pool.component.html b/frontend/src/app/components/pool/pool.component.html index 98d1d937b..8ecb68bc9 100644 --- a/frontend/src/app/components/pool/pool.component.html +++ b/frontend/src/app/components/pool/pool.component.html @@ -6,7 +6,7 @@
+ onError="this.onerror=null; this.src = '/resources/mining-pools/default.svg'" class="mr-3">

{{ poolStats.pool.name }}

@@ -85,10 +85,9 @@
- - - - + - - - + - - - + - - - - @@ -198,8 +168,8 @@ -
-
+
+
diff --git a/frontend/src/app/components/pool/pool.component.scss b/frontend/src/app/components/pool/pool.component.scss index 92fdc2ef3..8bd6763e5 100644 --- a/frontend/src/app/components/pool/pool.component.scss +++ b/frontend/src/app/components/pool/pool.component.scss @@ -32,6 +32,7 @@ } .chart { + margin-top: 10px; margin-bottom: 20px; @media (max-width: 768px) { margin-bottom: 10px; diff --git a/frontend/src/app/components/pool/pool.component.ts b/frontend/src/app/components/pool/pool.component.ts index 4457814c3..8274bf441 100644 --- a/frontend/src/app/components/pool/pool.component.ts +++ b/frontend/src/app/components/pool/pool.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { echarts, EChartsOption } from '../../graphs/echarts'; -import { BehaviorSubject, Observable, of, timer } from 'rxjs'; -import { catchError, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators'; +import { BehaviorSubject, Observable, combineLatest, of, timer } from 'rxjs'; +import { catchError, distinctUntilChanged, filter, map, share, switchMap, tap } from 'rxjs/operators'; import { BlockExtended, PoolStat } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; @@ -10,6 +10,11 @@ import { selectPowerOfTen } from '../../bitcoin.utils'; import { formatNumber } from '@angular/common'; import { SeoService } from '../../services/seo.service'; +interface AccelerationTotal { + cost: number, + count: number, +} + @Component({ selector: 'app-pool', templateUrl: './pool.component.html', @@ -25,6 +30,7 @@ export class PoolComponent implements OnInit { formatNumber = formatNumber; poolStats$: Observable; blocks$: Observable; + oobFees$: Observable; isLoading = true; chartOptions: EChartsOption = {}; @@ -59,7 +65,9 @@ export class PoolComponent implements OnInit { .pipe( switchMap((data) => { this.isLoading = false; - this.prepareChartOptions(data.map(val => [val.timestamp * 1000, val.avgHashrate])); + const hashrate = data.map(val => [val.timestamp * 1000, val.avgHashrate]); + const share = data.map(val => [val.timestamp * 1000, val.share * 100]); + this.prepareChartOptions(hashrate, share); return [slug]; }), catchError(() => { @@ -111,11 +119,22 @@ export class PoolComponent implements OnInit { map(() => this.blocks), share(), ); + + this.oobFees$ = this.route.params.pipe(map((params) => params.slug)).pipe( + switchMap(slug => { + return combineLatest([ + this.apiService.getAccelerationTotals$(this.slug, '1w'), + this.apiService.getAccelerationTotals$(this.slug, '1m'), + this.apiService.getAccelerationTotals$(this.slug), + ]); + }), + filter(oob => oob.length === 3 && oob[2].count > 0) + ); } - prepareChartOptions(data) { + prepareChartOptions(hashrate, share) { let title: object; - if (data.length <= 1) { + if (hashrate.length <= 1) { title = { textStyle: { color: 'grey', @@ -160,26 +179,57 @@ export class PoolComponent implements OnInit { }, borderColor: '#000', formatter: function (ticks: any[]) { - let hashratePowerOfTen: any = selectPowerOfTen(1); - let hashrate = ticks[0].data[1]; - - hashratePowerOfTen = selectPowerOfTen(ticks[0].data[1], 10); - hashrate = ticks[0].data[1] / hashratePowerOfTen.divider; + let hashrateString = ''; + let dominanceString = ''; + for (const tick of ticks) { + if (tick.seriesIndex === 0) { + let hashratePowerOfTen = selectPowerOfTen(tick.data[1], 10); + let hashrateData = tick.data[1] / hashratePowerOfTen.divider; + hashrateString = `${tick.marker} ${tick.seriesName}: ${formatNumber(hashrateData, this.locale, '1.0-0')} ${hashratePowerOfTen.unit}H/s
`; + } else if (tick.seriesIndex === 1) { + dominanceString = `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.0-2')}%`; + } + } + return ` ${ticks[0].axisValueLabel}
- ${ticks[0].marker} ${ticks[0].seriesName}: ${formatNumber(hashrate, this.locale, '1.0-0')} ${hashratePowerOfTen.unit}H/s
+ ${hashrateString} + ${dominanceString} `; }.bind(this) }, - xAxis: data.length <= 1 ? undefined : { + xAxis: hashrate.length <= 1 ? undefined : { type: 'time', splitNumber: (this.isMobile()) ? 5 : 10, axisLabel: { hideOverlap: true, } }, - yAxis: data.length <= 1 ? undefined : [ + legend: { + data: [ + { + name: $localize`:mining.hashrate:Hashrate`, + inactiveColor: 'rgb(110, 112, 121)', + textStyle: { + color: 'white', + }, + icon: 'roundRect', + itemStyle: { + color: '#FFB300', + }, + }, + { + name: $localize`:mining.pool-dominance:Pool Dominance`, + inactiveColor: 'rgb(110, 112, 121)', + textStyle: { + color: 'white', + }, + icon: 'roundRect', + }, + ], + }, + yAxis: hashrate.length <= 1 ? undefined : [ { min: (value) => { return value.min * 0.9; @@ -197,21 +247,45 @@ export class PoolComponent implements OnInit { show: false, } }, - ], - series: data.length <= 1 ? undefined : [ { - zlevel: 0, - name: 'Hashrate', + type: 'value', + axisLabel: { + color: 'rgb(110, 112, 121)', + formatter: (val) => { + return `${val}%` + } + }, + splitLine: { + show: false, + } + } + ], + series: hashrate.length <= 1 ? undefined : [ + { + zlevel: 1, + name: $localize`:mining.hashrate:Hashrate`, showSymbol: false, symbol: 'none', - data: data, + data: hashrate, type: 'line', lineStyle: { width: 2, }, }, + { + zlevel: 0, + name: $localize`:mining.pool-dominance:Pool Dominance`, + showSymbol: false, + symbol: 'none', + data: share, + type: 'line', + yAxisIndex: 1, + lineStyle: { + width: 2, + }, + } ], - dataZoom: data.length <= 1 ? undefined : [{ + dataZoom: hashrate.length <= 1 ? undefined : [{ type: 'inside', realtime: true, zoomLock: true, diff --git a/frontend/src/app/components/privacy-policy/privacy-policy.component.html b/frontend/src/app/components/privacy-policy/privacy-policy.component.html index d14b12bc3..1250af686 100644 --- a/frontend/src/app/components/privacy-policy/privacy-policy.component.html +++ b/frontend/src/app/components/privacy-policy/privacy-policy.component.html @@ -11,7 +11,7 @@
-

The mempool.space website, the liquid.network website, the bisq.markets website, their associated API services, and related network and server infrastructure (collectively, the "Website") are operated by Mempool Space K.K. in Japan ("Mempool", "We", or "Us") and self-hosted from AS142052.

+

The mempool.space website, the liquid.network website, their associated API services, and related network and server infrastructure (collectively, the "Website") are operated by Mempool Space K.K. in Japan ("Mempool", "We", or "Us") and self-hosted from AS142052.

This website and its API service (collectively, the "Website") are operated by a member of the Bitcoin community ("We" or "Us"). Mempool Space K.K. in Japan ("Mempool") has no affiliation with the operator of this Website, and does not sponsor or endorse the information provided herein.

diff --git a/frontend/src/app/components/privacy-policy/privacy-policy.component.ts b/frontend/src/app/components/privacy-policy/privacy-policy.component.ts index b98390731..05f77c063 100644 --- a/frontend/src/app/components/privacy-policy/privacy-policy.component.ts +++ b/frontend/src/app/components/privacy-policy/privacy-policy.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { Env, StateService } from '../../services/state.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; @Component({ selector: 'app-privacy-policy', @@ -13,10 +14,12 @@ export class PrivacyPolicyComponent { constructor( private stateService: StateService, private seoService: SeoService, + private ogService: OpenGraphService, ) { } ngOnInit(): void { this.seoService.setTitle('Privacy Policy'); this.seoService.setDescription('Trusted third parties are security holes, as are trusted first parties...you should only trust your own self-hosted instance of The Mempool Open Source Project®.'); + this.ogService.setManualOgImage('privacy.jpg'); } } diff --git a/frontend/src/app/components/push-transaction/push-transaction.component.ts b/frontend/src/app/components/push-transaction/push-transaction.component.ts index cbc5d905a..2fbe356e8 100644 --- a/frontend/src/app/components/push-transaction/push-transaction.component.ts +++ b/frontend/src/app/components/push-transaction/push-transaction.component.ts @@ -3,6 +3,7 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; import { seoDescriptionNetwork } from '../../shared/common.utils'; @Component({ @@ -21,6 +22,7 @@ export class PushTransactionComponent implements OnInit { private apiService: ApiService, public stateService: StateService, private seoService: SeoService, + private ogService: OpenGraphService, ) { } ngOnInit(): void { @@ -30,6 +32,7 @@ export class PushTransactionComponent implements OnInit { this.seoService.setTitle($localize`:@@meta.title.push-tx:Broadcast Transaction`); this.seoService.setDescription($localize`:@@meta.description.push-tx:Broadcast a transaction to the ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} network using the transaction's hash.`); + this.ogService.setManualOgImage('tx-push.jpg'); } postTx() { diff --git a/frontend/src/app/components/rbf-list/rbf-list.component.ts b/frontend/src/app/components/rbf-list/rbf-list.component.ts index 0721c7fdf..ff30dd1c9 100644 --- a/frontend/src/app/components/rbf-list/rbf-list.component.ts +++ b/frontend/src/app/components/rbf-list/rbf-list.component.ts @@ -7,6 +7,7 @@ import { RbfTree } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; import { seoDescriptionNetwork } from '../../shared/common.utils'; @Component({ @@ -29,6 +30,7 @@ export class RbfList implements OnInit, OnDestroy { public stateService: StateService, private websocketService: WebsocketService, private seoService: SeoService, + private ogService: OpenGraphService, ) { } ngOnInit(): void { @@ -57,6 +59,7 @@ export class RbfList implements OnInit, OnDestroy { this.seoService.setTitle($localize`:@@5e3d5a82750902f159122fcca487b07f1af3141f:RBF Replacements`); this.seoService.setDescription($localize`:@@meta.description.rbf-list:See the most recent RBF replacements on the Bitcoin${seoDescriptionNetwork(this.stateService.network)} network, updated in real-time.`); + this.ogService.setManualOgImage('rbf.jpg'); } ngOnDestroy(): void { diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index 3bbe5bccb..8f5e65f96 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -178,7 +178,7 @@ export class SearchFormComponent implements OnInit { const addressPrefixSearchResults = result[0]; const lightningResults = result[1]; - // Do not show date and timestamp results for liquid and bisq + // Do not show date and timestamp results for liquid const isNetworkBitcoin = this.network === '' || this.network === 'testnet' || this.network === 'signet'; const matchesBlockHeight = this.regexBlockheight.test(searchText) && parseInt(searchText) <= this.stateService.latestBlockHeight; @@ -186,16 +186,10 @@ export class SearchFormComponent implements OnInit { const matchesUnixTimestamp = this.regexUnixTimestamp.test(searchText) && parseInt(searchText) <= Math.floor(Date.now() / 1000) && isNetworkBitcoin; const matchesTxId = this.regexTransaction.test(searchText) && !this.regexBlockhash.test(searchText); const matchesBlockHash = this.regexBlockhash.test(searchText); - let matchesAddress = !matchesTxId && this.regexAddress.test(searchText); + const matchesAddress = !matchesTxId && this.regexAddress.test(searchText); const otherNetworks = findOtherNetworks(searchText, this.network as any || 'mainnet', this.env); const liquidAsset = this.assets ? (this.assets[searchText] || []) : []; - - // Add B prefix to addresses in Bisq network - if (!matchesAddress && this.network === 'bisq' && getRegex('address', 'mainnet').test(searchText)) { - searchText = 'B' + searchText; - matchesAddress = !matchesTxId && this.regexAddress.test(searchText); - } - + if (matchesDateTime && searchText.indexOf('/') !== -1) { searchText = searchText.replace(/\//g, '-'); } @@ -299,7 +293,7 @@ export class SearchFormComponent implements OnInit { navigate(url: string, searchText: string, extras?: any, swapNetwork?: string) { - if (needBaseModuleChange(this.env.BASE_MODULE as 'liquid' | 'bisq' | 'mempool', swapNetwork as Network)) { + if (needBaseModuleChange(this.env.BASE_MODULE as 'liquid' | 'mempool', swapNetwork as Network)) { window.location.href = getTargetUrl(swapNetwork as Network, searchText, this.env); } else { this.router.navigate([this.relativeUrlPipe.transform(url, swapNetwork), searchText], extras); diff --git a/frontend/src/app/components/server-health/server-health.component.html b/frontend/src/app/components/server-health/server-health.component.html new file mode 100644 index 000000000..217e5c019 --- /dev/null +++ b/frontend/src/app/components/server-health/server-health.component.html @@ -0,0 +1,36 @@ +
+ + + + + + +
+
+ +
@@ -111,72 +110,43 @@
+ + +
- - - + + + - - - + + +
RewardHashrate (24h)Avg HealthBlocks (24h)1wAll
{{ poolStats.estimatedHashrate | amountShortener : 1 : 'H/s' }}{{ poolStats.avgBlockHealth }}% - - Unknown - - {{ formatNumber(poolStats.blockCount['24h'], this.locale, '1.0-0') }} ({{ formatNumber(100 * + poolStats.blockShare['24h'], this.locale, '1.0-0') }}%){{ formatNumber(poolStats.blockCount['1w'], this.locale, '1.0-0') }} ({{ formatNumber(100 * + poolStats.blockShare['1w'], this.locale, '1.0-0') }}%){{ formatNumber(poolStats.blockCount['all'], this.locale, '1.0-0') }} ({{ formatNumber(100 * + poolStats.blockShare['all'], this.locale, '1.0-0') }}%)
+ +
- - - + + + - - - - -
Blocks (24h)1wAllOut-of-band Fees (1w)1mAll
{{ formatNumber(poolStats.blockCount['24h'], this.locale, '1.0-0') }} ({{ formatNumber(100 * - poolStats.blockShare['24h'], this.locale, '1.0-0') }}%){{ formatNumber(poolStats.blockCount['1w'], this.locale, '1.0-0') }} ({{ formatNumber(100 * - poolStats.blockShare['1w'], this.locale, '1.0-0') }}%){{ formatNumber(poolStats.blockCount['all'], this.locale, '1.0-0') }} ({{ formatNumber(100 * - poolStats.blockShare['all'], this.locale, '1.0-0') }}%)
-
- - - - - - - - - - - - +
Blocks (24h)1wAll
{{ formatNumber(poolStats.blockCount['24h'], this.locale, '1.0-0') }} ({{ formatNumber(100 * - poolStats.blockShare['24h'], this.locale, '1.0-0') }}%){{ formatNumber(poolStats.blockCount['1w'], this.locale, '1.0-0') }} ({{ formatNumber(100 * - poolStats.blockShare['1w'], this.locale, '1.0-0') }}%){{ formatNumber(poolStats.blockCount['all'], this.locale, '1.0-0') }} ({{ formatNumber(100 * - poolStats.blockShare['all'], this.locale, '1.0-0') }}%)
+ + + + + + + + + + + + + + + + + + + + +
HostLast checkedRTTRTTHeight
{{ i + 1 }}{{ host.active ? '⭐️' : host.flag }}{{ host.link }}{{ getLastUpdateSeconds(host) }}{{ (host.rtt / 1000) | number : '1.1-1' }} {{ host.rtt == null ? '' : 's'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }}{{ host.rtt | number : '1.0-0' }} {{ host.rtt == null ? '' : 'ms'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }}{{ host.latestHeight }} {{ !host.checked ? '⏳' : (host.outOfSync ? '🚫' : (host.latestHeight && host.latestHeight < tip ? '🟧' : '✅')) }}
+
+ +
diff --git a/frontend/src/app/components/server-health/server-health.component.scss b/frontend/src/app/components/server-health/server-health.component.scss new file mode 100644 index 000000000..4cba0cd76 --- /dev/null +++ b/frontend/src/app/components/server-health/server-health.component.scss @@ -0,0 +1,75 @@ +.tomahawk { + .links { + text-align: right; + margin-inline-end: 1em; + + a, span { + margin-left: 1em; + } + } + + .status-panel { + max-width: 720px; + margin: auto; + padding: 1em; + background: #24273e; + } + + .status-table { + width: 100%; + + td, th { + padding: 0.25em; + + &.rank, &.flag { + width: 28px; + text-align: right; + } + &.updated { + display: none; + width: 130px; + text-align: right; + white-space: pre-wrap; + } + &.rtt, &.height { + width: 92px; + text-align: right; + } + &.only-small { + display: table-cell; + &.rtt { + width: 60px; + } + } + &.only-large { + display: none; + } + &.height { + padding-right: 0.5em; + } + &.host { + width: auto; + overflow: hidden; + text-overflow: ellipsis; + } + + @media (min-width: 576px) { + &.rank, &.flag { + width: 32px; + } + &.updated { + display: table-cell; + } + &.rtt, &.height { + width: 96px; + } + &.only-small { + display: none; + } + &.only-large { + display: table-cell; + } + } + } + } +} \ No newline at end of file diff --git a/frontend/src/app/components/server-health/server-health.component.ts b/frontend/src/app/components/server-health/server-health.component.ts new file mode 100644 index 000000000..bb4d59959 --- /dev/null +++ b/frontend/src/app/components/server-health/server-health.component.ts @@ -0,0 +1,84 @@ +import { Component, OnInit, ChangeDetectionStrategy, SecurityContext, ChangeDetectorRef } from '@angular/core'; +import { WebsocketService } from '../../services/websocket.service'; +import { Observable, Subject, map } from 'rxjs'; +import { StateService } from '../../services/state.service'; +import { HealthCheckHost } from '../../interfaces/websocket.interface'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Component({ + selector: 'app-server-health', + templateUrl: './server-health.component.html', + styleUrls: ['./server-health.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ServerHealthComponent implements OnInit { + hosts$: Observable; + tip$: Subject; + interval: number; + now: number = Date.now(); + + constructor( + private websocketService: WebsocketService, + private stateService: StateService, + private cd: ChangeDetectorRef, + public sanitizer: DomSanitizer, + ) {} + + ngOnInit(): void { + this.hosts$ = this.stateService.serverHealth$.pipe( + map((hosts) => { + const subpath = window.location.pathname.slice(0, -11); + for (const host of hosts) { + let statusUrl = ''; + let linkHost = ''; + if (host.socket) { + statusUrl = 'https://' + window.location.hostname + subpath + '/status'; + linkHost = window.location.hostname + subpath; + } else { + const hostUrl = new URL(host.host); + statusUrl = 'https://' + hostUrl.hostname + subpath + '/status'; + linkHost = hostUrl.hostname + subpath; + } + host.statusPage = this.sanitizer.bypassSecurityTrustResourceUrl(this.sanitizer.sanitize(SecurityContext.URL, statusUrl)); + host.link = linkHost; + host.flag = this.parseFlag(host.host); + } + return hosts; + }) + ); + this.tip$ = this.stateService.chainTip$; + this.websocketService.want(['mempool-blocks', 'stats', 'blocks', 'tomahawk']); + + this.interval = window.setInterval(() => { + this.now = Date.now(); + this.cd.markForCheck(); + }, 1000); + } + + trackByFn(index: number, host: HealthCheckHost): string { + return host.host; + } + + getLastUpdateSeconds(host: HealthCheckHost): string { + if (host.lastChecked) { + const seconds = Math.ceil((this.now - host.lastChecked) / 1000); + return `${seconds} second${seconds > 1 ? 's' : ' '} ago`; + } else { + return '~'; + } + } + + private parseFlag(host: string): string { + if (host.includes('.fra.')) { + return '🇩🇪'; + } else if (host.includes('.tk7.')) { + return '🇯🇵'; + } else if (host.includes('.fmt.')) { + return '🇺🇸'; + } else if (host.includes('.va1.')) { + return '🇺🇸'; + } else { + return ''; + } + } +} diff --git a/frontend/src/app/components/server-health/server-status.component.html b/frontend/src/app/components/server-health/server-status.component.html new file mode 100644 index 000000000..7e8a5cc50 --- /dev/null +++ b/frontend/src/app/components/server-health/server-status.component.html @@ -0,0 +1,16 @@ +
+ + + + + + + + + +
diff --git a/frontend/src/app/components/server-health/server-status.component.scss b/frontend/src/app/components/server-health/server-status.component.scss new file mode 100644 index 000000000..1f3c01a0c --- /dev/null +++ b/frontend/src/app/components/server-health/server-status.component.scss @@ -0,0 +1,22 @@ +.tomahawk { + .links { + text-align: right; + margin-inline-end: 1em; + + a, span { + margin-left: 1em; + } + } + + .mempoolStatus { + width: 100%; + height: 270px; + border: none; + } + + .hostLink { + text-align: center; + margin: auto; + margin-top: 1em; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/server-health/server-status.component.ts b/frontend/src/app/components/server-health/server-status.component.ts new file mode 100644 index 000000000..e1300a68d --- /dev/null +++ b/frontend/src/app/components/server-health/server-status.component.ts @@ -0,0 +1,80 @@ +import { Component, OnInit, ChangeDetectionStrategy, SecurityContext, OnDestroy, ChangeDetectorRef } from '@angular/core'; +import { WebsocketService } from '../../services/websocket.service'; +import { Observable, Subject, Subscription, map, tap } from 'rxjs'; +import { StateService } from '../../services/state.service'; +import { HealthCheckHost } from '../../interfaces/websocket.interface'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Component({ + selector: 'app-server-status', + templateUrl: './server-status.component.html', + styleUrls: ['./server-status.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ServerStatusComponent implements OnInit, OnDestroy { + tip$: Subject; + hosts: HealthCheckHost[] = []; + hostSubscription: Subscription; + + constructor( + private websocketService: WebsocketService, + private stateService: StateService, + private cd: ChangeDetectorRef, + public sanitizer: DomSanitizer, + ) {} + + ngOnInit(): void { + this.hostSubscription = this.stateService.serverHealth$.pipe( + map((hosts) => { + const subpath = window.location.pathname.slice(0, -6); + for (const host of hosts) { + let statusUrl = ''; + let linkHost = ''; + if (host.socket) { + statusUrl = 'https://' + window.location.hostname + subpath + '/status'; + linkHost = window.location.hostname + subpath; + } else { + const hostUrl = new URL(host.host); + statusUrl = 'https://' + hostUrl.hostname + subpath + '/status'; + linkHost = hostUrl.hostname + subpath; + } + host.statusPage = this.sanitizer.bypassSecurityTrustResourceUrl(this.sanitizer.sanitize(SecurityContext.URL, statusUrl)); + host.link = linkHost; + } + return hosts; + }), + tap((hosts) => { + if (this.hosts.length !== hosts.length) { + this.hosts = hosts.sort((a,b) => { + const aParts = (a.host?.split('.') || []).reverse(); + const bParts = (b.host?.split('.') || []).reverse(); + let i = 0; + while (i < Math.max(aParts.length, bParts.length)) { + if (aParts[i] && !bParts[i]) { + return 1; + } else if (bParts[i] && !aParts[i]) { + return -1; + } else if (aParts[i] !== bParts[i]) { + return aParts[i].localeCompare(bParts[i]); + } + i++; + } + return 0; + }); + } + this.cd.markForCheck(); + }) + ).subscribe(); + this.tip$ = this.stateService.chainTip$; + this.websocketService.want(['mempool-blocks', 'stats', 'blocks', 'tomahawk']); + } + + trackByFn(index: number, host: HealthCheckHost): string { + return host.host; + } + + ngOnDestroy(): void { + this.hosts = []; + this.hostSubscription.unsubscribe(); + } +} diff --git a/frontend/src/app/components/start/start.component.html b/frontend/src/app/components/start/start.component.html index 862baf80a..0f65bb412 100644 --- a/frontend/src/app/components/start/start.component.html +++ b/frontend/src/app/components/start/start.component.html @@ -2,8 +2,9 @@
-
-
+
+
+
{{ eventName }}
@@ -12,8 +13,6 @@
+ + + pixel + pixel + + diff --git a/frontend/src/app/components/start/start.component.scss b/frontend/src/app/components/start/start.component.scss index ec70506f2..e321b8d6f 100644 --- a/frontend/src/app/components/start/start.component.scss +++ b/frontend/src/app/components/start/start.component.scss @@ -11,16 +11,6 @@ transform: translateX(0px); transition: transform 0; - &.menu-open { - transform: translateX(-112.5px); - transition: transform 0.25s; - } - - &.menu-closing { - transform: translateX(0px); - transition: transform 0.25s; - } - &.with-menu { width: calc(100% + 120px); } @@ -144,52 +134,86 @@ body { overflow: hidden; } -.pyro > .before, .pyro > .after { +.pyro > .inner { z-index: 100; position: absolute; width: 5px; height: 5px; border-radius: 50%; - @include animation((1s bang ease-out infinite backwards, 1s gravity ease-in infinite backwards, 5s position linear infinite backwards)); + @include animation((1.25s ease-out infinite bang, 1.25s ease-in infinite gravity, 10s linear infinite position)); + + &.b { + @include animation-delay((-4.35s, -4.35s, -4.35s)); + @include animation-duration((1.45s, 1.45s, 11.6s)); + } + &.c { + @include animation-delay((-3.2s, -3.2s, -3.2s)); + @include animation-duration((1.6s, 1.6s, 12.8s)); + } } -.pyro > .after { - @include animation-delay((1.25s, 1.25s, 1.25s)); - @include animation-duration((1.25s, 1.25s, 6.25s)); -} - -@keyframes bang{ - to{ - box-shadow:-314.6666666667px -362.6666666667px red,-51.6666666667px 32.3333333333px #ff3700,-354.6666666667px -264.6666666667px #7b00ff,-319.6666666667px -73.6666666667px #00f7ff,-135.6666666667px -154.6666666667px #00ff48,57.3333333333px -402.6666666667px #0d00ff,-126.6666666667px -121.6666666667px #00ff7b,-335.6666666667px -5.6666666667px #00fff2,-291.6666666667px -.6666666667px #4f0,-126.6666666667px -187.6666666667px #7f0,-413.6666666667px -224.6666666667px #00ffbf,-283.6666666667px -391.6666666667px #00ff3c,-340.6666666667px -345.6666666667px #02f,-168.6666666667px -179.6666666667px #eaff00,7.3333333333px -153.6666666667px #26ff00,-175.6666666667px -234.6666666667px #8400ff,-324.6666666667px -254.6666666667px #0048ff,-335.6666666667px -9.6666666667px #00ff59,-304.6666666667px -8.6666666667px #001eff,-331.6666666667px -44.6666666667px #3f0,.3333333333px -49.6666666667px #0fc,-370.6666666667px -60.6666666667px #0015ff,29.3333333333px -13.6666666667px #8cff00,-168.6666666667px -281.6666666667px #f80,-48.6666666667px -61.6666666667px #f0b,33.3333333333px -113.6666666667px #ff00e1,-193.6666666667px -196.6666666667px #ff7b00,-14.6666666667px -24.6666666667px #ff0037,-149.6666666667px -273.6666666667px #0fa,-19.6666666667px -63.6666666667px #ff0004,13.3333333333px -227.6666666667px #7f0,-265.6666666667px -43.6666666667px #ff4800,-121.6666666667px -95.6666666667px #bfff00,-241.6666666667px -90.6666666667px #6200ff,-307.6666666667px -231.6666666667px #ff0062,78.3333333333px -128.6666666667px #ffbf00,27.3333333333px 44.3333333333px #95ff00,-81.6666666667px 6.3333333333px #ffc800,-343.6666666667px -247.6666666667px #2f0,-225.6666666667px -250.6666666667px #08f,-9.6666666667px -243.6666666667px #ff1a00,83.3333333333px -409.6666666667px #04f,-380.6666666667px -331.6666666667px #84ff00,-103.6666666667px -51.6666666667px #f02,-174.6666666667px -169.6666666667px #ffc800,20.3333333333px -191.6666666667px #ff0059,-40.6666666667px -55.6666666667px #0400ff,-199.6666666667px -66.6666666667px #ffd500,-358.6666666667px -5.6666666667px #0051ff,-84.6666666667px -289.6666666667px #f7ff00,-193.6666666667px -184.6666666667px #80f +@keyframes bang { + 0%, 15% { + box-sizing: none; + } + 84% { + box-shadow: -30.55vw -35.21vw red,-5.02vw 3.14vw #ff3700,-34.43vw -25.70vw #7b00ff,-31.04vw -7.15vw #00f7ff,-13.17vw -15.02vw #00ff48,5.57vw -39.09vw #0d00ff,-12.30vw -11.81vw #00ff7b,-32.59vw -0.55vw #00fff2,-28.32vw -.6666666667px #4f0,-12.30vw -18.22vw #7f0,-40.16vw -21.81vw #00ffbf,-27.54vw -38.03vw #00ff3c,-33.07vw -33.56vw #02f,-16.38vw -17.44vw #eaff00,0.71vw -14.92vw #26ff00,-17.06vw -22.78vw #8400ff,-31.52vw -24.72vw #0048ff,-32.59vw -0.94vw #00ff59,-29.58vw -0.84vw #001eff,-32.20vw -4.34vw #3f0,.3333333333px -4.82vw #0fc,-35.99vw -5.89vw #0015ff,2.85vw -1.33vw #8cff00,-16.38vw -27.35vw #f80,-4.72vw -5.99vw #f0b,3.24vw -11.04vw #ff00e1,-18.80vw -19.09vw #ff7b00,-1.42vw -2.39vw #ff0037,-14.53vw -26.57vw #0fa,-1.91vw -6.18vw #ff0004,1.29vw -22.10vw #7f0,-25.79vw -4.24vw #ff4800,-11.81vw -9.29vw #bfff00,-23.46vw -8.80vw #6200ff,-29.87vw -22.49vw #ff0062,7.61vw -12.49vw #ffbf00,2.65vw 4.30vw #95ff00,-7.93vw 0.61vw #ffc800,-33.37vw -24.05vw #2f0,-21.91vw -24.34vw #08f,-0.94vw -23.66vw #ff1a00,8.09vw -39.77vw #04f,-36.96vw -32.20vw #84ff00,-10.06vw -5.02vw #f02,-16.96vw -16.47vw #ffc800,1.97vw -18.61vw #ff0059,-3.95vw -5.40vw #0400ff,-19.39vw -6.47vw #ffd500,-34.82vw -0.55vw #0051ff,-8.22vw -28.12vw #f7ff00,-18.80vw -17.93vw #80f; + } + 85%, 100% { + box-sizing: none; } } @include keyframes(gravity) { - to { + 0% { + @include transform(translateY(0px)); + opacity: 0; + } + 15% { + @include transform(translateY(0px)); + opacity: 1; + } + 84% { + @include transform(translateY(400px)); + opacity: 0; + } + 85%, 100% { @include transform(translateY(200px)); opacity: 0; } } @include keyframes(position) { - 0%, 19.9% { - margin-top: 10%; - margin-left: 40%; + 0%, 12.4% { + margin-top: 10vw; + margin-left: 50%; } - 20%, 39.9% { - margin-top: 40%; - margin-left: 30%; + 12.5%, 24.9% { + margin-top: 22vw; + margin-left: 35%; } - 40%, 59.9% { - margin-top: 20%; - margin-left: 70% + 25%, 37.4% { + margin-top: 15vw; + margin-left: 80% } - 60%, 79.9% { - margin-top: 30%; - margin-left: 20%; + 37.5%, 49.9% { + margin-top: 28vw; + margin-left: 72%; } - 80%, 99.9% { - margin-top: 30%; - margin-left: 80%; + 50%, 62.4% { + margin-top: 22vw; + margin-left: 37%; + } + 62.5%, 74.9% { + margin-top: 10vw; + margin-left: 66%; + } + 75%, 87.4% { + margin-top: 25vw; + margin-left: 49%; + } + 87.5%, 99.9% { + margin-top: 18vw; + margin-left: 58%; } } diff --git a/frontend/src/app/components/start/start.component.ts b/frontend/src/app/components/start/start.component.ts index 1326f7119..0a4943bde 100644 --- a/frontend/src/app/components/start/start.component.ts +++ b/frontend/src/app/components/start/start.component.ts @@ -24,7 +24,7 @@ export class StartComponent implements OnInit, AfterViewChecked, OnDestroy { timeLtrSubscription: Subscription; timeLtr: boolean = this.stateService.timeLtr.value; chainTipSubscription: Subscription; - chainTip: number = 100; + chainTip: number = -1; tipIsSet: boolean = false; lastMark: MarkBlockState; markBlockSubscription: Subscription; @@ -32,7 +32,6 @@ export class StartComponent implements OnInit, AfterViewChecked, OnDestroy { @ViewChild('blockchainWrapper', { static: true }) blockchainWrapper: ElementRef; @ViewChild('blockchainContainer') blockchainContainer: ElementRef; resetScrollSubscription: Subscription; - menuSubscription: Subscription; isMobile: boolean = false; isiOS: boolean = false; @@ -56,14 +55,11 @@ export class StartComponent implements OnInit, AfterViewChecked, OnDestroy { scrollLeft: number = null; chainWidth: number = window.innerWidth; - menuOpen: boolean = false; - menuSliding: boolean = false; - menuTimeout: number; hasMenu = false; constructor( - private stateService: StateService, + public stateService: StateService, private cd: ChangeDetectorRef, ) { this.isiOS = ['iPhone','iPod','iPad'].includes((navigator as any)?.userAgentData?.platform || navigator.platform); @@ -147,12 +143,15 @@ export class StartComponent implements OnInit, AfterViewChecked, OnDestroy { } } } - if (specialBlocks[block.height] && specialBlocks[block.height].networks.includes(this.stateService.network || 'mainnet')) { - this.specialEvent = true; - this.eventName = specialBlocks[block.height].labelEventCompleted; - setTimeout(() => { + for (const block of blocks) { + if (specialBlocks[block.height] && specialBlocks[block.height].networks.includes(this.stateService.network || 'mainnet')) { + this.specialEvent = true; + this.eventName = specialBlocks[block.height].labelEventCompleted; + } + if (specialBlocks[block.height - 8] && specialBlocks[block.height - 8].networks.includes(this.stateService.network || 'mainnet')) { this.specialEvent = false; - }, 60 * 60 * 1000); + this.eventName = ''; + } } }); this.resetScrollSubscription = this.stateService.resetScroll$.subscribe(reset => { @@ -162,12 +161,6 @@ export class StartComponent implements OnInit, AfterViewChecked, OnDestroy { } }); - this.menuSubscription = this.stateService.menuOpen$.subscribe((open) => { - if (this.menuOpen !== open) { - this.menuOpen = open; - this.applyMenuScroll(this.menuOpen); - } - }); } ngAfterViewChecked(): void { @@ -220,15 +213,6 @@ export class StartComponent implements OnInit, AfterViewChecked, OnDestroy { } } - applyMenuScroll(opening: boolean): void { - this.menuSliding = true; - window.clearTimeout(this.menuTimeout); - this.menuTimeout = window.setTimeout(() => { - this.menuSliding = false; - this.cd.markForCheck(); - }, 300); - } - @HostListener('window:resize', ['$event']) onResize(): void { this.chainWidth = window.innerWidth; @@ -515,6 +499,5 @@ export class StartComponent implements OnInit, AfterViewChecked, OnDestroy { this.markBlockSubscription.unsubscribe(); this.blockCounterSubscription.unsubscribe(); this.resetScrollSubscription.unsubscribe(); - this.menuSubscription.unsubscribe(); } } diff --git a/frontend/src/app/components/svg-images/svg-images.component.html b/frontend/src/app/components/svg-images/svg-images.component.html index 11dfe1d79..3fe3f56c8 100644 --- a/frontend/src/app/components/svg-images/svg-images.component.html +++ b/frontend/src/app/components/svg-images/svg-images.component.html @@ -60,9 +60,6 @@ - - - diff --git a/frontend/src/app/components/terms-of-service/terms-of-service.component.html b/frontend/src/app/components/terms-of-service/terms-of-service.component.html index c03f509e9..922ce9434 100644 --- a/frontend/src/app/components/terms-of-service/terms-of-service.component.html +++ b/frontend/src/app/components/terms-of-service/terms-of-service.component.html @@ -11,7 +11,7 @@
-

The mempool.space website, the liquid.network website, the bisq.markets website, their associated API services, and related network and server infrastructure (collectively, the "Website") are operated by Mempool Space K.K. in Japan ("Mempool", "We", or "Us") and self-hosted from AS142052.

+

The mempool.space website, the liquid.network website, their associated API services, and related network and server infrastructure (collectively, the "Website") are operated by Mempool Space K.K. in Japan ("Mempool", "We", or "Us") and self-hosted from AS142052.

This website and its API service (collectively, the "Website") are operated by a member of the Bitcoin community ("We" or "Us"). Mempool Space K.K. in Japan ("Mempool") has no affiliation with the operator of this Website, and does not sponsor or endorse the information provided herein.

@@ -61,7 +61,7 @@

The mempool.space explorer is Bitcoin Only.

-

Unfortunately, the liquid.network and bisq.markets explorers cannot be considered Bitcoin Only, as they may display altcoins as part of their core network functionality. We do not endorse any altcoins and encourage you to stay Bitcoin Only. Please see bitcoin-only.com for more Bitcoin Only resources.

+

Unfortunately, the liquid.network explorer cannot be considered Bitcoin Only, as they may display altcoins as part of their core network functionality. We do not endorse any altcoins and encourage you to stay Bitcoin Only. Please see bitcoin-only.com for more Bitcoin Only resources.


diff --git a/frontend/src/app/components/terms-of-service/terms-of-service.component.ts b/frontend/src/app/components/terms-of-service/terms-of-service.component.ts index 708ebad76..71a86c759 100644 --- a/frontend/src/app/components/terms-of-service/terms-of-service.component.ts +++ b/frontend/src/app/components/terms-of-service/terms-of-service.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { Env, StateService } from '../../services/state.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; @Component({ selector: 'app-terms-of-service', @@ -12,10 +13,12 @@ export class TermsOfServiceComponent { constructor( private stateService: StateService, private seoService: SeoService, + private ogService: OpenGraphService, ) { } ngOnInit(): void { this.seoService.setTitle('Terms of Service'); this.seoService.setDescription('Out of respect for the Bitcoin community, the mempool.space website is Bitcoin Only and does not display any advertising.'); + this.ogService.setManualOgImage('tos.jpg'); } } diff --git a/frontend/src/app/components/time/time.component.ts b/frontend/src/app/components/time/time.component.ts index 2febf6238..954e3d157 100644 --- a/frontend/src/app/components/time/time.component.ts +++ b/frontend/src/app/components/time/time.component.ts @@ -10,7 +10,6 @@ import { dates } from '../../shared/i18n/dates'; export class TimeComponent implements OnInit, OnChanges, OnDestroy { interval: number; text: string; - units: string[] = ['year', 'month', 'week', 'day', 'hour', 'minute', 'second']; precisionThresholds = { year: 100, month: 18, @@ -24,11 +23,13 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy { @Input() time: number; @Input() dateString: number; - @Input() kind: 'plain' | 'since' | 'until' | 'span' = 'plain'; + @Input() kind: 'plain' | 'since' | 'until' | 'span' | 'before' = 'plain'; @Input() fastRender = false; @Input() fixedRender = false; @Input() relative = false; @Input() precision: number = 0; + @Input() numUnits: number = 1; + @Input() units: string[] = ['year', 'month', 'week', 'day', 'hour', 'minute', 'second']; @Input() minUnit: 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' = 'second'; @Input() fractionDigits: number = 0; @@ -85,7 +86,9 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy { seconds = Math.floor(this.time); } - if (seconds < 60) { + if (seconds < 1 && this.kind === 'span') { + return $localize`:@@date-base.immediately:Immediately`; + } else if (seconds < 60) { if (this.relative || this.kind === 'since') { return $localize`:@@date-base.just-now:Just now`; } else if (this.kind === 'until') { @@ -94,6 +97,8 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy { } let counter: number; + const result = []; + let usedUnits = 0; for (const [index, unit] of this.units.entries()) { let precisionUnit = this.units[Math.min(this.units.length - 1, index + this.precision)]; counter = Math.floor(seconds / this.intervals[unit]); @@ -105,107 +110,149 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy { counter = Math.max(1, counter); } if (counter > 0) { - let rounded = Math.round(seconds / this.intervals[precisionUnit]); - if (this.fractionDigits) { - const roundFactor = Math.pow(10,this.fractionDigits); + let rounded; + const roundFactor = Math.pow(10,this.fractionDigits || 0); + if (this.kind === 'until' && usedUnits < this.numUnits) { + rounded = Math.floor((seconds / this.intervals[precisionUnit]) * roundFactor) / roundFactor; + } else { rounded = Math.round((seconds / this.intervals[precisionUnit]) * roundFactor) / roundFactor; } - const dateStrings = dates(rounded); - switch (this.kind) { - case 'since': - if (rounded === 1) { - switch (precisionUnit) { // singular (1 day) - case 'year': return $localize`:@@time-since:${dateStrings.i18nYear}:DATE: ago`; break; - case 'month': return $localize`:@@time-since:${dateStrings.i18nMonth}:DATE: ago`; break; - case 'week': return $localize`:@@time-since:${dateStrings.i18nWeek}:DATE: ago`; break; - case 'day': return $localize`:@@time-since:${dateStrings.i18nDay}:DATE: ago`; break; - case 'hour': return $localize`:@@time-since:${dateStrings.i18nHour}:DATE: ago`; break; - case 'minute': return $localize`:@@time-since:${dateStrings.i18nMinute}:DATE: ago`; break; - case 'second': return $localize`:@@time-since:${dateStrings.i18nSecond}:DATE: ago`; break; - } - } else { - switch (precisionUnit) { // plural (2 days) - case 'year': return $localize`:@@time-since:${dateStrings.i18nYears}:DATE: ago`; break; - case 'month': return $localize`:@@time-since:${dateStrings.i18nMonths}:DATE: ago`; break; - case 'week': return $localize`:@@time-since:${dateStrings.i18nWeeks}:DATE: ago`; break; - case 'day': return $localize`:@@time-since:${dateStrings.i18nDays}:DATE: ago`; break; - case 'hour': return $localize`:@@time-since:${dateStrings.i18nHours}:DATE: ago`; break; - case 'minute': return $localize`:@@time-since:${dateStrings.i18nMinutes}:DATE: ago`; break; - case 'second': return $localize`:@@time-since:${dateStrings.i18nSeconds}:DATE: ago`; break; - } - } - break; - case 'until': - if (rounded === 1) { - switch (precisionUnit) { // singular (In ~1 day) - case 'year': return $localize`:@@time-until:In ~${dateStrings.i18nYear}:DATE:`; break; - case 'month': return $localize`:@@time-until:In ~${dateStrings.i18nMonth}:DATE:`; break; - case 'week': return $localize`:@@time-until:In ~${dateStrings.i18nWeek}:DATE:`; break; - case 'day': return $localize`:@@time-until:In ~${dateStrings.i18nDay}:DATE:`; break; - case 'hour': return $localize`:@@time-until:In ~${dateStrings.i18nHour}:DATE:`; break; - case 'minute': return $localize`:@@time-until:In ~${dateStrings.i18nMinute}:DATE:`; - case 'second': return $localize`:@@time-until:In ~${dateStrings.i18nSecond}:DATE:`; - } - } else { - switch (precisionUnit) { // plural (In ~2 days) - case 'year': return $localize`:@@time-until:In ~${dateStrings.i18nYears}:DATE:`; break; - case 'month': return $localize`:@@time-until:In ~${dateStrings.i18nMonths}:DATE:`; break; - case 'week': return $localize`:@@time-until:In ~${dateStrings.i18nWeeks}:DATE:`; break; - case 'day': return $localize`:@@time-until:In ~${dateStrings.i18nDays}:DATE:`; break; - case 'hour': return $localize`:@@time-until:In ~${dateStrings.i18nHours}:DATE:`; break; - case 'minute': return $localize`:@@time-until:In ~${dateStrings.i18nMinutes}:DATE:`; break; - case 'second': return $localize`:@@time-until:In ~${dateStrings.i18nSeconds}:DATE:`; break; - } - } - break; - case 'span': - if (rounded === 1) { - switch (precisionUnit) { // singular (1 day) - case 'year': return $localize`:@@time-span:After ${dateStrings.i18nYear}:DATE:`; break; - case 'month': return $localize`:@@time-span:After ${dateStrings.i18nMonth}:DATE:`; break; - case 'week': return $localize`:@@time-span:After ${dateStrings.i18nWeek}:DATE:`; break; - case 'day': return $localize`:@@time-span:After ${dateStrings.i18nDay}:DATE:`; break; - case 'hour': return $localize`:@@time-span:After ${dateStrings.i18nHour}:DATE:`; break; - case 'minute': return $localize`:@@time-span:After ${dateStrings.i18nMinute}:DATE:`; break; - case 'second': return $localize`:@@time-span:After ${dateStrings.i18nSecond}:DATE:`; break; - } - } else { - switch (precisionUnit) { // plural (2 days) - case 'year': return $localize`:@@time-span:After ${dateStrings.i18nYears}:DATE:`; break; - case 'month': return $localize`:@@time-span:After ${dateStrings.i18nMonths}:DATE:`; break; - case 'week': return $localize`:@@time-span:After ${dateStrings.i18nWeeks}:DATE:`; break; - case 'day': return $localize`:@@time-span:After ${dateStrings.i18nDays}:DATE:`; break; - case 'hour': return $localize`:@@time-span:After ${dateStrings.i18nHours}:DATE:`; break; - case 'minute': return $localize`:@@time-span:After ${dateStrings.i18nMinutes}:DATE:`; break; - case 'second': return $localize`:@@time-span:After ${dateStrings.i18nSeconds}:DATE:`; break; - } - } - break; - default: - if (rounded === 1) { - switch (precisionUnit) { // singular (1 day) - case 'year': return dateStrings.i18nYear; break; - case 'month': return dateStrings.i18nMonth; break; - case 'week': return dateStrings.i18nWeek; break; - case 'day': return dateStrings.i18nDay; break; - case 'hour': return dateStrings.i18nHour; break; - case 'minute': return dateStrings.i18nMinute; break; - case 'second': return dateStrings.i18nSecond; break; - } - } else { - switch (precisionUnit) { // plural (2 days) - case 'year': return dateStrings.i18nYears; break; - case 'month': return dateStrings.i18nMonths; break; - case 'week': return dateStrings.i18nWeeks; break; - case 'day': return dateStrings.i18nDays; break; - case 'hour': return dateStrings.i18nHours; break; - case 'minute': return dateStrings.i18nMinutes; break; - case 'second': return dateStrings.i18nSeconds; break; - } - } + if (this.kind !== 'until' || this.numUnits === 1) { + return this.formatTime(this.kind, precisionUnit, rounded); + } else { + if (!usedUnits) { + result.push(this.formatTime(this.kind, precisionUnit, rounded)); + } else { + result.push(this.formatTime('', precisionUnit, rounded)); + } + seconds -= (rounded * this.intervals[precisionUnit]); + usedUnits++; + if (usedUnits >= this.numUnits) { + return result.join(', '); + } } } } + return result.join(', '); } + private formatTime(kind, unit, number): string { + const dateStrings = dates(number); + switch (kind) { + case 'since': + if (number === 1) { + switch (unit) { // singular (1 day) + case 'year': return $localize`:@@time-since:${dateStrings.i18nYear}:DATE: ago`; break; + case 'month': return $localize`:@@time-since:${dateStrings.i18nMonth}:DATE: ago`; break; + case 'week': return $localize`:@@time-since:${dateStrings.i18nWeek}:DATE: ago`; break; + case 'day': return $localize`:@@time-since:${dateStrings.i18nDay}:DATE: ago`; break; + case 'hour': return $localize`:@@time-since:${dateStrings.i18nHour}:DATE: ago`; break; + case 'minute': return $localize`:@@time-since:${dateStrings.i18nMinute}:DATE: ago`; break; + case 'second': return $localize`:@@time-since:${dateStrings.i18nSecond}:DATE: ago`; break; + } + } else { + switch (unit) { // plural (2 days) + case 'year': return $localize`:@@time-since:${dateStrings.i18nYears}:DATE: ago`; break; + case 'month': return $localize`:@@time-since:${dateStrings.i18nMonths}:DATE: ago`; break; + case 'week': return $localize`:@@time-since:${dateStrings.i18nWeeks}:DATE: ago`; break; + case 'day': return $localize`:@@time-since:${dateStrings.i18nDays}:DATE: ago`; break; + case 'hour': return $localize`:@@time-since:${dateStrings.i18nHours}:DATE: ago`; break; + case 'minute': return $localize`:@@time-since:${dateStrings.i18nMinutes}:DATE: ago`; break; + case 'second': return $localize`:@@time-since:${dateStrings.i18nSeconds}:DATE: ago`; break; + } + } + break; + case 'until': + if (number === 1) { + switch (unit) { // singular (In ~1 day) + case 'year': return $localize`:@@time-until:In ~${dateStrings.i18nYear}:DATE:`; break; + case 'month': return $localize`:@@time-until:In ~${dateStrings.i18nMonth}:DATE:`; break; + case 'week': return $localize`:@@time-until:In ~${dateStrings.i18nWeek}:DATE:`; break; + case 'day': return $localize`:@@time-until:In ~${dateStrings.i18nDay}:DATE:`; break; + case 'hour': return $localize`:@@time-until:In ~${dateStrings.i18nHour}:DATE:`; break; + case 'minute': return $localize`:@@time-until:In ~${dateStrings.i18nMinute}:DATE:`; + case 'second': return $localize`:@@time-until:In ~${dateStrings.i18nSecond}:DATE:`; + } + } else { + switch (unit) { // plural (In ~2 days) + case 'year': return $localize`:@@time-until:In ~${dateStrings.i18nYears}:DATE:`; break; + case 'month': return $localize`:@@time-until:In ~${dateStrings.i18nMonths}:DATE:`; break; + case 'week': return $localize`:@@time-until:In ~${dateStrings.i18nWeeks}:DATE:`; break; + case 'day': return $localize`:@@time-until:In ~${dateStrings.i18nDays}:DATE:`; break; + case 'hour': return $localize`:@@time-until:In ~${dateStrings.i18nHours}:DATE:`; break; + case 'minute': return $localize`:@@time-until:In ~${dateStrings.i18nMinutes}:DATE:`; break; + case 'second': return $localize`:@@time-until:In ~${dateStrings.i18nSeconds}:DATE:`; break; + } + } + break; + case 'span': + if (number === 1) { + switch (unit) { // singular (1 day) + case 'year': return $localize`:@@time-span:After ${dateStrings.i18nYear}:DATE:`; break; + case 'month': return $localize`:@@time-span:After ${dateStrings.i18nMonth}:DATE:`; break; + case 'week': return $localize`:@@time-span:After ${dateStrings.i18nWeek}:DATE:`; break; + case 'day': return $localize`:@@time-span:After ${dateStrings.i18nDay}:DATE:`; break; + case 'hour': return $localize`:@@time-span:After ${dateStrings.i18nHour}:DATE:`; break; + case 'minute': return $localize`:@@time-span:After ${dateStrings.i18nMinute}:DATE:`; break; + case 'second': return $localize`:@@time-span:After ${dateStrings.i18nSecond}:DATE:`; break; + } + } else { + switch (unit) { // plural (2 days) + case 'year': return $localize`:@@time-span:After ${dateStrings.i18nYears}:DATE:`; break; + case 'month': return $localize`:@@time-span:After ${dateStrings.i18nMonths}:DATE:`; break; + case 'week': return $localize`:@@time-span:After ${dateStrings.i18nWeeks}:DATE:`; break; + case 'day': return $localize`:@@time-span:After ${dateStrings.i18nDays}:DATE:`; break; + case 'hour': return $localize`:@@time-span:After ${dateStrings.i18nHours}:DATE:`; break; + case 'minute': return $localize`:@@time-span:After ${dateStrings.i18nMinutes}:DATE:`; break; + case 'second': return $localize`:@@time-span:After ${dateStrings.i18nSeconds}:DATE:`; break; + } + } + break; + case 'before': + if (number === 1) { + switch (unit) { // singular (1 day) + case 'year': return $localize`:@@time-span:${dateStrings.i18nYear}:DATE: before`; break; + case 'month': return $localize`:@@time-span:${dateStrings.i18nMonth}:DATE: before`; break; + case 'week': return $localize`:@@time-span:${dateStrings.i18nWeek}:DATE: before`; break; + case 'day': return $localize`:@@time-span:${dateStrings.i18nDay}:DATE: before`; break; + case 'hour': return $localize`:@@time-span:${dateStrings.i18nHour}:DATE: before`; break; + case 'minute': return $localize`:@@time-span:${dateStrings.i18nMinute}:DATE: before`; break; + case 'second': return $localize`:@@time-span:${dateStrings.i18nSecond}:DATE: before`; break; + } + } else { + switch (unit) { // plural (2 days) + case 'year': return $localize`:@@time-span:${dateStrings.i18nYears}:DATE: before`; break; + case 'month': return $localize`:@@time-span:${dateStrings.i18nMonths}:DATE: before`; break; + case 'week': return $localize`:@@time-span:${dateStrings.i18nWeeks}:DATE: before`; break; + case 'day': return $localize`:@@time-span:${dateStrings.i18nDays}:DATE: before`; break; + case 'hour': return $localize`:@@time-span:${dateStrings.i18nHours}:DATE: before`; break; + case 'minute': return $localize`:@@time-span:${dateStrings.i18nMinutes}:DATE: before`; break; + case 'second': return $localize`:@@time-span:${dateStrings.i18nSeconds}:DATE: before`; break; + } + } + break; + default: + if (number === 1) { + switch (unit) { // singular (1 day) + case 'year': return dateStrings.i18nYear; break; + case 'month': return dateStrings.i18nMonth; break; + case 'week': return dateStrings.i18nWeek; break; + case 'day': return dateStrings.i18nDay; break; + case 'hour': return dateStrings.i18nHour; break; + case 'minute': return dateStrings.i18nMinute; break; + case 'second': return dateStrings.i18nSecond; break; + } + } else { + switch (unit) { // plural (2 days) + case 'year': return dateStrings.i18nYears; break; + case 'month': return dateStrings.i18nMonths; break; + case 'week': return dateStrings.i18nWeeks; break; + case 'day': return dateStrings.i18nDays; break; + case 'hour': return dateStrings.i18nHours; break; + case 'minute': return dateStrings.i18nMinutes; break; + case 'second': return dateStrings.i18nSeconds; break; + } + } + } + } } diff --git a/frontend/src/app/components/trademark-policy/trademark-policy.component.html b/frontend/src/app/components/trademark-policy/trademark-policy.component.html index 54212ebd4..cc1667e84 100644 --- a/frontend/src/app/components/trademark-policy/trademark-policy.component.html +++ b/frontend/src/app/components/trademark-policy/trademark-policy.component.html @@ -326,7 +326,7 @@
-

If you have any questions about this Policy, would like to speak with us about the use of our Marks in ways not described in the Policy, or see any abuse of our Marks, please email us at <legal@mempool.space>

+

If you have any questions about this Policy, would like to speak with us about the use of our Marks in ways not described in the Policy, or see any abuse of our Marks, please email us at <legal@mempool.space>

diff --git a/frontend/src/app/components/trademark-policy/trademark-policy.component.ts b/frontend/src/app/components/trademark-policy/trademark-policy.component.ts index b8f53afcf..ad8b6b372 100644 --- a/frontend/src/app/components/trademark-policy/trademark-policy.component.ts +++ b/frontend/src/app/components/trademark-policy/trademark-policy.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { Env, StateService } from '../../services/state.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; @Component({ selector: 'app-trademark-policy', @@ -13,10 +14,12 @@ export class TrademarkPolicyComponent { constructor( private stateService: StateService, private seoService: SeoService, + private ogService: OpenGraphService, ) { } ngOnInit(): void { this.seoService.setTitle('Trademark Policy'); this.seoService.setDescription('An overview of the trademarks registered by Mempool Space K.K. and The Mempool Open Source Project® and what we consider to be lawful usage of those trademarks.'); + this.ogService.setManualOgImage('trademark-policy.jpg'); } } diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index b283e0d23..d211edaf2 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -70,6 +70,24 @@
Audit + + Coinbase + Expected in Block + Seen in Mempool + Not seen in Mempool + Added + Prioritized + Conflict + + + +
@@ -133,7 +151,7 @@ - +
Accelerate @@ -147,6 +165,7 @@ +
@@ -498,9 +517,9 @@
-
-

{{ error.error }}

-
+ + Error loading transaction data. +
@@ -509,10 +528,10 @@ - + - + @@ -534,15 +553,37 @@ - -   - Accelerated - - + + + + + + +
Fee{{ tx.fee | number }} sat {{ tx.fee | number }} sat
Fee rate
Miner + + {{ pool.name }} + + +
+ + + + + + + + Accelerated + + {{ filter.label }} + + + + diff --git a/frontend/src/app/components/transaction/transaction.component.scss b/frontend/src/app/components/transaction/transaction.component.scss index d78edf85b..bfdd4cc03 100644 --- a/frontend/src/app/components/transaction/transaction.component.scss +++ b/frontend/src/app/components/transaction/transaction.component.scss @@ -149,6 +149,10 @@ .btn { display: block; } + + &.wrap-cell { + white-space: normal; + } } } @@ -283,7 +287,7 @@ .etaDeepMempool { display: flex !important; - justify-content: end; + justify-content: flex-end; flex-wrap: wrap; align-content: center; @media (max-width: 995px) { @@ -306,3 +310,8 @@ margin-left: 5px; } } + +.goggles-icon { + display: block; + width: 2.2em; +} \ No newline at end of file diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index ace4ded37..93fb97fac 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, AfterViewInit, OnDestroy, HostListener, ViewChild, ElementRef } from '@angular/core'; +import { Component, OnInit, AfterViewInit, OnDestroy, HostListener, ViewChild, ElementRef, Inject, ChangeDetectorRef } from '@angular/core'; import { ElectrsApiService } from '../../services/electrs-api.service'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { @@ -7,12 +7,12 @@ import { catchError, retryWhen, delay, - map, mergeMap, - tap + tap, + map } from 'rxjs/operators'; import { Transaction } from '../../interfaces/electrs.interface'; -import { of, merge, Subscription, Observable, Subject, from, throwError } from 'rxjs'; +import { of, merge, Subscription, Observable, Subject, from, throwError, combineLatest } from 'rxjs'; import { StateService } from '../../services/state.service'; import { CacheService } from '../../services/cache.service'; import { WebsocketService } from '../../services/websocket.service'; @@ -21,11 +21,33 @@ import { ApiService } from '../../services/api.service'; import { SeoService } from '../../services/seo.service'; import { StorageService } from '../../services/storage.service'; import { seoDescriptionNetwork } from '../../shared/common.utils'; +import { getTransactionFlags } from '../../shared/transaction.utils'; +import { Filter, toFilters, TransactionFlags } from '../../shared/filters.utils'; import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition, DifficultyAdjustment, Acceleration } from '../../interfaces/node-api.interface'; import { LiquidUnblinding } from './liquid-ublinding'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { Price, PriceService } from '../../services/price.service'; import { isFeatureActive } from '../../bitcoin.utils'; +import { ServicesApiServices } from '../../services/services-api.service'; +import { EnterpriseService } from '../../services/enterprise.service'; +import { ZONE_SERVICE } from '../../injection-tokens'; + +interface Pool { + id: number; + name: string; + slug: string; +} + +interface AuditStatus { + seen?: boolean; + expected?: boolean; + added?: boolean; + prioritized?: boolean; + delayed?: number; + accelerated?: boolean; + conflict?: boolean; + coinbase?: boolean; +} @Component({ selector: 'app-transaction', @@ -57,6 +79,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { urlFragmentSubscription: Subscription; mempoolBlocksSubscription: Subscription; blocksSubscription: Subscription; + miningSubscription: Subscription; + currencyChangeSubscription: Subscription; fragmentParams: URLSearchParams; rbfTransaction: undefined | Transaction; replaced: boolean = false; @@ -66,11 +90,15 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { accelerationInfo: Acceleration | null = null; sigops: number | null; adjustedVsize: number | null; + pool: Pool | null; + auditStatus: AuditStatus | null; + filters: Filter[] = []; showCpfpDetails = false; fetchCpfp$ = new Subject(); fetchRbfHistory$ = new Subject(); fetchCachedTx$ = new Subject(); fetchAcceleration$ = new Subject(); + fetchMiningInfo$ = new Subject<{ hash: string, height: number, txid: string }>(); isCached: boolean = false; now = Date.now(); da$: Observable; @@ -78,7 +106,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { inputIndex: number; outputIndex: number; graphExpanded: boolean = false; - graphWidth: number = 1000; + graphWidth: number = 1068; graphHeight: number = 360; inOutLimit: number = 150; maxInOut: number = 0; @@ -86,7 +114,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { hideFlow: boolean = this.stateService.hideFlow.value; overrideFlowPreference: boolean = null; flowEnabled: boolean; - blockConversion: Price; tooltipPosition: { x: number, y: number }; isMobile: boolean; @@ -99,6 +126,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { acceleratorAvailable: boolean = this.stateService.env.OFFICIAL_MEMPOOL_SPACE && this.stateService.env.ACCELERATOR && this.stateService.network === ''; showAccelerationSummary = false; scrollIntoAccelPreview = false; + auditEnabled: boolean = this.stateService.env.AUDIT && this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true; @ViewChild('graphContainer') graphContainer: ElementRef; @@ -113,14 +141,20 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { private websocketService: WebsocketService, private audioService: AudioService, private apiService: ApiService, + private servicesApiService: ServicesApiServices, private seoService: SeoService, private priceService: PriceService, - private storageService: StorageService + private storageService: StorageService, + private enterpriseService: EnterpriseService, + private cd: ChangeDetectorRef, + @Inject(ZONE_SERVICE) private zoneService: any, ) {} ngOnInit() { this.acceleratorAvailable = this.stateService.env.OFFICIAL_MEMPOOL_SPACE && this.stateService.env.ACCELERATOR && this.stateService.network === ''; + this.enterpriseService.page(); + this.websocketService.want(['blocks', 'mempool-blocks']); this.stateService.networkChanged$.subscribe( (network) => { @@ -247,20 +281,70 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.accelerationInfo = null; }), switchMap((blockHash: string) => { - return this.apiService.getAccelerationHistory$({ blockHash }); + return this.servicesApiService.getAccelerationHistory$({ blockHash }); }), catchError(() => { return of(null); }) ).subscribe((accelerationHistory) => { for (const acceleration of accelerationHistory) { - if (acceleration.txid === this.txId && (acceleration.status === 'completed' || acceleration.status === 'mined') && acceleration.feePaid > 0) { + if (acceleration.txid === this.txId && (acceleration.status === 'completed' || acceleration.status === 'completed_provisional')) { acceleration.acceleratedFee = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + acceleration.feePaid - acceleration.baseFee - acceleration.vsizeFee); this.accelerationInfo = acceleration; } } }); + this.miningSubscription = this.fetchMiningInfo$.pipe( + filter((target) => target.txid === this.txId), + tap(() => { + this.pool = null; + this.auditStatus = null; + }), + switchMap(({ hash, height, txid }) => { + const foundBlock = this.cacheService.getCachedBlock(height) || null; + const auditAvailable = this.isAuditAvailable(height); + const isCoinbase = this.tx.vin.some(v => v.is_coinbase); + const fetchAudit = auditAvailable && !isCoinbase; + return combineLatest([ + foundBlock ? of(foundBlock.extras.pool) : this.apiService.getBlock$(hash).pipe( + map(block => { + return block.extras.pool; + }), + catchError(() => { + return of(null); + }) + ), + fetchAudit ? this.apiService.getBlockAudit$(hash).pipe( + map(audit => { + const isAdded = audit.addedTxs.includes(txid); + const isPrioritized = audit.prioritizedTxs.includes(txid); + const isAccelerated = audit.acceleratedTxs.includes(txid); + const isConflict = audit.fullrbfTxs.includes(txid); + const isExpected = audit.template.some(tx => tx.txid === txid); + return { + seen: isExpected || isPrioritized || isAccelerated, + expected: isExpected, + added: isAdded, + prioritized: isPrioritized, + conflict: isConflict, + accelerated: isAccelerated, + }; + }), + catchError(() => { + return of(null); + }) + ) : of(isCoinbase ? { coinbase: true } : null) + ]); + }), + catchError(() => { + return of(null); + }) + ).subscribe(([pool, auditStatus]) => { + this.pool = pool; + this.auditStatus = auditStatus; + }); + this.mempoolPositionSubscription = this.stateService.mempoolTxPosition$.subscribe(txPosition => { this.now = Date.now(); if (txPosition && txPosition.txid === this.txId && txPosition.position) { @@ -281,7 +365,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { } }); - this.subscription = this.route.paramMap + this.subscription = this.zoneService.wrapObservable(this.route.paramMap .pipe( switchMap((params: ParamMap) => { const urlMatch = (params.get('id') || '').split(':'); @@ -355,7 +439,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { } return of(tx); }) - ) + )) .subscribe((tx: Transaction) => { if (!tx) { this.fetchCachedTx$.next(this.txId); @@ -391,6 +475,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { } } else { this.fetchAcceleration$.next(tx.status.block_hash); + this.fetchMiningInfo$.next({ hash: tx.status.block_hash, height: tx.status.block_height, txid: tx.txid }); this.transactionTime = 0; } @@ -417,14 +502,18 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { } } this.fetchRbfHistory$.next(this.tx.txid); - - this.priceService.getBlockPrice$(tx.status?.block_time, true).pipe( - tap((price) => { - this.blockConversion = price; + this.currencyChangeSubscription?.unsubscribe(); + this.currencyChangeSubscription = this.stateService.fiatCurrency$.pipe( + switchMap((currency) => { + return tx.status.block_time ? this.priceService.getBlockPrice$(tx.status.block_time, true, currency).pipe( + tap((price) => tx['price'] = price), + ) : of(undefined); }) ).subscribe(); setTimeout(() => { this.applyFragment(); }, 0); + + this.cd.detectChanges(); }, (error) => { this.error = error; @@ -442,12 +531,13 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { block_time: block.timestamp, }; this.stateService.markBlock$.next({ blockHeight: block.height }); - if (this.tx.acceleration || (this.accelerationInfo && ['accelerating', 'mined', 'completed'].includes(this.accelerationInfo.status))) { + if (this.tx.acceleration || (this.accelerationInfo && ['accelerating', 'completed_provisional', 'completed'].includes(this.accelerationInfo.status))) { this.audioService.playSound('wind-chimes-harp-ascend'); } else { this.audioService.playSound('magic'); } this.fetchAcceleration$.next(block.id); + this.fetchMiningInfo$.next({ hash: block.id, height: block.height, txid: this.tx.txid }); } }); @@ -507,7 +597,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { } } } - if (!found && txFeePerVSize < mempoolBlocks[mempoolBlocks.length - 1].feeRange[0]) { + if (!found && mempoolBlocks.length && txFeePerVSize < mempoolBlocks[mempoolBlocks.length - 1].feeRange[0]) { this.txInBlockIndex = 7; } }); @@ -526,6 +616,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { if (!this.txId) { return; } + this.enterpriseService.goal(8); this.showAccelerationSummary = true && this.acceleratorAvailable; this.scrollIntoAccelPreview = !this.scrollIntoAccelPreview; return false; @@ -592,6 +683,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.segwitEnabled = !this.tx.status.confirmed || isFeatureActive(this.stateService.network, this.tx.status.block_height, 'segwit'); this.taprootEnabled = !this.tx.status.confirmed || isFeatureActive(this.stateService.network, this.tx.status.block_height, 'taproot'); this.rbfEnabled = !this.tx.status.confirmed || isFeatureActive(this.stateService.network, this.tx.status.block_height, 'rbf'); + this.tx.flags = getTransactionFlags(this.tx); + this.filters = this.tx.flags ? toFilters(this.tx.flags).filter(f => f.txPage) : []; } else { this.segwitEnabled = false; this.taprootEnabled = false; @@ -600,6 +693,29 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.featuresEnabled = this.segwitEnabled || this.taprootEnabled || this.rbfEnabled; } + isAuditAvailable(blockHeight: number): boolean { + if (!this.auditEnabled) { + return false; + } + switch (this.stateService.network) { + case 'testnet': + if (blockHeight < this.stateService.env.TESTNET_BLOCK_AUDIT_START_HEIGHT) { + return false; + } + break; + case 'signet': + if (blockHeight < this.stateService.env.SIGNET_BLOCK_AUDIT_START_HEIGHT) { + return false; + } + break; + default: + if (blockHeight < this.stateService.env.MAINNET_BLOCK_AUDIT_START_HEIGHT) { + return false; + } + } + return true; + } + resetTransaction() { this.error = undefined; this.tx = null; @@ -615,10 +731,13 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.hasEffectiveFeeRate = false; this.rbfInfo = null; this.rbfReplaces = []; + this.filters = []; this.showCpfpDetails = false; this.accelerationInfo = null; this.txInBlockIndex = null; this.mempoolPosition = null; + this.pool = null; + this.auditStatus = null; document.body.scrollTo(0, 0); this.leaveTransaction(); } @@ -680,9 +799,9 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { @HostListener('window:resize', ['$event']) setGraphSize(): void { this.isMobile = window.innerWidth < 850; - if (this.graphContainer?.nativeElement) { + if (this.graphContainer?.nativeElement && this.stateService.isBrowser) { setTimeout(() => { - if (this.graphContainer?.nativeElement) { + if (this.graphContainer?.nativeElement?.clientWidth) { this.graphWidth = this.graphContainer.nativeElement.clientWidth; } else { setTimeout(() => { this.setGraphSize(); }, 1); @@ -706,6 +825,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.mempoolPositionSubscription.unsubscribe(); this.mempoolBlocksSubscription.unsubscribe(); this.blocksSubscription.unsubscribe(); + this.miningSubscription?.unsubscribe(); + this.currencyChangeSubscription?.unsubscribe(); this.leaveTransaction(); } } diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index 24d34d6ec..625a7c70e 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -33,7 +33,7 @@ - + @@ -204,7 +204,7 @@ Peg-out to - + diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index da9bdfe04..b66889f0b 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -32,11 +32,14 @@ export class TransactionsListComponent implements OnInit, OnChanges { @Input() outputIndex: number; @Input() address: string = ''; @Input() rowLimit = 12; + @Input() blockTime: number = 0; // Used for price calculation if all the transactions are in the same block @Output() loadMore = new EventEmitter(); latestBlock$: Observable; outspendsSubscription: Subscription; + currencyChangeSubscription: Subscription; + currency: string; refreshOutspends$: ReplaySubject = new ReplaySubject(); refreshChannels$: ReplaySubject = new ReplaySubject(); showDetails$ = new BehaviorSubject(false); @@ -125,6 +128,35 @@ export class TransactionsListComponent implements OnInit, OnChanges { ) , ).subscribe(() => this.ref.markForCheck()); + + this.currencyChangeSubscription = this.stateService.fiatCurrency$ + .subscribe(currency => { + this.currency = currency; + this.refreshPrice(); + }); + } + + refreshPrice(): void { + // Loop over all transactions + if (!this.transactions || !this.transactions.length || !this.currency) { + return; + } + const confirmedTxs = this.transactions.filter((tx) => tx.status.confirmed).length; + if (!this.blockTime) { + this.transactions.forEach((tx) => { + if (!this.blockTime) { + if (tx.status.block_time) { + this.priceService.getBlockPrice$(tx.status.block_time, confirmedTxs < 10, this.currency).pipe( + tap((price) => tx['price'] = price), + ).subscribe(); + } + } + }); + } else { + this.priceService.getBlockPrice$(this.blockTime, true, this.currency).pipe( + tap((price) => this.transactions?.forEach((tx) => tx['price'] = price)), + ).subscribe(); + } } ngOnChanges(changes): void { @@ -148,6 +180,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { this.transactionsLength = this.transactions.length; this.cacheService.setTxCache(this.transactions); + const confirmedTxs = this.transactions.filter((tx) => tx.status.confirmed).length; this.transactions.forEach((tx) => { tx['@voutLimit'] = true; tx['@vinLimit'] = true; @@ -197,10 +230,18 @@ export class TransactionsListComponent implements OnInit, OnChanges { } } - this.priceService.getBlockPrice$(tx.status.block_time).pipe( - tap((price) => tx['price'] = price) - ).subscribe(); + if (!this.blockTime && tx.status.block_time && this.currency) { + this.priceService.getBlockPrice$(tx.status.block_time, confirmedTxs < 10, this.currency).pipe( + tap((price) => tx['price'] = price), + ).subscribe(); + } }); + + if (this.blockTime && this.transactions?.length && this.currency) { + this.priceService.getBlockPrice$(this.blockTime, true, this.currency).pipe( + tap((price) => this.transactions?.forEach((tx) => tx['price'] = price)), + ).subscribe(); + } const txIds = this.transactions.filter((tx) => !tx._outspends).map((tx) => tx.txid); if (txIds.length && !this.cached) { this.refreshOutspends$.next(txIds); @@ -308,5 +349,6 @@ export class TransactionsListComponent implements OnInit, OnChanges { ngOnDestroy(): void { this.outspendsSubscription.unsubscribe(); + this.currencyChangeSubscription?.unsubscribe(); } } diff --git a/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.html b/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.html index 1bc141f49..6aa7cf7c0 100644 --- a/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.html +++ b/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.html @@ -44,6 +44,28 @@ Fee #{{ line.index + 1 }} + + + + + + + + + + + + + + + + + + + + + +

@@ -51,8 +73,26 @@

-

Output  #{{ line.vout + 1 }}

-

Input  #{{ line.vin + 1 }}

+

Output  #{{ line.vout + 1 }} + + + + + + + + +

+

Input  #{{ line.vin + 1 }} + + + + + + + + +

Confidential

@@ -66,7 +106,7 @@
- +

@@ -77,4 +117,42 @@ {{ item.displayValue / pow(10, assetsMinimal[item.asset][3]) | number: '1.' + assetsMinimal[item.asset][3] + '-' + assetsMinimal[item.asset][3] }} {{ assetsMinimal[item.asset][1] }} + + + + 1 block earlier + + + + 1 block later + + + + in the same block + + + + (prevout + + {{ n }} blocks earlier) + + + ) + + + ) + + + + + (spent + + {{ n }} blocks later) + + + ) + + + ) + \ No newline at end of file diff --git a/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.scss b/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.scss index 4e1efa980..97cfcddb7 100644 --- a/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.scss +++ b/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.scss @@ -7,7 +7,7 @@ padding: 10px 15px; text-align: left; pointer-events: none; - max-width: 300px; + max-width: 350px; p { margin: 0; diff --git a/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.ts b/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.ts index 924982983..ff1e86596 100644 --- a/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.ts +++ b/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.ts @@ -1,7 +1,8 @@ import { Component, ElementRef, ViewChild, Input, OnChanges, OnInit } from '@angular/core'; -import { tap } from 'rxjs'; +import { Subscription, of, switchMap, tap } from 'rxjs'; import { Price, PriceService } from '../../services/price.service'; import { StateService } from '../../services/state.service'; +import { ApiService } from '../../services/api.service'; import { environment } from '../../../environments/environment'; interface Xput { @@ -19,6 +20,9 @@ interface Xput { pegout?: string; confidential?: boolean; timestamp?: number; + blockHeight?: number; + status?: any; + spent?: boolean; asset?: string; } @@ -34,7 +38,14 @@ export class TxBowtieGraphTooltipComponent implements OnChanges { @Input() assetsMinimal: any; tooltipPosition = { x: 0, y: 0 }; - blockConversion: Price; + blockConversions: { [timestamp: number]: Price } = {}; + inputStatus: { [index: number]: any } = {}; + currency: string; + viewFiat: boolean; + chainTip: number; + currencyChangeSubscription: Subscription; + viewFiatSubscription: Subscription; + chainTipSubscription: Subscription; nativeAssetId = this.stateService.network === 'liquidtestnet' ? environment.nativeTestAssetId : environment.nativeAssetId; @@ -43,15 +54,39 @@ export class TxBowtieGraphTooltipComponent implements OnChanges { constructor( private priceService: PriceService, private stateService: StateService, + private apiService: ApiService, ) {} + ngOnInit(): void { + this.currencyChangeSubscription = this.stateService.fiatCurrency$.subscribe(currency => { + this.currency = currency; + this.blockConversions = {}; + this.inputStatus = {}; + }); + this.viewFiatSubscription = this.stateService.viewFiat$.subscribe(viewFiat => this.viewFiat = viewFiat); + this.chainTipSubscription = this.stateService.chainTip$.subscribe(tip => this.chainTip = tip); + } + ngOnChanges(changes): void { if (changes.line?.currentValue) { - this.priceService.getBlockPrice$(changes.line?.currentValue.timestamp, true).pipe( - tap((price) => { - this.blockConversion = price; - }) - ).subscribe(); + if (changes.line.currentValue.type === 'input') { + if (!this.inputStatus[changes.line.currentValue.index]) { + if (changes.line.currentValue.txid) { + this.apiService.getTransactionStatus$(changes.line.currentValue.txid).pipe( + tap((status) => { + changes.line.currentValue.status = status; + this.inputStatus[changes.line.currentValue.index] = status; + this.fetchPrices(changes); + }) + ).subscribe(); + } + } else { + changes.line.currentValue.status = this.inputStatus[changes.line.currentValue.index]; + this.fetchPrices(changes); + } + } else { + this.fetchPrices(changes); + } } if (changes.cursorPosition && changes.cursorPosition.currentValue) { @@ -71,7 +106,32 @@ export class TxBowtieGraphTooltipComponent implements OnChanges { } } + fetchPrices(changes: any) { + if (!this.currency || !this.viewFiat) return; + if (this.isConnector) { // If the tooltip is on a connector, we fetch prices at the time of the input / output + if (['input', 'output'].includes(changes.line.currentValue.type) && changes.line.currentValue?.status?.block_time && !this.blockConversions?.[changes.line.currentValue?.status.block_time]) { + this.priceService.getBlockPrice$(changes.line.currentValue?.status.block_time, true, this.currency).pipe( + tap((price) => this.blockConversions[changes.line.currentValue.status.block_time] = price), + ).subscribe(); + } + } else { // If the tooltip is on the transaction itself, we fetch prices at the time of the transaction + if (changes.line.currentValue.timestamp && !this.blockConversions[changes.line.currentValue.timestamp]) { + if (changes.line.currentValue.timestamp) { + this.priceService.getBlockPrice$(changes.line.currentValue.timestamp, true, this.currency).pipe( + tap((price) => this.blockConversions[changes.line.currentValue.timestamp] = price), + ).subscribe(); + } + } + } + } + pow(base: number, exponent: number): number { return Math.pow(base, exponent); } + + ngOnDestroy(): void { + this.currencyChangeSubscription?.unsubscribe(); + this.viewFiatSubscription?.unsubscribe(); + this.chainTipSubscription?.unsubscribe(); + } } diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html index 14e49e21b..9f491affc 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html @@ -1,5 +1,13 @@

- + +
- +
Transaction Fees
@@ -16,64 +16,49 @@
-
-
- -
+
+ +
Mempool Goggles: {{ goggleCycle[goggleIndex].name }}
+   + +
+
+
+ +
+
+
+
- - -
- -
-
-
- - -
- -
-
- - - - - - - - -
- - - - - {{ group.name }} -
-
- -
+ +
Incoming Transactions
+
-
-
+
- -
-
-
L-BTC in circulation
- -

{{ liquidPegsMonth.series.slice(-1)[0] | number: '1.2-2' }} L-BTC

-
+ +
+ +
+ +
+
Liquid Federation Holdings
+
+
+ +
+
+
+ +
+
Federation Timelock-Expired UTXOs
+
+
+ +
+
+
+ +
+
+
L-BTC Supply Against BTC Holdings
+
+ +
+
+
+ +
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+
- -
Incoming Transactions
- - -  Backend is synchronizing ({{ mempoolLoadingStatus$ | async }}%) - - -
-
 
-
‎{{ mempoolInfoData.value.vBytesPerSecond | ceil | number }} vB/s
-
‎{{ mempoolInfoData.value.vBytesPerSecond * 4 | ceil | number }} WU/s
-
-
-
-
+ + +
+ Audit in progress: Bitcoin block height #{{ auditStatus.lastBlockAudit }} / #{{ auditStatus.bitcoinHeaders }} +
+
+
\ No newline at end of file diff --git a/frontend/src/app/dashboard/dashboard.component.scss b/frontend/src/app/dashboard/dashboard.component.scss index 884ba1027..01eb7f73d 100644 --- a/frontend/src/app/dashboard/dashboard.component.scss +++ b/frontend/src/app/dashboard/dashboard.component.scss @@ -44,8 +44,11 @@ .graph-card { height: 100%; + @media (min-width: 768px) { + height: 415px; + } @media (min-width: 992px) { - height: 385px; + height: 510px; } } @@ -56,6 +59,10 @@ display: flex; flex-direction: row; } + &.lbtc-pegs-stats { + display: flex; + flex-direction: row; + } h5 { margin-bottom: 10px; } @@ -66,7 +73,7 @@ @media (min-width: 485px) { margin: 0px auto 10px; } - @media (min-width: 785px) { + @media (min-width: 768px) { margin: 0px auto 0px; } &:last-child { @@ -97,6 +104,9 @@ color: #ffffff66; font-size: 12px; } + .bitcoin-color { + color: #b86d12; + } } .progress { width: 90%; @@ -255,6 +265,12 @@ .mempool-graph { height: 255px; + @media (min-width: 768px) { + height: 285px; + } + @media (min-width: 992px) { + height: 370px; + } } .loadingGraphs{ height: 250px; @@ -305,6 +321,9 @@ flex-direction: column; justify-content: space-around; padding: 22px 20px; + &.liquid { + height: 124.5px; + } } .less-padding { padding: 20px 20px; @@ -361,3 +380,64 @@ text-decoration: none; color: inherit; } + +.mempool-block-wrapper { + max-height: 410px; + max-width: 410px; + margin: auto; + + @media (min-width: 768px) { + max-height: 344px; + max-width: 344px; + } + @media (min-width: 992px) { + max-height: 410px; + max-width: 410px; + } +} + +.goggle-badge { + margin: 6px 5px 8px; + background: none; + border: solid 2px #105fb0; + cursor: pointer; + + &.active { + background: #105fb0; + } +} + +.btn-xs { + padding: 0.35rem 0.5rem; + font-size: 12px; +} + +.quick-filter { + margin-top: 5px; + margin-bottom: 6px; +} + +.card-liquid { + background-color: #1d1f31; + height: 418px; + @media (min-width: 992px) { + height: 512px; + } + &.smaller { + height: 408px; + } +} + +.card-title-liquid { + padding-top: 20px; + margin-left: 10px; +} + +.in-progress-message { + position: relative; + color: #ffffff91; + margin-top: 20px; + text-align: center; + padding-bottom: 3px; + font-weight: 500; +} diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 8a34bf768..f396ba6ae 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -1,12 +1,14 @@ -import { AfterViewInit, ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; -import { combineLatest, merge, Observable, of, Subscription } from 'rxjs'; -import { catchError, filter, map, scan, share, switchMap, tap } from 'rxjs/operators'; -import { BlockExtended, OptimizedMempoolStats } from '../interfaces/node-api.interface'; -import { MempoolInfo, TransactionStripped, ReplacementInfo } from '../interfaces/websocket.interface'; +import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core'; +import { combineLatest, EMPTY, fromEvent, interval, merge, Observable, of, Subject, Subscription, timer } from 'rxjs'; +import { catchError, delayWhen, distinctUntilChanged, filter, map, scan, share, shareReplay, startWith, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators'; +import { AuditStatus, BlockExtended, CurrentPegs, FederationAddress, FederationUtxo, OptimizedMempoolStats, PegsVolume, RecentPeg, TransactionStripped } from '../interfaces/node-api.interface'; +import { MempoolInfo, ReplacementInfo } from '../interfaces/websocket.interface'; import { ApiService } from '../services/api.service'; import { StateService } from '../services/state.service'; import { WebsocketService } from '../services/websocket.service'; import { SeoService } from '../services/seo.service'; +import { ActiveFilter, FilterMode, toFlags } from '../shared/filters.utils'; +import { detectWebGL } from '../shared/graphs.utils'; interface MempoolBlocksData { blocks: number; @@ -32,7 +34,6 @@ interface MempoolStatsData { changeDetection: ChangeDetectionStrategy.OnPush }) export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { - featuredAssets$: Observable; network$: Observable; mempoolBlocksData$: Observable; mempoolInfoData$: Observable; @@ -47,26 +48,69 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { transactionsWeightPerSecondOptions: any; isLoadingWebSocket$: Observable; liquidPegsMonth$: Observable; + currentPeg$: Observable; + auditStatus$: Observable; + auditUpdated$: Observable; + liquidReservesMonth$: Observable; + currentReserves$: Observable; + recentPegsList$: Observable; + pegsVolume$: Observable; + federationAddresses$: Observable; + federationAddressesNumber$: Observable; + federationUtxosNumber$: Observable; + expiredUtxos$: Observable; + emergencySpentUtxosStats$: Observable; + fullHistory$: Observable; + isLoad: boolean = true; + filterSubscription: Subscription; + mempoolInfoSubscription: Subscription; currencySubscription: Subscription; currency: string; + incomingGraphHeight: number = 300; + lbtcPegGraphHeight: number = 360; + webGlEnabled = true; + private lastPegBlockUpdate: number = 0; + private lastPegAmount: string = ''; + private lastReservesBlockUpdate: number = 0; + + goggleResolution = 82; + goggleCycle: { index: number, name: string, mode: FilterMode, filters: string[] }[] = [ + { index: 0, name: 'All', mode: 'and', filters: [] }, + { index: 1, name: 'Consolidation', mode: 'and', filters: ['consolidation'] }, + { index: 2, name: 'Coinjoin', mode: 'and', filters: ['coinjoin'] }, + { index: 3, name: 'Data', mode: 'or', filters: ['inscription', 'fake_pubkey', 'op_return'] }, + ]; + goggleFlags = 0n; + goggleMode: FilterMode = 'and'; + goggleIndex = 0; + + private destroy$ = new Subject(); constructor( public stateService: StateService, private apiService: ApiService, private websocketService: WebsocketService, - private seoService: SeoService - ) { } + private seoService: SeoService, + @Inject(PLATFORM_ID) private platformId: Object, + ) { + this.webGlEnabled = this.stateService.isBrowser && detectWebGL(); + } ngAfterViewInit(): void { this.stateService.focusSearchInputDesktop(); } ngOnDestroy(): void { + this.filterSubscription.unsubscribe(); + this.mempoolInfoSubscription.unsubscribe(); this.currencySubscription.unsubscribe(); this.websocketService.stopTrackRbfSummary(); + this.destroy$.next(1); + this.destroy$.complete(); } ngOnInit(): void { + this.onResize(); this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$; this.seoService.resetTitle(); this.seoService.resetDescription(); @@ -78,11 +122,34 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { map((indicators) => indicators.mempool !== undefined ? indicators.mempool : 100) ); + this.filterSubscription = this.stateService.activeGoggles$.subscribe((active: ActiveFilter) => { + const activeFilters = active.filters.sort().join(','); + for (const goggle of this.goggleCycle) { + if (goggle.mode === active.mode) { + const goggleFilters = goggle.filters.sort().join(','); + if (goggleFilters === activeFilters) { + this.goggleIndex = goggle.index; + this.goggleFlags = toFlags(goggle.filters); + this.goggleMode = goggle.mode; + return; + } + } + } + this.goggleCycle.push({ + index: this.goggleCycle.length, + name: 'Custom', + mode: active.mode, + filters: active.filters, + }); + this.goggleIndex = this.goggleCycle.length - 1; + this.goggleFlags = toFlags(active.filters); + this.goggleMode = active.mode; + }); + this.mempoolInfoData$ = combineLatest([ this.stateService.mempoolInfo$, this.stateService.vbytesPerSecond$ - ]) - .pipe( + ]).pipe( map(([mempoolInfo, vbytesPerSecond]) => { const percent = Math.round((Math.min(vbytesPerSecond, this.vBytesPerSecondLimit) / this.vBytesPerSecondLimit) * 100); @@ -112,6 +179,8 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { }) ); + this.mempoolInfoSubscription = this.mempoolInfoData$.subscribe(); + this.mempoolBlocksData$ = this.stateService.mempoolBlocks$ .pipe( map((mempoolBlocks) => { @@ -125,30 +194,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { }) ); - this.featuredAssets$ = this.apiService.listFeaturedAssets$() - .pipe( - map((featured) => { - const newArray = []; - for (const feature of featured) { - if (feature.ticker !== 'L-BTC' && feature.asset) { - newArray.push(feature); - } - } - return newArray.slice(0, 4); - }), - ); - - this.transactions$ = this.stateService.transactions$ - .pipe( - scan((acc, tx) => { - if (acc.find((t) => t.txid == tx.txid)) { - return acc; - } - acc.unshift(tx); - acc = acc.slice(0, 6); - return acc; - }, []), - ); + this.transactions$ = this.stateService.transactions$; this.blocks$ = this.stateService.blocks$ .pipe( @@ -185,7 +231,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { acc.unshift(stats); acc = acc.slice(0, 120); return acc; - }, mempoolStats) + }, (mempoolStats || [])) ), of(mempoolStats) ); @@ -200,12 +246,118 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { return null; } }), - share(), + shareReplay(1), ); - if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') { - this.liquidPegsMonth$ = this.apiService.listLiquidPegsMonth$() + if (this.stateService.network === 'liquid') { + this.auditStatus$ = this.stateService.blocks$.pipe( + takeUntil(this.destroy$), + throttleTime(40000), + delayWhen(_ => this.isLoad ? timer(0) : timer(2000)), + tap(() => this.isLoad = false), + switchMap(() => this.apiService.federationAuditSynced$()), + shareReplay(1) + ); + + this.currentPeg$ = this.auditStatus$.pipe( + switchMap(_ => + this.apiService.liquidPegs$().pipe( + filter((currentPegs) => currentPegs.lastBlockUpdate >= this.lastPegBlockUpdate), + tap((currentPegs) => { + this.lastPegBlockUpdate = currentPegs.lastBlockUpdate; + }) + ) + ), + share() + ); + + this.auditUpdated$ = combineLatest([ + this.auditStatus$, + this.currentPeg$ + ]).pipe( + filter(([auditStatus, _]) => auditStatus.isAuditSynced === true), + map(([auditStatus, currentPeg]) => ({ + lastBlockAudit: auditStatus.lastBlockAudit, + currentPegAmount: currentPeg.amount + })), + switchMap(({ lastBlockAudit, currentPegAmount }) => { + const blockAuditCheck = lastBlockAudit > this.lastReservesBlockUpdate; + const amountCheck = currentPegAmount !== this.lastPegAmount; + this.lastPegAmount = currentPegAmount; + return of(blockAuditCheck || amountCheck); + }), + share() + ); + + this.currentReserves$ = this.auditUpdated$.pipe( + filter(auditUpdated => auditUpdated === true), + throttleTime(40000), + switchMap(_ => + this.apiService.liquidReserves$().pipe( + filter((currentReserves) => currentReserves.lastBlockUpdate >= this.lastReservesBlockUpdate), + tap((currentReserves) => { + this.lastReservesBlockUpdate = currentReserves.lastBlockUpdate; + }) + ) + ), + share() + ); + + this.recentPegsList$ = this.auditUpdated$.pipe( + filter(auditUpdated => auditUpdated === true), + throttleTime(40000), + switchMap(_ => this.apiService.recentPegsList$()), + share() + ); + + this.pegsVolume$ = this.auditUpdated$.pipe( + filter(auditUpdated => auditUpdated === true), + throttleTime(40000), + switchMap(_ => this.apiService.pegsVolume$()), + share() + ); + + this.federationAddresses$ = this.auditUpdated$.pipe( + filter(auditUpdated => auditUpdated === true), + throttleTime(40000), + switchMap(_ => this.apiService.federationAddresses$()), + share() + ); + + this.federationAddressesNumber$ = this.auditUpdated$.pipe( + filter(auditUpdated => auditUpdated === true), + throttleTime(40000), + switchMap(_ => this.apiService.federationAddressesNumber$()), + map(count => count.address_count), + share() + ); + + this.federationUtxosNumber$ = this.auditUpdated$.pipe( + filter(auditUpdated => auditUpdated === true), + throttleTime(40000), + switchMap(_ => this.apiService.federationUtxosNumber$()), + map(count => count.utxo_count), + share() + ); + + this.expiredUtxos$ = this.auditUpdated$.pipe( + filter(auditUpdated => auditUpdated === true), + throttleTime(40000), + switchMap(_ => this.apiService.expiredUtxos$()), + share() + ); + + this.emergencySpentUtxosStats$ = this.auditUpdated$.pipe( + filter(auditUpdated => auditUpdated === true), + throttleTime(40000), + switchMap(_ => this.apiService.emergencySpentUtxosStats$()), + share() + ); + + this.liquidPegsMonth$ = interval(60 * 60 * 1000) .pipe( + startWith(0), + switchMap(() => this.apiService.listLiquidPegsMonth$()), map((pegs) => { const labels = pegs.map(stats => stats.date); const series = pegs.map(stats => parseFloat(stats.amount) / 100000000); @@ -217,6 +369,45 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { }), share(), ); + + this.liquidReservesMonth$ = interval(60 * 60 * 1000).pipe( + startWith(0), + switchMap(() => this.apiService.listLiquidReservesMonth$()), + map(reserves => { + const labels = reserves.map(stats => stats.date); + const series = reserves.map(stats => parseFloat(stats.amount) / 100000000); + return { + series, + labels + }; + }), + share() + ); + + this.fullHistory$ = combineLatest([this.liquidPegsMonth$, this.currentPeg$, this.liquidReservesMonth$, this.currentReserves$]) + .pipe( + map(([liquidPegs, currentPeg, liquidReserves, currentReserves]) => { + liquidPegs.series[liquidPegs.series.length - 1] = parseFloat(currentPeg.amount) / 100000000; + + if (liquidPegs.series.length === liquidReserves?.series.length) { + liquidReserves.series[liquidReserves.series.length - 1] = parseFloat(currentReserves?.amount) / 100000000; + } else if (liquidPegs.series.length === liquidReserves?.series.length + 1) { + liquidReserves.series.push(parseFloat(currentReserves?.amount) / 100000000); + liquidReserves.labels.push(liquidPegs.labels[liquidPegs.labels.length - 1]); + } else { + liquidReserves = { + series: [], + labels: [] + }; + } + + return { + liquidPegs, + liquidReserves + }; + }), + share() + ); } this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => { @@ -237,4 +428,30 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { trackByBlock(index: number, block: BlockExtended) { return block.height; } + + getArrayFromNumber(num: number): number[] { + return Array.from({ length: num }, (_, i) => i + 1); + } + + setFilter(index): void { + const selected = this.goggleCycle[index]; + this.stateService.activeGoggles$.next(selected); + } + + @HostListener('window:resize', ['$event']) + onResize(): void { + if (window.innerWidth >= 992) { + this.incomingGraphHeight = 300; + this.goggleResolution = 82; + this.lbtcPegGraphHeight = 360; + } else if (window.innerWidth >= 768) { + this.incomingGraphHeight = 215; + this.goggleResolution = 80; + this.lbtcPegGraphHeight = 270; + } else { + this.incomingGraphHeight = 180; + this.goggleResolution = 86; + this.lbtcPegGraphHeight = 270; + } + } } diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index 86a63e513..14dc43a64 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -9,8 +9,8 @@ const emptyCodeSample = { response: `` }; -const showJsExamplesDefault = { "": true, "testnet": true, "signet": true, "liquid": true, "liquidtestnet": false, "bisq": true }; -const showJsExamplesDefaultFalse = { "": false, "testnet": false, "signet": false, "liquid": false, "liquidtestnet": false, "bisq": false }; +const showJsExamplesDefault = { "": true, "testnet": true, "signet": true, "liquid": true, "liquidtestnet": false }; +const showJsExamplesDefaultFalse = { "": false, "testnet": false, "signet": false, "liquid": false, "liquidtestnet": false }; export const wsApiDocsData = { showJsExamples: showJsExamplesDefault, @@ -101,7 +101,6 @@ if __name__ == "__main__": codeSampleTestnet: emptyCodeSample, codeSampleSignet: emptyCodeSample, codeSampleLiquid: emptyCodeSample, - codeSampleBisq: emptyCodeSample, }; export const restApiDocsData = [ @@ -110,7 +109,7 @@ export const restApiDocsData = [ category: "general", fragment: "general", title: "General", - showConditions: bitcoinNetworks.concat(["bisq"]) + showConditions: bitcoinNetworks, }, { type: "endpoint", @@ -155,6 +154,7 @@ export const restApiDocsData = [ previousRetarget: -4.807005268478962, nextRetargetHeight: 741888, timeAvg: 302328, + adjustedTimeAvg: 302328, timeOffset: 0 }` }, @@ -171,6 +171,7 @@ export const restApiDocsData = [ previousRetarget: -4.807005268478962, nextRetargetHeight: 741888, timeAvg: 302328, + adjustedTimeAvg: 302328, timeOffset: 0 }` }, @@ -187,6 +188,7 @@ export const restApiDocsData = [ previousRetarget: -4.807005268478962, nextRetargetHeight: 741888, timeAvg: 302328, + adjustedTimeAvg: 302328, timeOffset: 0 }` }, @@ -203,6 +205,7 @@ export const restApiDocsData = [ previousRetarget: -4.807005268478962, nextRetargetHeight: 741888, timeAvg: 302328, + adjustedTimeAvg: 302328, timeOffset: 0 }` } @@ -247,7 +250,6 @@ export const restApiDocsData = [ codeSampleSignet: emptyCodeSample, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -258,9 +260,9 @@ export const restApiDocsData = [ fragment: "get-historical-price", title: "GET Historical Price", description: { - default: "Returns bitcoin historical price denominated in main currencies." + default: "Returns bitcoin historical price denominated in main currencies. Available query parameters: currency, timestamp. If no parameter is provided, the full price history for all currencies is returned." }, - urlString: "/v1/historical-price", + urlString: "/v1/historical-price?currency=EUR×tamp=1500000000", showConditions: [""], showJsExamples: showJsExamplesDefaultFalse, codeExample: { @@ -268,7 +270,7 @@ export const restApiDocsData = [ codeTemplate: { commonJS: ``, esModule: ``, - curl: `/api/v1/historical-price`, + curl: `/api/v1/historical-price?currency=EUR×tamp=1500000000`, }, codeSampleMainnet: { esModule: [], @@ -277,34 +279,18 @@ export const restApiDocsData = [ response: `{ prices: [ { - time: 1703692800, - USD: 42972, - EUR: 39590, - GBP: 36803, - CAD: 56883, - CHF: 36486, - AUD: 63006, - JPY: 6124530 - }, - ... - { - time: 1279497600, - USD: 0.08584, - EUR: -1, - GBP: -1, - CAD: -1, - CHF: -1, - AUD: -1, - JPY: -1 + "time": 1499904000, + "EUR": 1964, + "USD": 2254.9 } ], exchangeRates: { - USDEUR: 0.92, - USDGBP: 0.86, - USDCAD: 1.32, - USDCHF: 0.85, - USDAUD: 1.47, - USDJPY: 142.52 + "USDEUR": 0.92, + "USDGBP": 0.78, + "USDCAD": 1.36, + "USDCHF": 0.89, + "USDAUD": 1.53, + "USDJPY": 149.48 } } ` @@ -313,530 +299,6 @@ export const restApiDocsData = [ codeSampleSignet: emptyCodeSample, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, - } - } - }, - { - type: "endpoint", - category: "general", - httpRequestMethod: "GET", - fragment: "get-stats", - title: "GET Stats", - description: { - default: "Returns statistics about all Bisq transactions." - }, - urlString: "/stats", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - commonJS: ` - const { %{0}: { statistics } } = mempoolJS(); - - const stats = await statistics.getStats(); - - document.getElementById("result").textContent = JSON.stringify(stats, undefined, 2); - `, - esModule: ` - const { %{0}: { statistics } } = mempoolJS(); - - const stats = await statistics.getStats(); - console.log(stats); - `, - curl: `/api/stats`, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: [], - commonJS: [], - curl: [], - response: `{ - addresses: 213825, - minted: 6148323.75, - burnt: 1830262.66, - spent_txos: 215705, - unspent_txos: 2572 -}` - }, - } - } - }, - { - type: "category", - category: "markets", - fragment: "markets", - title: "Markets", - showConditions: ["bisq"] - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-currencies", - title: "GET Market Currencies", - description: { - default: "Provides list of available currencies for a given base currency." - }, - urlString: "/currencies", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/currencies`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const currencies = await markets.getCurrencies(); - - document.getElementById("result").textContent = JSON.stringify(currencies, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const currencies = await markets.getCurrencies(); - console.log(currencies); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: [], - commonJS: [], - curl: [], - response: `{ - BTC: { - code: 'BTC', - name: 'Bitcoin', - precision: 8, - _type: 'crypto' - } - ... -}`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-depth", - title: "GET Market Depth", - description: { - default: "Provides list of open offer prices for a single market." - }, - urlString: "/depth?market=[:market]", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/depth?market=%{1}`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const depth = await markets.getDepth({ market }); - - document.getElementById("result").textContent = JSON.stringify(depth, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const depth = await markets.getDepth({ market }); - console.log(depth); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD'], - commonJS: ['BTC_USD'], - curl: ['BTC_USD'], - response: `{ - btc_usd: { - buys: [ - '4.56941560', - ... - ], - sells: [ - '4.54668218', - ... - ] - } -}`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-hloc", - title: "GET Market HLOC", - description: { - default: "Provides hi/low/open/close data for a given market. This can be used to generate a candlestick chart." - }, - urlString: "/hloc?market=[:market]", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/hloc?market=%{1}`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const hloc = await markets.getHloc({ market }); - - document.getElementById("result").textContent = JSON.stringify(hloc, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const hloc = await markets.getHloc({ market }); - console.log(hloc); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD'], - commonJS: ['BTC_USD'], - curl: ['BTC_USD'], - response: `[ - { - period_start: 1609459200, - open: '30448.18510000', - close: '45717.81750000', - high: '77700.00000000', - low: '27500.00000000', - avg: '44613.01158471', - volume_right: '4923536.57150000', - volume_left: '110.36100000' - } - ... -]`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-markets", - title: "GET Markets", - description: { - default: "Provides list of available markets." - }, - urlString: "/markets", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/markets`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const allMarkets = await markets.getMarkets(); - - document.getElementById("result").textContent = JSON.stringify(allMarkets, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const allMarkets = await markets.getMarkets(); - console.log(allMarkets); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD'], - commonJS: ['BTC_USD'], - curl: ['BTC_USD'], - response: `{ - btc_brl: { - pair: 'btc_brl', - lname: 'Bitcoin', - rname: 'Brazilian Real', - lsymbol: 'BTC', - rsymbol: 'BRL', - lprecision: 8, - rprecision: 2, - ltype: 'crypto', - rtype: 'fiat', - name: 'Bitcoin/Brazilian Real' - }, - ... -}`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-offers", - title: "GET Market Offers", - description: { - default: "Provides list of open offer details for a single market." - }, - urlString: "/offers?market=[:market]", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/offers?market=%{1}`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const offers = await markets.getOffers({ market }); - - document.getElementById("result").textContent = JSON.stringify(offers, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const offers = await markets.getOffers({ market }); - console.log(offers); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD'], - commonJS: ['BTC_USD'], - curl: ['BTC_USD'], - response: `{ - btc_usd: { - buys: [ - { - offer_id: "ORHL1BE-0c193d04-be60-4657-ba42-cc172bb4ae5d-172", - offer_date: 1630207815462, - direction: "BUY", - min_amount: "0.00500000", - amount: "0.01500000", - price: "50030.24770000", - volume: "750.45370000", - payment_method: "AMAZON_GIFT_CARD", - offer_fee_txid: null - }, - ... - ], - sells: [ - { - offer_id: "nswiwkre-7676d5e6-e808-4c47-9c51-d5708e465ad5-172", - offer_date: 1630320354509, - direction: "SELL", - min_amount: "0.04170000", - amount: "0.04170000", - price: "49534.89880000", - volume: "2065.60520000", - payment_method: "CASH_DEPOSIT", - offer_fee_txid: null - }, - ... - ] - } -}`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-ticker", - title: "GET Market Ticker", - description: { - default: "Provides 24-hour price ticker for single market or all markets." - }, - urlString: "/ticker?market=[:market]", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/ticker?market=%{1}`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const ticker = await markets.getTicker({ market }); - - document.getElementById("result").textContent = JSON.stringify(ticker, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const ticker = await markets.getTicker({ market }); - console.log(ticker); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD'], - commonJS: ['BTC_USD'], - curl: ['BTC_USD'], - response: `{ - last: "53923.20570000", - high: "53923.20570000", - low: "48137.67410000", - volume_left: "0.27160000", - volume_right: "13593.92070000", - buy: "48118.52400000", - sell: "49555.63750000" -}`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-trades", - title: "GET Market Trades", - description: { - default: "Provides list of completed trades for a single market." - }, - urlString: "/trades?market=[:market]&limit=[:limit]", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/trades?market=%{1}&limit=%{2}`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const trades = await markets.getTrades({ market, limit: %{2} }); - - document.getElementById("result").textContent = JSON.stringify(trades, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const trades = await markets.getTrades({ market, limit: %{2} }); - console.log(trades); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD', '1'], - commonJS: ['BTC_USD', '1'], - curl: ['BTC_USD', '1'], - response: `[ - { - price: "53923.20570000", - amount: "0.00500000", - volume: "269.61600000", - payment_method: "CLEAR_X_CHANGE", - trade_date: 1630646161647 - } -]`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-volumes", - title: "GET Market Volumes", - description: { - default: "Provides periodic volume data in terms of base currency for one or all markets." - }, - urlString: "/volumes?basecurrency=[:basecurrency]", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/volumes?markets=%{1}`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const volumes = await markets.getVolumes({ market }); - - document.getElementById("result").textContent = JSON.stringify(volumes, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const volumes = await markets.getVolumes({ market }); - console.log(volumes); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD', 'BTC'], - commonJS: ['BTC_USD', 'BTC'], - curl: ['BTC_USD', 'BTC'], - response: `[ - { - period_start: 1451606400, - num_trades: 1923, - volume: "1095.22050000" - }, - ... -]`, - }, } } }, @@ -845,7 +307,7 @@ export const restApiDocsData = [ category: "addresses", fragment: "addresses", title: "Addresses", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]) + showConditions: bitcoinNetworks.concat(liquidNetworks) }, { type: "endpoint", @@ -857,7 +319,7 @@ export const restApiDocsData = [ default: "Returns details about an address. Available fields: address, chain_stats, and mempool_stats. chain_stats and mempool_stats each contain an object with tx_count, funded_txo_count, funded_txo_sum, spent_txo_count, and spent_txo_sum." }, urlString: "/address/:address", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]), + showConditions: bitcoinNetworks.concat(liquidNetworks), showJsExamples: showJsExamplesDefault, codeExample: { default: { @@ -981,28 +443,6 @@ export const restApiDocsData = [ } }` }, - codeSampleBisq: { - esModule: [`B1DgwRN92rdQ9xpEVCdXRfgeqGw9X4YtrZz`], - commonJS: [`B1DgwRN92rdQ9xpEVCdXRfgeqGw9X4YtrZz`], - curl: [`B1DgwRN92rdQ9xpEVCdXRfgeqGw9X4YtrZz`], - response: `[ - { - "txVersion": "1", - "id": "d6f0a6fd191ac907ff88fc51af91cae8d50e596a846952ffa0ad0cea84eedc9a", - "blockHeight": 679129, - "blockHash": "00000000000000000001328850b0482312325f7f4abd5457e45d37cad664675d", - "time": 1618369311000, - "inputs": [ ... ], - "outputs": [ ... ], - "txType": "PAY_TRADE_FEE", - "txTypeDisplayString": "Pay trade fee", - "burntFee": 6, - "invalidatedBsq": 0, - "unlockBlockHeight": 0 - }, - ... -]` - }, } } }, @@ -1013,7 +453,7 @@ export const restApiDocsData = [ fragment: "get-address-transactions", title: "GET Address Transactions", description: { - default: "Get transaction history for the specified address/scripthash, sorted with newest first. Returns up to 50 mempool transactions plus the first 25 confirmed transactions. You can request more confirmed transactions using :last_seen_txid (see below)." + default: "Get transaction history for the specified address/scripthash, sorted with newest first. Returns up to 50 mempool transactions plus the first 25 confirmed transactions. You can request more confirmed transactions using an after_txid query parameter." }, urlString: "/address/:address/txs", showConditions: bitcoinNetworks.concat(liquidNetworks), @@ -1156,7 +596,6 @@ export const restApiDocsData = [ ... ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -1310,12 +749,6 @@ export const restApiDocsData = [ ... ]` }, - codeSampleBisq: { - esModule: [], - commonJS: [], - curl: [], - response: '' - }, } } }, @@ -1441,7 +874,6 @@ export const restApiDocsData = [ } ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -1574,7 +1006,6 @@ export const restApiDocsData = [ } ]`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -1633,7 +1064,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -1741,7 +1171,6 @@ export const restApiDocsData = [ "ticker": "LCAD" }`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -1828,7 +1257,6 @@ export const restApiDocsData = [ ... ]`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -1879,7 +1307,6 @@ export const restApiDocsData = [ curl: [`05aa9f02a06da37f2a0a572c49ac381499a16a643ad7c70c51ac94560778c92e`], response: `1000`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -1963,7 +1390,7 @@ export const restApiDocsData = [ category: "blocks", fragment: "blocks", title: "Blocks", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]) + showConditions: bitcoinNetworks.concat(liquidNetworks) }, { type: "endpoint", @@ -1976,7 +1403,7 @@ export const restApiDocsData = [ liquid: "Returns details about a block. Available fields: id, height, version, timestamp, bits, nonce, merkle_root, tx_count, size, weight,proof, and previousblockhash." }, urlString: "/block/:hash", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]), + showConditions: bitcoinNetworks.concat(liquidNetworks), showJsExamples: showJsExamplesDefault, codeExample: { default: { @@ -2132,18 +1559,6 @@ export const restApiDocsData = [ ext: {...} }`, }, - codeSampleBisq: { - esModule: ['0000000000000000000b24f70ed27da8b282b050f38e20831923211a1f7266d5'], - commonJS: ['0000000000000000000b24f70ed27da8b282b050f38e20831923211a1f7266d5'], - curl: ['0000000000000000000b24f70ed27da8b282b050f38e20831923211a1f7266d5'], - response: `{ - height: 698746, - time: 1630621494000, - hash: "0000000000000000000b24f70ed27da8b282b050f38e20831923211a1f7266d5", - previousBlockHash: "000000000000000000039cd226a99c125ee3004e9d585b04e2ccceccddef7547", - txs: [] -}` - }, } } }, @@ -2209,7 +1624,6 @@ export const restApiDocsData = [ curl: [`8f7cb70f32e2069724212c986f34462fc40180eabf189b44486faf6989824f9a`], response: `000000a0263542a60466e252dbc301001f2f87cdd232106344209d6c252bbda572fd4527b4b9a8412c0ecaca405241beaa6779e74d505a481941a873be74b0b34511cce7d806d261515c020001220020e9e4117540f7f23b3edd7c2cad660a17fb33c7959b8c37cf61d92b189133929a96000000fbee9cea00d8efdc49cfbec328537e0d7032194de6ebf3cf42e5c05bb89a08b10003004730440220303a6fc365e016422bd5d714e403db237964c9e53c244310a4a03f432583290202206951e82c2ffa028f88d64d9bb4ec7789ced137046bb38a02816617b554efd42b012551210217e403ddb181872c32a0cd468c710040b2f53d8cac69f18dad07985ee37e9a7151ae`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2272,7 +1686,6 @@ export const restApiDocsData = [ curl: [`150000`], response: `67d5eb1aee63c6c2058a088985503ff0626fd3f7f8022bdc74fab36a359164db`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2327,7 +1740,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -2393,7 +1805,6 @@ export const restApiDocsData = [ curl: [`67d5eb1aee63c6c2058a088985503ff0626fd3f7f8022bdc74fab36a359164db`], response: '', }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2479,7 +1890,6 @@ export const restApiDocsData = [ next_best: "2f24f3d94c006971b86fe2c9cdc92a7ed0aa7ec3b0643a836b8d8b5a54103bab" }`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2493,7 +1903,7 @@ export const restApiDocsData = [ default: "Returns the height of the last block." }, urlString: "/blocks/tip/height", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]), + showConditions: bitcoinNetworks.concat(liquidNetworks), showJsExamples: showJsExamplesDefault, codeExample: { default: { @@ -2543,12 +1953,6 @@ export const restApiDocsData = [ curl: [''], response: `162495`, }, - codeSampleBisq: { - esModule: [''], - commonJS: [''], - curl: [''], - response: `698765` - }, } } }, @@ -2612,7 +2016,6 @@ export const restApiDocsData = [ curl: [''], response: `ff643a1e102b555103d8feb20b296ee5cf3b4a202fa284e5d6ce82945b738ae7`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2678,7 +2081,6 @@ export const restApiDocsData = [ curl: ['b6b4aeefa220c6a17da116bda666e869b3146967d2479656448a8bce1e799b8f', '1'], response: `41493aa0eec8b6d359c2defc90e2fafb42fb5b8633456648553467a4d3a16c4a` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2773,7 +2175,6 @@ export const restApiDocsData = [ "fa6b8dda9037f8284a659627005ad32dbb81e22b102c1d3d8a9bab0893ce2ab7" ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2929,7 +2330,6 @@ export const restApiDocsData = [ ... ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -3133,7 +2533,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -3346,7 +2745,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -3421,87 +2819,6 @@ export const restApiDocsData = [ mediantime: 1640871614 }, ... -]` - }, - codeSampleBisq: emptyCodeSample, - } - } - }, - { - type: "endpoint", - category: "blocks", - httpRequestMethod: "GET", - fragment: "get-blocks", - title: "GET Blocks", - description: { - default: "

Returns the past n blocks with BSQ transactions starting m blocks ago.

Assume a block height of 700,000. Query /blocks/0/10 for the past 10 blocks before 700,000 with BSQ transactions. Query /blocks/1000/10 for the past 10 blocks before 699,000 with BSQ transactions." - }, - urlString: "/blocks/:m/:n", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/blocks/%{1}/%{2}`, - commonJS: ` - const { %{0}: { blocks } } = mempoolJS(); - - const getBlocks = await blocks.getBlocks({ index: %{1}, length: %{2} }); - - document.getElementById("result").textContent = JSON.stringify(getBlocks, undefined, 2); - `, - esModule: ` - const { %{0}: { blocks } } = mempoolJS(); - - const getBlocks = await blocks.getBlocks({ index: %{1}, length: %{2} }); - console.log(getBlocks); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: { - esModule: ['0', '5'], - commonJS: ['0', '5'], - curl: ['0', '5'], - response: `[ - { - "height": 739030, - "time": 1654203258000, - "hash": "000000000000000000036bc04416ddeec264cbb977a9cd9e454897acb547b601", - "previousBlockHash": "00000000000000000000f49261617b589d76e5e70529ea1d4c16f3e19ddcb8ef", - "txs": [ ... ], - }, - { - "height": 739029, - "time": 1654203236000, - "hash": "00000000000000000000f49261617b589d76e5e70529ea1d4c16f3e19ddcb8ef", - "previousBlockHash": "00000000000000000008dd87e9486cd0d71c5d84e452432bab33c2a0cbaa31ce", - "txs": [ ... ], - }, - { - "height": 739025, - "time": 1654199569000, - "hash": "000000000000000000021e9ce82dec208af75807f92a9b1d9dae91f2b4d40e24", - "previousBlockHash": "00000000000000000002db644c025a76464b466d25900402452b07213b30c40b", - "txs": [ ... ] - }, - { - "height": 739023, - "time": 1654198597000, - "hash": "0000000000000000000702ce10250a46bea4155ca7acb869f3ea92c1e3a68bc5", - "previousBlockHash": "00000000000000000002b3d6c1adc5676262ded84181982f88dbd357b9f9d1ec", - "txs": [ ... ] - }, - { - "height": 739020, - "time": 1654197263000, - "hash": "000000000000000000046eb46ad941028381d3534c35658f9c80de0641dbbb31", - "previousBlockHash": "000000000000000000073f1c49b4c4895f3fa6b866d1e21ab8b22f3f9318b42f", - "txs": [ ... ] - } ]` }, } @@ -3609,7 +2926,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -3702,7 +3018,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -3809,7 +3124,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -3930,7 +3244,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4140,7 +3453,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4260,7 +3572,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4355,7 +3666,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4416,7 +3726,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4515,7 +3824,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4609,7 +3917,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4736,7 +4043,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4880,7 +4186,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4989,7 +4294,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -5047,7 +4351,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -5132,7 +4435,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -5281,7 +4583,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -5442,7 +4743,6 @@ export const restApiDocsData = [ } ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -5536,7 +4836,6 @@ export const restApiDocsData = [ minimumFee: 0.1 }` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -5647,7 +4946,6 @@ export const restApiDocsData = [ ] }` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -5734,7 +5032,6 @@ export const restApiDocsData = [ "dfbe66e6e71e775c9529a822c14286de0ee1066c2760a53552615d05e17006f3" ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -5841,7 +5138,6 @@ export const restApiDocsData = [ ... ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -5972,7 +5268,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -6102,7 +5397,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -6111,7 +5405,7 @@ export const restApiDocsData = [ category: "transactions", fragment: "transactions", title: "Transactions", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]) + showConditions: bitcoinNetworks.concat(liquidNetworks) }, { type: "endpoint", @@ -6175,7 +5469,6 @@ export const restApiDocsData = [ curl: ['txid'], response: `` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -6189,7 +5482,7 @@ export const restApiDocsData = [ default: "Returns details about a transaction. Available fields: txid, version, locktime, size, weight, fee, vin, vout, and status." }, urlString: "/tx/:txid", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]), + showConditions: bitcoinNetworks.concat(liquidNetworks), showJsExamples: showJsExamplesDefault, codeExample: { default: { @@ -6314,27 +5607,6 @@ export const restApiDocsData = [ block_hash: "05a51089255650a16c17b4b3f3977376bc7ebe90a35584578f12916c3eaba59e", block_time: 1642000444 } -}`, - }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: `{ - txid: "98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e", - version: 1, - locktime: 0, - vin: [], - vout: [], - size: 402, - weight: 957, - fee: 2390, - status: { - confirmed: true, - block_height: 698788, - block_hash: "00000000000000000005bfe17b41395bed53565022e0c98965b15ec1d00b1f31", - block_time: 1630645738 - } }`, }, } @@ -6402,12 +5674,6 @@ export const restApiDocsData = [ curl: [`59dd7a0bce4f3310272ff352402291bc555f141149812d8f573f62e7fdc19cc4`], response: `020000000102fa567669f73a314138aa6dbe74e3935612895df273d20ccbbedbecd44a04d3ce0000000000fdffffff8412fed07b8316dd4304df90af6f20292d3b2950133711c0ee43eb94fe12cc4f0100000000fdffffff040b801035010192095b8d9316f28450e98a85c915994c3f80ecc493adf505d73e9609a51e48bc0f35e34f88c482654d659fa779dcbf0457dc71053f3edcf76bd3667f03821ffcc4fc4ae5c2668685fec678e4...`, }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: `0100000000010222ae3642a9300262f6e730e8bfb7979b15852c8836f3835beef9cd58c464e5f70000000000ffffffff22ae3642a9300262f6e730e8bfb7979b15852c8836f3835beef9cd58c464e5f70200000000ffffffff03de0900000000000016001490f9ee145d7b1c9352b793350741da97f3e4d795aca80500000000001600144168859b4b74a09277969fb8152115aea9d33a159c960600000000001600146534b1859209d8ae8f1a8...`, - }, } }, }, @@ -6463,12 +5729,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: `04000020e05c5f176bdb7966b44388ca223bef6e548fb390a9f202000000000000000000f10d5017f8e98200ea6e9d9a90d48e8078a49f2ee1da2cae9f80f48a0badfdaaeaad3161a0fa0f174d163a5daa0400000c77d2b87749e72de52feacaab57134c40172ae247c9de1f8f180736a8ef64a024542ab6b22b2c1fc961eae3d7d7d6c5f...`, - }, } } }, @@ -6584,28 +5844,6 @@ export const restApiDocsData = [ "1dbe7041197b78f73c0d4a3810c47080c252bc928f041b787acaad3fa76ba7a0" ], pos: 1 -}`, - }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: `{ - block_height: 698788, - merkle: [ - "455eb8942edf5444f0130194353185705e891fb328b47fd5c43c0f5260de8121", - "98d18cb3470a3ee27a1d083e8f7baf76eaed19b5c972af33a335acdb3374dc75", - "1bf53a838bef7d64c2f7207bdb054df7dcc58e335ba9bd43803d00a24b4aec1b", - "19033925798e6e09f385dd7b5afbd76136f910b21b75ca03a2692ee804e0860e", - "5b4bd0b3cbbd5b73ae36d00bd144ee8db0966ff1f78c4483a4a8d601dc0b2ded", - "485a0c2af1687efe5433d4621c5dd222f6c5d6d7d7e3ea61c91f2c2bb2b62a54", - "1af9cbc4539b66e44c9bd6d07c5720301ba4694088e06b7f5978686b7a94aa62", - "b7a27d5a849f30cdf82c19b3d84902a146a0723a0798e46f91028f412af0d14d", - "b3925e68565674c54b3f0beb9f5f3820f4cd35cd15683b119cd232f52024a997", - "24a064efa83607188f1fdec947e22a17404c1357abcaea2fe52de74977b8d277", - "bd818bb2791a0d536097163c0d4dfb4dc3657cbd169617ca286dcc828ddf444d" - ], - pos: 546 }`, }, } @@ -6727,22 +5965,6 @@ export const restApiDocsData = [ block_hash: "3b10cdce761c4a2ec3e1239648c7d034922b34608a66f894e2f707307dae6b18", block_time: 1642002136 } -}`, - }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`, '1'], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`, '1'], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`, '1'], - response: `{ - spent: true, - txid: "455eb8942edf5444f0130194353185705e891fb328b47fd5c43c0f5260de8121", - vin: 0, - status: { - confirmed: true, - block_height: 698788, - block_hash: "00000000000000000005bfe17b41395bed53565022e0c98965b15ec1d00b1f31", - block_time: 1630645738 - } }`, }, } @@ -6879,30 +6101,6 @@ export const restApiDocsData = [ { spent: false } -]`, - }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: `[ - { - spent: false - }, - { - spent: true, - txid: "455eb8942edf5444f0130194353185705e891fb328b47fd5c43c0f5260de8121", - vin: 0, - status: { - confirmed: true, - block_height: 698788, - block_hash: "00000000000000000005bfe17b41395bed53565022e0c98965b15ec1d00b1f31", - block_time: 1630645738 - } - }, - { - spent: false - } ]`, }, } @@ -6970,12 +6168,6 @@ export const restApiDocsData = [ curl: ['59dd7a0bce4f3310272ff352402291bc555f141149812d8f573f62e7fdc19cc4'], response: ``, }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: ``, - }, } } }, @@ -7117,7 +6309,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -7203,17 +6394,6 @@ export const restApiDocsData = [ block_height: 168765, block_hash: "05a51089255650a16c17b4b3f3977376bc7ebe90a35584578f12916c3eaba59e", block_time: 1642000444 -}`, - }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: `{ - confirmed: true, - block_height: 698788, - block_hash: "00000000000000000005bfe17b41395bed53565022e0c98965b15ec1d00b1f31", - block_time: 1630645738 }`, }, } @@ -7261,64 +6441,6 @@ export const restApiDocsData = [ } } }, - { - type: "endpoint", - category: "transactions", - httpRequestMethod: "GET", - fragment: "get-transactions", - title: "GET Transactions", - description: { - default: "Returns :length of latest Bisq transactions, starting from :index." - }, - urlString: "/txs/:index/:length", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/txs/%{1}/%{2}`, - commonJS: ` - const { %{0}: { transactions } } = mempoolJS(); - - const txs = await transactions.getTxs({ index: %{1}, length: %{2} }); - - document.getElementById("result").textContent = JSON.stringify(txs, undefined, 2); - `, - esModule: ` - const { %{0}: { transactions } } = mempoolJS(); - - const txs = await transactions.getTxs({ index: %{1}, length: %{2} }); - console.log(txStatus); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: [`0`, '1'], - commonJS: [`0`, '1'], - curl: [`0`, '1'], - response: `[ - { - txVersion: "1", - id: "be1b2932155c012bec79bbd0f7cf7db32a4a35859dcb7b70f5d35fea581ac30a", - blockHeight: 698808, - blockHash: "0000000000000000000bf9461e8e0b8e077bcc0e8fe0f55483a7fd5d0860336c", - time: 1630658066000, - inputs: [], - outputs: [], - txType: "PAY_TRADE_FEE", - txTypeDisplayString: "Pay trade fee", - burntFee: 609, - invalidatedBsq: 0, - unlockBlockHeight: 0 - } -]`, - }, - } - } - }, { type: "endpoint", category: "transactions", @@ -7383,7 +6505,6 @@ export const restApiDocsData = [ curl: [`0200000001fd5b5fcd1cb066c27cfc9fda5428b9be850b81ac440ea51f1ddba2f987189ac1010000008a4730440220686a40e9d2dbffeab4ca1ff66341d06a17806767f12a1fc4f55740a7af24c6b5022049dd3c9a85ac6c51fecd5f4baff7782a518781bbdd94453c8383755e24ba755c01410436d554adf4a3eb03a317c77aa4020a7bba62999df633bba0ea8f83f48b9e01b0861d3b3c796840f982ee6b14c3c4b7ad04fcfcc3774f81bff9aaf52a15751fedfdffffff02416c00000000000017a914bc791b2afdfe1e1b5650864a9297b20d74c61f4787d71d0000000000001976a9140a59837ccd4df25adc31cdad39be6a8d97557ed688ac00000000`], response: ``, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -7487,7 +6608,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -7573,7 +6693,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -7796,7 +6915,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -7941,7 +7059,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -8061,7 +7178,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -8174,7 +7290,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -8298,7 +7413,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -8496,7 +7610,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -8693,7 +7806,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -8881,7 +7993,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9046,7 +8157,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9145,7 +8255,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9311,7 +8420,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9513,7 +8621,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9650,7 +8757,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9759,7 +8865,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9868,10 +8973,410 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } + }, + { + type: "category", + category: "accelerator-public", + fragment: "accelerator-public", + title: "Accelerator (Public)", + showConditions: [""], + options: { officialOnly: true }, + }, + { + options: { officialOnly: true }, + type: "endpoint", + category: "accelerator-public", + httpRequestMethod: "POST", + fragment: "accelerator-estimate", + title: "POST Calculate Estimated Costs", + description: { + default: "

Returns estimated costs to accelerate a transaction. Optionally set the api_key header to get customized estimation.

" + }, + urlString: "/v1/services/accelerator/estimate", + showConditions: [""], + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `%{1}" "[[hostname]][[baseNetworkUrl]]/api/v1/services/accelerator/estimate`, //custom interpolation technique handled in replaceCurlPlaceholder() + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: ["txInput=ee13ebb99632377c15c94980357f674d285ac413452050031ea6dcd3e9b2dc29"], + headers: "api_key: stacksats", + response: `{ + "txSummary": { + "txid": "ee13ebb99632377c15c94980357f674d285ac413452050031ea6dcd3e9b2dc29", + "effectiveVsize": 154, + "effectiveFee": 154, + "ancestorCount": 1 + }, + "cost": 3850, + "targetFeeRate": 26, + "nextBlockFee": 4004, + "userBalance": 99900000, + "mempoolBaseFee": 40000, + "vsizeFee": 50000, + "hasAccess": true +}`, + }, + } + } + }, + { + options: { officialOnly: true }, + type: "endpoint", + category: "accelerator-public", + httpRequestMethod: "GET", + fragment: "accelerator-pending", + title: "GET Pending Accelerations", + description: { + default: "

Returns all transactions currently being accelerated.

" + }, + urlString: "/v1/services/accelerator/accelerations", + showConditions: [""], + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/services/accelerator/accelerations`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [], + headers: '', + response: `[ + { + "txid": "8a183c8ae929a2afb857e7f2acd440aaefdf2797f8f7eab1c5f95ff8602abc81", + "added": 1707558316, + "feeDelta": 3500, + "effectiveVsize": 111, + "effectiveFee": 1671, + "pools": [ + 111 + ] + }, + { + "txid": "6097f295e21bdd8d725bd8d9ad4dd72b05bd795dc648bfef52150a9b2b7f7a45", + "added": 1707560464, + "feeDelta": 60000, + "effectiveVsize": 812, + "effectiveFee": 7790, + "pools": [ + 111 + ] } +]`, + }, + } + } + }, + { + options: { officialOnly: true }, + type: "endpoint", + category: "accelerator-public", + httpRequestMethod: "GET", + fragment: "accelerator-public-history", + title: "GET Acceleration History", + description: { + default: `

Returns all past accelerated transactions. + Filters can be applied:

` + }, + urlString: "/v1/services/accelerator/accelerations/history", + showConditions: [""], + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/services/accelerator/accelerations/history?blockHash=00000000000000000000482f0746d62141694b9210a813b97eb8445780a32003`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [], + headers: '', + response: `[ + { + "txid": "d7e1796d8eb4a09d4e6c174e36cfd852f1e6e6c9f7df4496339933cd32cbdd1d", + "status": "completed", + "feePaid": 53239, + "added": 1707421053, + "lastUpdated": 1707422952, + "baseFee": 50000, + "vsizeFee": 0, + "effectiveFee": 146, + "effectiveVsize": 141, + "feeDelta": 14000, + "blockHash": "00000000000000000000482f0746d62141694b9210a813b97eb8445780a32003", + "blockHeight": 829559, + "pools": [ + { + "pool_unique_id": 111, + "username": "foundryusa" + } + ] + } +]`, + }, + } + } + }, + { + type: "category", + category: "accelerator-private", + fragment: "accelerator-private", + title: "Accelerator (Authenticated)", + showConditions: [""], + options: { officialOnly: true }, + }, + { + options: { officialOnly: true }, + type: "endpoint", + category: "accelerator-private", + httpRequestMethod: "GET", + fragment: "accelerator-deposit-history", + title: "GET Deposit History", + description: { + default: "

Returns a list of deposits the user has made as prepayment for the accelerator service.

" + }, + urlString: "/v1/services/accelerator/deposit-history", + showConditions: [""], + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/services/accelerator/deposit-history`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [], + headers: "api_key: stacksats", + response: `[ + { + "type": "Bitcoin", + "invoiceId": "CCunucVyNw7jUiUz64mmHz", + "amount": 10311031, + "status": "pending", + "date": 1706372653000, + "link": "/payment/bitcoin/CCunucVyNw7jUiUz64mmHz" + }, + { + "type": "Bitcoin", + "invoiceId": "SG1U27R9PdWi3gH3jB9tm9", + "amount": 21000000, + "status": "paid", + "date": 1706372582000, + "link": null + }, + ... +]`, + }, + } + } + }, + { + options: { officialOnly: true }, + type: "endpoint", + category: "accelerator-private", + httpRequestMethod: "GET", + fragment: "accelerator-balance", + title: "GET Available Balance", + description: { + default: "

Returns the user's currently available balance, currently locked funds, and total fees paid so far.

" + }, + urlString: "/v1/services/accelerator/balance", + showConditions: [""], + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/services/accelerator/balance`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [], + headers: "api_key: stacksats", + response: `{ + "balance": 99900000, + "hold": 101829, + "feesPaid": 133721 +}`, + }, + } + } + }, + { + options: { officialOnly: true }, + type: "endpoint", + category: "accelerator-private", + httpRequestMethod: "POST", + fragment: "accelerator-accelerate", + title: "POST Accelerate A Transaction", + description: { + default: "

Sends a request to accelerate a transaction.

" + }, + urlString: "/v1/services/accelerator/accelerate", + showConditions: [""], + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `%{1}" "[[hostname]][[baseNetworkUrl]]/api/v1/services/accelerator/accelerate`, //custom interpolation technique handled in replaceCurlPlaceholder() + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: ["txInput=ee13ebb99632377c15c94980357f674d285ac413452050031ea6dcd3e9b2dc29&userBid=21000000"], + headers: "api_key: stacksats", + response: `HTTP/1.1 200 OK`, + }, + } + } + }, + { + options: { officialOnly: true }, + type: "endpoint", + category: "accelerator-private", + httpRequestMethod: "GET", + fragment: "accelerator-history", + title: "GET Acceleration History", + description: { + default: "

Returns the user's past acceleration requests.

Pass one of the following for :status: all, requested, accelerating, mined, completed, failed. Pass true in :details to get a detailed history of the acceleration request.

" + }, + urlString: "/v1/services/accelerator/history?status=:status&details=:details", + showConditions: [""], + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/services/accelerator/history?status=all&details=true`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [], + headers: "api_key: stacksats", + response: `[ + { + "id": 89, + "user_id": 1, + "txid": "ae2639469ec000ed1d14e2550cbb01794e1cd288a00cdc7cce18398ba3cc2ffe", + "status": "failed" + "fee_paid": 0, + "added": 1706378712, + "last_updated": 1706378712, + "confirmations": 4, + "base_fee": 0, + "vsize_fee": 0, + "max_bid": 7000, + "effective_vsize": 135, + "effective_fee": 3128, + "history": [ + { + "event": "user-requested-acceleration", + "timestamp": 1706378712 + }, + { + "event": "accepted_test-api-key", + "timestamp": 1706378712 + }, + { + "event": "failed-at-block-827672", + "timestamp": 1706380261 + } + ] + }, + { + "id": 88, + "user_id": 1, + "txid": "c5840e89173331760e959a190b24e2a289121277ed7f8a095fe289b37cee9fde", + "status": "completed" + "fee_paid": 140019, + "added": 1706378704, + "last_updated": 1706380231, + "confirmations": 6, + "base_fee": 40000, + "vsize_fee": 100000, + "max_bid": 14000, + "effective_vsize": 135, + "effective_fee": 3152, + "history": [ + { + "event": "user-requested-acceleration", + "timestamp": 1706378704 + }, + { + "event": "accepted_test-api-key", + "timestamp": 1706378704 + }, + { + "event": "complete-at-block-827670", + "timestamp": 1706380231 + } + ] + }, + { + "id": 87, + "user_id": 1, + "txid": "178b5b9b310f0d667d7ea563a2cdcc17bc8cd15261b58b1653860a724ca83458", + "status": "completed" + "fee_paid": 90062, + "added": 1706378684, + "last_updated": 1706380231, + "confirmations": 6, + "base_fee": 40000, + "vsize_fee": 50000, + "max_bid": 14000, + "effective_vsize": 135, + "effective_fee": 3260, + "history": [ + { + "event": "user-requested-acceleration", + "timestamp": 1706378684 + }, + { + "event": "accepted_test-api-key", + "timestamp": 1706378684 + }, + { + "event": "complete-at-block-827670", + "timestamp": 1706380231 + } + ] + } +]`, + }, + } + } + }, ]; export const faqData = [ diff --git a/frontend/src/app/docs/api-docs/api-docs-nav.component.html b/frontend/src/app/docs/api-docs/api-docs-nav.component.html index ec1cde38f..7ec57714d 100644 --- a/frontend/src/app/docs/api-docs/api-docs-nav.component.html +++ b/frontend/src/app/docs/api-docs/api-docs-nav.component.html @@ -1,4 +1,8 @@ +
+

Get higher API limits with Mempool Enterprise®

+ More Info +
-

{{ item.title }}

+

{{ item.title }}

{{ item.title }}
diff --git a/frontend/src/app/docs/api-docs/api-docs-nav.component.scss b/frontend/src/app/docs/api-docs/api-docs-nav.component.scss index 3d1b17324..550d1c936 100644 --- a/frontend/src/app/docs/api-docs/api-docs-nav.component.scss +++ b/frontend/src/app/docs/api-docs/api-docs-nav.component.scss @@ -11,3 +11,22 @@ a { display: block; margin: 5px 0; } + +#enterprise-cta-desktop { + text-align: center; + padding: 20px; + margin: 20px 20px 20px 0; + background-color: #1d1f31; + border-radius: 12px; +} + +#enterprise-cta-desktop p { + margin: 0 auto 16px auto; + color: #fff; + font-weight: 400; +} + +#enterprise-cta-desktop a { + display: inline-block; +} + diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index c3a260995..57f2fc442 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -39,58 +39,68 @@
+
+

Get higher API limits with Mempool Enterprise®

+ +
+

Below is a reference for the {{ network.val === '' ? 'Bitcoin' : network.val.charAt(0).toUpperCase() + network.val.slice(1) }} REST API service.

Note that we enforce rate limits. If you exceed these limits, you will get an HTTP 429 error. If you repeatedly exceed the limits, you may be banned from accessing the service altogether. Consider an enterprise sponsorship if you need higher API limits.

-

{{ item.title }}

-
- {{ item.title }} {{ item.category }} -
-
-
Endpoint
- - {{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }} +
+

{{ item.title }}

+
+ {{ item.title }} {{ item.category }} +
+
+
Endpoint
+ + {{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }} + + + + {{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }} +

{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}

+
+
+ + + {{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }} +

{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}

+
+
+
{{ item.httpRequestMethod }} {{ item.urlString }}
+
+
+
Description
+ +
+
+ + +
+
+
+ +
+
+
+ + - - - {{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }} -

{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}

+ + + - - - {{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }} -

{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}

-
-
-
{{ item.httpRequestMethod }} {{ item.urlString }}
-
-
-
Description
- -
-
- - -
-
-
- -
+ +
- - - - - - - - - - -
@@ -98,7 +108,7 @@
-
+
diff --git a/frontend/src/app/docs/api-docs/api-docs.component.scss b/frontend/src/app/docs/api-docs/api-docs.component.scss index 27dfec61d..ed7b79636 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.scss +++ b/frontend/src/app/docs/api-docs/api-docs.component.scss @@ -269,7 +269,7 @@ h3 { .position-container { position: absolute; left: 50%; - bottom: 150px; + bottom: 158px; } #divider { @@ -315,6 +315,41 @@ h3 { margin-bottom: 0; } +#enterprise-cta-mobile { + padding: 20px; + background-color: #1d1f31; + border-radius: 0.25rem; + text-align: center; + position: fixed; + z-index: 100; + left: 30px; + width: calc(100% - 60px); + bottom: 70px; + display: none; + border: 3px solid #533180; +} + +#enterprise-cta-mobile p { + font-size: 16px; + display: inline-block; + margin: 0 auto; +} + +#enterprise-cta-mobile a { + padding: 4px 8px; + font-size: 16px; + margin: 15px 5px 5px 5px; +} + +#enterprise-cta-mobile .btn-secondary:hover { + background-color: #2d3348; + border-color: #2d3348; +} + +#enterprise-cta-mobile .no-line-break { + white-space: nowrap; +} + @media (max-width: 992px) { h3 { @@ -373,6 +408,10 @@ h3 { #disclaimer table { display: none; } + + #enterprise-cta-mobile { + display: initial; + } } @media (min-width: 992px) { diff --git a/frontend/src/app/docs/api-docs/api-docs.component.ts b/frontend/src/app/docs/api-docs/api-docs.component.ts index 333bb01ad..0f59524f7 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs.component.ts @@ -30,6 +30,7 @@ export class ApiDocsComponent implements OnInit, AfterViewInit { officialMempoolInstance: boolean; auditEnabled: boolean; mobileViewport: boolean = false; + showMobileEnterpriseUpsell: boolean = true; timeLtrSubscription: Subscription; timeLtr: boolean = this.stateService.timeLtr.value; @@ -182,10 +183,6 @@ export class ApiDocsComponent implements OnInit, AfterViewInit { if (network === 'liquidtestnet') { curlResponse = code.codeSampleLiquidTestnet.curl; } - if (network === 'bisq') { - curlResponse = code.codeSampleBisq.curl; - } - let curlNetwork = ''; if (this.env.BASE_MODULE === 'mempool') { if (!['', 'mainnet'].includes(network)) { diff --git a/frontend/src/app/docs/code-template/code-template.component.html b/frontend/src/app/docs/code-template/code-template.component.html index d97074ada..7d169a19f 100644 --- a/frontend/src/app/docs/code-template/code-template.component.html +++ b/frontend/src/app/docs/code-template/code-template.component.html @@ -33,7 +33,7 @@
  • Python -
    Code Example
    +
    Code Example
  • diff --git a/frontend/src/app/docs/code-template/code-template.component.ts b/frontend/src/app/docs/code-template/code-template.component.ts index 5ef8a64ba..395f6a297 100644 --- a/frontend/src/app/docs/code-template/code-template.component.ts +++ b/frontend/src/app/docs/code-template/code-template.component.ts @@ -36,9 +36,6 @@ export class CodeTemplateComponent implements OnInit { npmGithubLink(){ let npmLink = `https://github.com/mempool/mempool.js`; - if (this.network === 'bisq') { - npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-bisq-js`; - } if (this.network === 'liquid' || this.network === 'liquidtestnet') { npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-liquid-js`; } @@ -47,9 +44,6 @@ export class CodeTemplateComponent implements OnInit { npmModuleLink() { let npmLink = `https://www.npmjs.org/package/@mempool/mempool.js`; - if (this.network === 'bisq') { - npmLink = `https://www.npmjs.org/package/@mempool/bisq.js`; - } if (this.network === 'liquid' || this.network === 'liquidtestnet') { npmLink = `https://www.npmjs.org/package/@mempool/liquid.js`; } @@ -58,12 +52,12 @@ export class CodeTemplateComponent implements OnInit { normalizeHostsESModule(codeText: string) { if (this.env.BASE_MODULE === 'mempool') { - if (['liquid', 'bisq'].includes(this.network)) { + if (['liquid'].includes(this.network)) { codeText = codeText.replace('%{0}', this.network); } else { codeText = codeText.replace('%{0}', 'bitcoin'); } - if(['', 'main', 'liquid', 'bisq', 'liquidtestnet'].includes(this.network)) { + if(['', 'main', 'liquid', 'liquidtestnet'].includes(this.network)) { codeText = codeText.replace('mempoolJS();', `mempoolJS({ hostname: '${document.location.hostname}' });`); @@ -75,11 +69,6 @@ export class CodeTemplateComponent implements OnInit { } } - if (this.env.BASE_MODULE === 'bisq') { - codeText = codeText.replace('} = mempoolJS();', ` = bisqJS();`); - codeText = codeText.replace('{ %{0}: ', ''); - } - if (this.env.BASE_MODULE === 'liquid') { codeText = codeText.replace('} = mempoolJS();', ` = liquidJS();`); codeText = codeText.replace('{ %{0}: ', ''); @@ -89,12 +78,12 @@ export class CodeTemplateComponent implements OnInit { normalizeHostsCommonJS(codeText: string) { if (this.env.BASE_MODULE === 'mempool') { - if (['liquid', 'bisq'].includes(this.network)) { + if (['liquid'].includes(this.network)) { codeText = codeText.replace('%{0}', this.network); } else { codeText = codeText.replace('%{0}', 'bitcoin'); } - if(['', 'main', 'liquid', 'bisq'].includes(this.network)) { + if(['', 'main', 'liquid'].includes(this.network)) { codeText = codeText.replace('mempoolJS();', `mempoolJS({ hostname: '${document.location.hostname}' });`); @@ -106,11 +95,6 @@ export class CodeTemplateComponent implements OnInit { } } - if (this.env.BASE_MODULE === 'bisq') { - codeText = codeText.replace('} = mempoolJS();', ` = bisqJS();`); - codeText = codeText.replace('{ %{0}: ', ''); - } - if (this.env.BASE_MODULE === 'liquid') { codeText = codeText.replace('} = mempoolJS();', ` = liquidJS();`); codeText = codeText.replace('{ %{0}: ', ''); @@ -135,14 +119,8 @@ export class CodeTemplateComponent implements OnInit { if (this.network === 'liquid' || this.network === 'liquidtestnet') { codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule); } - if (this.network === 'bisq') { - codeText = this.replaceJSPlaceholder(codeText, code.codeSampleBisq.esModule); - } let importText = `import mempoolJS from "@mempool/mempool.js";`; - if (this.env.BASE_MODULE === 'bisq') { - importText = `import bisqJS from "@mempool/bisq.js";`; - } if (this.env.BASE_MODULE === 'liquid') { importText = `import liquidJS from "@mempool/liquid.js";`; } @@ -174,18 +152,12 @@ init();`; if (this.network === 'liquid' || this.network === 'liquidtestnet') { codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule); } - if (this.network === 'bisq') { - codeText = this.replaceJSPlaceholder(codeText, code.codeSampleBisq.esModule); - } if (code.noWrap) { return codeText; } let importText = ``; - if (this.env.BASE_MODULE === 'bisq') { - importText = ``; - } if (this.env.BASE_MODULE === 'liquid') { importText = ``; } @@ -224,14 +196,6 @@ npm install @mempool/mempool.js --save # yarn yarn add @mempool/mempool.js`; - if (this.env.BASE_MODULE === 'bisq') { - importTemplate = `# npm -npm install @mempool/bisq.js --save - -# yarn -yarn add @mempool/bisq.js`; - } - if (this.env.BASE_MODULE === 'liquid') { importTemplate = `# npm npm install @mempool/liquid.js --save @@ -257,9 +221,6 @@ yarn add @mempool/liquid.js`; if (this.network === 'liquidtestnet') { return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleLiquidTestnet); } - if (this.network === 'bisq') { - return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleBisq); - } if (this.network === '' || this.network === 'main') { return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleMainnet); } @@ -282,9 +243,6 @@ yarn add @mempool/liquid.js`; if (this.network === 'liquidtestnet') { return code.codeSampleLiquidTestnet.response; } - if (this.network === 'bisq') { - return code.codeSampleBisq.response; - } return code.codeSampleMainnet.response; } @@ -311,27 +269,29 @@ yarn add @mempool/liquid.js`; text = text.replace('%{' + indexNumber + '}', textReplace); } + const headersString = code.headers ? ` -H "${code.headers}"` : ``; + if (this.env.BASE_MODULE === 'mempool') { if (this.network === 'main' || this.network === '') { if (this.method === 'POST') { - return `curl -X POST -sSLd "${text}"`; + return `curl${headersString} -X POST -sSLd "${text}"`; } - return `curl -sSL "${this.hostname}${text}"`; + return `curl${headersString} -sSL "${this.hostname}${text}"`; } if (this.method === 'POST') { - return `curl -X POST -sSLd "${text}"`; + return `curl${headersString} -X POST -sSLd "${text}"`; } - return `curl -sSL "${this.hostname}/${this.network}${text}"`; + return `curl${headersString} -sSL "${this.hostname}/${this.network}${text}"`; } else if (this.env.BASE_MODULE === 'liquid') { if (this.method === 'POST') { if (this.network !== 'liquid') { text = text.replace('/api', `/${this.network}/api`); } - return `curl -X POST -sSLd "${text}"`; + return `curl${headersString} -X POST -sSLd "${text}"`; } - return ( this.network === 'liquid' ? `curl -sSL "${this.hostname}${text}"` : `curl -sSL "${this.hostname}/${this.network}${text}"` ); + return ( this.network === 'liquid' ? `curl${headersString} -sSL "${this.hostname}${text}"` : `curl${headersString} -sSL "${this.hostname}/${this.network}${text}"` ); } else { - return `curl -sSL "${this.hostname}${text}"`; + return `curl${headersString} -sSL "${this.hostname}${text}"`; } } diff --git a/frontend/src/app/docs/docs.routing.module.ts b/frontend/src/app/docs/docs.routing.module.ts index 1babb1dcf..3c0eb961d 100644 --- a/frontend/src/app/docs/docs.routing.module.ts +++ b/frontend/src/app/docs/docs.routing.module.ts @@ -8,7 +8,7 @@ const browserWindowEnv = browserWindow.__env || {}; let routes: Routes = []; -if (browserWindowEnv.BASE_MODULE && (browserWindowEnv.BASE_MODULE === 'bisq' || browserWindowEnv.BASE_MODULE === 'liquid')) { +if (browserWindowEnv.BASE_MODULE && browserWindowEnv.BASE_MODULE === 'liquid') { routes = [ { path: '', diff --git a/frontend/src/app/docs/docs/docs.component.ts b/frontend/src/app/docs/docs/docs.component.ts index 6d6c3b0c1..35080a19f 100644 --- a/frontend/src/app/docs/docs/docs.component.ts +++ b/frontend/src/app/docs/docs/docs.component.ts @@ -3,6 +3,7 @@ import { ActivatedRoute } from '@angular/router'; import { Env, StateService } from '../../services/state.service'; import { WebsocketService } from '../../services/websocket.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; @Component({ selector: 'app-docs', @@ -24,14 +25,14 @@ export class DocsComponent implements OnInit { private stateService: StateService, private websocket: WebsocketService, private seoService: SeoService, + private ogService: OpenGraphService, ) { } ngOnInit(): void { this.websocket.want(['blocks']); this.env = this.stateService.env; - this.showWebSocketTab = ( ! ( ( this.stateService.network === "bisq" ) || ( this.stateService.network === "liquidtestnet" ) ) ); this.showFaqTab = ( this.env.BASE_MODULE === 'mempool' ) ? true : false; - this.showElectrsTab = this.stateService.env.OFFICIAL_MEMPOOL_SPACE && ( this.stateService.network !== "bisq" ); + this.showElectrsTab = this.stateService.env.OFFICIAL_MEMPOOL_SPACE; document.querySelector( "html" ).style.scrollBehavior = "smooth"; } @@ -44,13 +45,12 @@ export class DocsComponent implements OnInit { this.activeTab = 0; this.seoService.setTitle($localize`:@@meta.title.docs.faq:FAQ`); this.seoService.setDescription($localize`:@@meta.description.docs.faq:Get answers to common questions like: What is a mempool? Why isn't my transaction confirming? How can I run my own instance of The Mempool Open Source Project? And more.`); + this.ogService.setManualOgImage('faq.jpg'); } else if( url[1].path === "rest" ) { this.activeTab = 1; this.seoService.setTitle($localize`:@@meta.title.docs.rest:REST API`); - if( this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ) { + if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ) { this.seoService.setDescription($localize`:@@meta.description.docs.rest-liquid:Documentation for the liquid.network REST API service: get info on addresses, transactions, assets, blocks, and more.`); - } else if( this.stateService.network === 'bisq' ) { - this.seoService.setDescription($localize`:@@meta.description.docs.rest-bisq:Documentation for the bisq.markets REST API service: get info on recent trades, current offers, transactions, network state, and more.`); } else { this.seoService.setDescription($localize`:@@meta.description.docs.rest-bitcoin:Documentation for the mempool.space REST API service: get info on addresses, transactions, blocks, fees, mining, the Lightning network, and more.`); } diff --git a/frontend/src/app/graphs/echarts.ts b/frontend/src/app/graphs/echarts.ts index 342867168..74fec1e71 100644 --- a/frontend/src/app/graphs/echarts.ts +++ b/frontend/src/app/graphs/echarts.ts @@ -1,6 +1,6 @@ // Import tree-shakeable echarts import * as echarts from 'echarts/core'; -import { LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart } from 'echarts/charts'; +import { LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart, GaugeChart } from 'echarts/charts'; import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, DataZoomComponent, VisualMapComponent, MarkLineComponent } from 'echarts/components'; import { SVGRenderer, CanvasRenderer } from 'echarts/renderers'; // Typescript interfaces @@ -12,6 +12,6 @@ echarts.use([ TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, DataZoomComponent, VisualMapComponent, MarkLineComponent, - LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart + LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart, GaugeChart ]); export { echarts, EChartsOption, TreemapSeriesOption, LineSeriesOption, PieSeriesOption }; \ No newline at end of file diff --git a/frontend/src/app/graphs/graphs.module.ts b/frontend/src/app/graphs/graphs.module.ts index 85905d1f1..761bd8e1f 100644 --- a/frontend/src/app/graphs/graphs.module.ts +++ b/frontend/src/app/graphs/graphs.module.ts @@ -12,6 +12,14 @@ import { FeeDistributionGraphComponent } from '../components/fee-distribution-gr import { IncomingTransactionsGraphComponent } from '../components/incoming-transactions-graph/incoming-transactions-graph.component'; import { MempoolGraphComponent } from '../components/mempool-graph/mempool-graph.component'; import { LbtcPegsGraphComponent } from '../components/lbtc-pegs-graph/lbtc-pegs-graph.component'; +import { ReservesSupplyStatsComponent } from '../components/liquid-reserves-audit/reserves-supply-stats/reserves-supply-stats.component'; +import { ExpiredUtxosStatsComponent } from '../components/liquid-reserves-audit/expired-utxos-stats/expired-utxos-stats.component'; +import { ReservesRatioStatsComponent } from '../components/liquid-reserves-audit/reserves-ratio-stats/reserves-ratio-stats.component'; +import { ReservesRatioComponent } from '../components/liquid-reserves-audit/reserves-ratio/reserves-ratio.component'; +import { RecentPegsStatsComponent } from '../components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component'; +import { RecentPegsListComponent } from '../components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component'; +import { FederationAddressesStatsComponent } from '../components/liquid-reserves-audit/federation-addresses-stats/federation-addresses-stats.component'; +import { FederationAddressesListComponent } from '../components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component'; import { GraphsComponent } from '../components/graphs/graphs.component'; import { StatisticsComponent } from '../components/statistics/statistics.component'; import { MempoolBlockComponent } from '../components/mempool-block/mempool-block.component'; @@ -24,12 +32,15 @@ import { AcceleratorDashboardComponent } from '../components/acceleration/accele import { HashrateChartComponent } from '../components/hashrate-chart/hashrate-chart.component'; import { HashrateChartPoolsComponent } from '../components/hashrates-chart-pools/hashrate-chart-pools.component'; import { BlockHealthGraphComponent } from '../components/block-health-graph/block-health-graph.component'; +import { AddressComponent } from '../components/address/address.component'; +import { AddressGraphComponent } from '../components/address-graph/address-graph.component'; import { CommonModule } from '@angular/common'; @NgModule({ declarations: [ DashboardComponent, MempoolBlockComponent, + AddressComponent, MiningDashboardComponent, AcceleratorDashboardComponent, @@ -48,9 +59,18 @@ import { CommonModule } from '@angular/common'; IncomingTransactionsGraphComponent, MempoolGraphComponent, LbtcPegsGraphComponent, + ReservesSupplyStatsComponent, + ExpiredUtxosStatsComponent, + ReservesRatioStatsComponent, + ReservesRatioComponent, + RecentPegsStatsComponent, + RecentPegsListComponent, + FederationAddressesStatsComponent, + FederationAddressesListComponent, HashrateChartComponent, HashrateChartPoolsComponent, BlockHealthGraphComponent, + AddressGraphComponent, ], imports: [ CommonModule, diff --git a/frontend/src/app/graphs/graphs.routing.module.ts b/frontend/src/app/graphs/graphs.routing.module.ts index 0f217eb6e..e069022cd 100644 --- a/frontend/src/app/graphs/graphs.routing.module.ts +++ b/frontend/src/app/graphs/graphs.routing.module.ts @@ -19,6 +19,7 @@ import { TelevisionComponent } from '../components/television/television.compone import { DashboardComponent } from '../dashboard/dashboard.component'; import { AccelerationFeesGraphComponent } from '../components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component'; import { AccelerationsListComponent } from '../components/acceleration/accelerations-list/accelerations-list.component'; +import { AddressComponent } from '../components/address/address.component'; const routes: Routes = [ { @@ -52,7 +53,7 @@ const routes: Routes = [ ] }, { - path: 'acceleration-list', + path: 'acceleration/list', data: { networks: ['bitcoin'] }, component: AccelerationsListComponent, }, @@ -67,6 +68,15 @@ const routes: Routes = [ }, ] }, + { + path: 'address/:id', + children: [], + component: AddressComponent, + data: { + ogImage: true, + networkSpecific: true, + } + }, { path: 'graphs', data: { networks: ['bitcoin', 'liquid'] }, diff --git a/frontend/src/app/injection-tokens.ts b/frontend/src/app/injection-tokens.ts new file mode 100644 index 000000000..6d923fd45 --- /dev/null +++ b/frontend/src/app/injection-tokens.ts @@ -0,0 +1,3 @@ +import { InjectionToken } from '@angular/core'; + +export const ZONE_SERVICE = new InjectionToken('ZONE_TASK'); \ No newline at end of file diff --git a/frontend/src/app/interfaces/electrs.interface.ts b/frontend/src/app/interfaces/electrs.interface.ts index 58a02ad79..de2cbeeaf 100644 --- a/frontend/src/app/interfaces/electrs.interface.ts +++ b/frontend/src/app/interfaces/electrs.interface.ts @@ -27,6 +27,7 @@ export interface Transaction { _channels?: TransactionChannels; price?: Price; sigops?: number; + flags?: bigint; } export interface TransactionChannels { @@ -149,6 +150,13 @@ export interface AddressOrScriptHash { mempool_stats: MempoolStats; } +export interface AddressTxSummary { + txid: string; + value: number; + height: number; + time: number; +} + export interface ChainStats { funded_txo_count: number; funded_txo_sum: number; diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index 9d936722d..8441acc14 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -54,6 +54,7 @@ export interface DifficultyAdjustment { previousTime: number; nextRetargetHeight: number; timeAvg: number; + adjustedTimeAvg: number; timeOffset: number; expectedBlocks: number; } @@ -76,6 +77,54 @@ export interface LiquidPegs { date: string; } +export interface CurrentPegs { + amount: string; + lastBlockUpdate: number; + hash: string; +} + +export interface PegsVolume { + volume: string; + number: number; +} + +export interface FederationAddress { + bitcoinaddress: string; + balance: string; +} + +export interface FederationUtxo { + txid: string; + txindex: number; + bitcoinaddress: string; + amount: number; + blocknumber: number; + blocktime: number; + pegtxid: string; + pegindex: number; + pegblocktime: number; + timelock: number; + expiredAt: number; + isDust?: boolean; +} + +export interface RecentPeg { + txid: string; + txindex: number; + amount: number; + bitcoinaddress: string; + bitcointxid: string; + bitcoinindex: number; + blocktime: number; +} + +export interface AuditStatus { + bitcoinBlocks: number; + bitcoinHeaders: number; + lastBlockAudit: number; + isAuditSynced: boolean; +} + export interface ITranslators { [language: string]: string; } /** @@ -159,6 +208,7 @@ export interface BlockExtended extends Block { export interface BlockAudit extends BlockExtended { missingTxs: string[], addedTxs: string[], + prioritizedTxs: string[], freshTxs: string[], sigopTxs: string[], fullrbfTxs: string[], @@ -181,7 +231,8 @@ export interface TransactionStripped { rate?: number; // effective fee rate acc?: boolean; flags?: number | null; - status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; + time?: number; + status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'prioritized' | 'censored' | 'selected' | 'rbf' | 'accelerated'; context?: 'projected' | 'actual'; } @@ -329,7 +380,7 @@ export interface INode { export interface Acceleration { txid: string; - status: 'requested' | 'accelerating' | 'mined' | 'completed' | 'failed'; + status: 'requested' | 'accelerating' | 'completed_provisional' | 'completed' | 'failed' | 'failed_provisional'; pools: number[]; feePaid: number; added: number; // timestamp @@ -347,8 +398,25 @@ export interface Acceleration { } export interface AccelerationHistoryParams { - timeframe?: string, - status?: string, - pool?: string, - blockHash?: string, + status?: string; + timeframe?: string; + poolUniqueId?: number; + blockHash?: string; + blockHeight?: number; + page?: number; + pageLength?: number; +} + +export interface AccelerationInfo { + txid: string, + height: number, + pool: { + id: number, + slug: string, + name: string, + }, + effective_vsize: number, + effective_fee: number, + boost_rate: number, + boost_cost: number, } \ No newline at end of file diff --git a/frontend/src/app/interfaces/websocket.interface.ts b/frontend/src/app/interfaces/websocket.interface.ts index 35bcbe9cc..daf06603f 100644 --- a/frontend/src/app/interfaces/websocket.interface.ts +++ b/frontend/src/app/interfaces/websocket.interface.ts @@ -1,8 +1,10 @@ +import { SafeResourceUrl } from '@angular/platform-browser'; import { ILoadingIndicators } from '../services/state.service'; import { Transaction } from './electrs.interface'; -import { BlockExtended, DifficultyAdjustment, RbfTree } from './node-api.interface'; +import { BlockExtended, DifficultyAdjustment, RbfTree, TransactionStripped } from './node-api.interface'; export interface WebsocketResponse { + backend?: 'esplora' | 'electrum' | 'none'; block?: BlockExtended; blocks?: BlockExtended[]; conversions?: any; @@ -34,7 +36,6 @@ export interface WebsocketResponse { 'track-rbf'?: string; 'track-rbf-summary'?: boolean; 'watch-mempool'?: boolean; - 'track-bisq-market'?: string; 'refresh-blocks'?: boolean; } @@ -70,9 +71,15 @@ export interface MempoolBlockWithTransactions extends MempoolBlock { } export interface MempoolBlockDelta { - added: TransactionStripped[], - removed: string[], - changed?: { txid: string, rate: number | undefined, acc: boolean | undefined }[]; + added: TransactionStripped[]; + removed: string[]; + changed: { txid: string, rate: number, flags: number, acc: boolean }[]; +} + +export interface MempoolBlockDeltaCompressed { + added: TransactionCompressed[]; + removed: string[]; + changed: MempoolDeltaChange[]; } export interface MempoolInfo { @@ -85,17 +92,10 @@ export interface MempoolInfo { minrelaytxfee: number; // (numeric) Current minimum relay fee for transactions } -export interface TransactionStripped { - txid: string; - fee: number; - vsize: number; - value: number; - acc?: boolean; // is accelerated? - rate?: number; // effective fee rate - flags?: number; - status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; - context?: 'projected' | 'actual'; -} +// [txid, fee, vsize, value, rate, flags, acceleration?] +export type TransactionCompressed = [string, number, number, number, number, number, number, 1?]; +// [txid, rate, flags, acceleration?] +export type MempoolDeltaChange = [string, number, number, (1|0)]; export interface IBackendInfo { hostname?: string; @@ -109,4 +109,19 @@ export interface Recommendedfees { hourFee: number; minimumFee: number; economyFee: number; +} + +export interface HealthCheckHost { + host: string; + active: boolean; + rtt: number; + latestHeight: number; + socket: boolean; + outOfSync: boolean; + unreachable: boolean; + checked: boolean; + lastChecked: number; + link?: string; + statusPage?: SafeResourceUrl; + flag?: string; } \ No newline at end of file diff --git a/frontend/src/app/lightning/channel/channel-preview.component.html b/frontend/src/app/lightning/channel/channel-preview.component.html index e59361e42..108fe2e95 100644 --- a/frontend/src/app/lightning/channel/channel-preview.component.html +++ b/frontend/src/app/lightning/channel/channel-preview.component.html @@ -66,9 +66,7 @@
    -
    + Error loading data. -

    - {{ error.status }}: {{ error.error }} -
    +
    diff --git a/frontend/src/app/lightning/lightning-api.service.ts b/frontend/src/app/lightning/lightning-api.service.ts index ee55fb75d..736c12c13 100644 --- a/frontend/src/app/lightning/lightning-api.service.ts +++ b/frontend/src/app/lightning/lightning-api.service.ts @@ -8,6 +8,7 @@ import { IChannel, INodesRanking, IOldestNodes, ITopNodesPerCapacity, ITopNodesP providedIn: 'root' }) export class LightningApiService { + private apiBaseUrl: string; // base URL is protocol, hostname, and port private apiBasePath = ''; // network path is /testnet, etc. or '' for mainnet private requestCache = new Map, expiry: number }>; @@ -16,11 +17,12 @@ export class LightningApiService { private httpClient: HttpClient, private stateService: StateService, ) { + this.apiBaseUrl = ''; // use relative URL by default + if (!stateService.isBrowser) { // except when inside AU SSR process + this.apiBaseUrl = this.stateService.env.NGINX_PROTOCOL + '://' + this.stateService.env.NGINX_HOSTNAME + ':' + this.stateService.env.NGINX_PORT; + } this.apiBasePath = ''; // assume mainnet by default this.stateService.networkChanged$.subscribe((network) => { - if (network === 'bisq' && !this.stateService.env.BISQ_SEPARATE_BACKEND) { - network = ''; - } this.apiBasePath = network ? '/' + network : ''; }); } @@ -66,15 +68,15 @@ export class LightningApiService { } getNode$(publicKey: string): Observable { - return this.httpClient.get(this.apiBasePath + '/api/v1/lightning/nodes/' + publicKey); + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/' + publicKey); } getNodeGroup$(name: string): Observable { - return this.httpClient.get(this.apiBasePath + '/api/v1/lightning/nodes/group/' + name); + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/group/' + name); } getChannel$(shortId: string): Observable { - return this.httpClient.get(this.apiBasePath + '/api/v1/lightning/channels/' + shortId); + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/channels/' + shortId); } getChannelsByNodeId$(publicKey: string, index: number = 0, status = 'open'): Observable { @@ -84,57 +86,57 @@ export class LightningApiService { .set('status', status) ; - return this.httpClient.get(this.apiBasePath + '/api/v1/lightning/channels', { params, observe: 'response' }); + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/channels', { params, observe: 'response' }); } getLatestStatistics$(): Observable { - return this.httpClient.get(this.apiBasePath + '/api/v1/lightning/statistics/latest'); + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/statistics/latest'); } listNodeStats$(publicKey: string): Observable { - return this.httpClient.get(this.apiBasePath + '/api/v1/lightning/nodes/' + publicKey + '/statistics'); + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/' + publicKey + '/statistics'); } getNodeFeeHistogram$(publicKey: string): Observable { - return this.httpClient.get(this.apiBasePath + '/api/v1/lightning/nodes/' + publicKey + '/fees/histogram'); + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/' + publicKey + '/fees/histogram'); } getNodesRanking$(): Observable { - return this.httpClient.get(this.apiBasePath + '/api/v1/lightning/nodes/rankings'); + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/rankings'); } listChannelStats$(publicKey: string): Observable { - return this.httpClient.get(this.apiBasePath + '/channels/' + publicKey + '/statistics'); + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/channels/' + publicKey + '/statistics'); } listStatistics$(interval: string | undefined): Observable { return this.httpClient.get( - this.apiBasePath + '/api/v1/lightning/statistics' + + this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/statistics' + (interval !== undefined ? `/${interval}` : ''), { observe: 'response' } ); } getTopNodesByCapacity$(): Observable { return this.httpClient.get( - this.apiBasePath + '/api/v1/lightning/nodes/rankings/liquidity' + this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/rankings/liquidity' ); } getTopNodesByChannels$(): Observable { return this.httpClient.get( - this.apiBasePath + '/api/v1/lightning/nodes/rankings/connectivity' + this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/rankings/connectivity' ); } getPenaltyClosedChannels$(): Observable { return this.httpClient.get( - this.apiBasePath + '/api/v1/lightning/penalties' + this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/penalties' ); } getOldestNodes$(): Observable { return this.httpClient.get( - this.apiBasePath + '/api/v1/lightning/nodes/rankings/age' + this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/rankings/age' ); } } diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html index 89059185e..ec59b7432 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html @@ -34,9 +34,11 @@
    -
    +
    - +
    + +
    @@ -44,11 +46,13 @@
    -
    +
    -
    Lightning Network History
    - - +
    +
    Lightning Network History
    + + +
    diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss index e91f7606a..e6da450df 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss @@ -20,6 +20,19 @@ } } +.fixed-mempool-graph { + height: 330px; +} + +.mempool-graph, .fixed-mempool-graph { + @media (min-width: 768px) { + height: 345px; + } + @media (min-width: 992px) { + height: 439px; + } +} + .card-title { font-size: 1rem; color: #4a68b9; diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts index dece98ddb..fd72cddfe 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts @@ -1,8 +1,9 @@ -import { AfterViewInit, ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, OnInit } from '@angular/core'; +import { Observable, merge } from 'rxjs'; import { share } from 'rxjs/operators'; import { INodesRanking, INodesStatistics } from '../../interfaces/node-api.interface'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; import { StateService } from '../../services/state.service'; import { LightningApiService } from '../lightning-api.service'; @@ -16,22 +17,45 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit { statistics$: Observable; nodesRanking$: Observable; officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE; + graphHeight: number = 300; constructor( private lightningApiService: LightningApiService, private seoService: SeoService, + private ogService: OpenGraphService, private stateService: StateService, + private cd: ChangeDetectorRef, ) { } ngOnInit(): void { + this.onResize(); + this.seoService.setTitle($localize`:@@142e923d3b04186ac6ba23387265d22a2fa404e0:Lightning Explorer`); this.seoService.setDescription($localize`:@@meta.description.lightning.dashboard:Get stats on the Lightning network (aggregate capacity, connectivity, etc), Lightning nodes (channels, liquidity, etc) and Lightning channels (status, fees, etc).`); + this.ogService.setManualOgImage('lightning.jpg'); this.nodesRanking$ = this.lightningApiService.getNodesRanking$().pipe(share()); this.statistics$ = this.lightningApiService.getLatestStatistics$().pipe(share()); + + if (!this.stateService.isBrowser) { + merge(this.nodesRanking$, this.statistics$).subscribe(() => { + this.cd.markForCheck(); + }); + } } ngAfterViewInit(): void { this.stateService.focusSearchInputDesktop(); } + + @HostListener('window:resize', ['$event']) + onResize(): void { + if (window.innerWidth >= 992) { + this.graphHeight = 340; + } else if (window.innerWidth >= 768) { + this.graphHeight = 245; + } else { + this.graphHeight = 210; + } + } } diff --git a/frontend/src/app/lightning/lightning.module.ts b/frontend/src/app/lightning/lightning.module.ts index f0154a15f..0b824ad78 100644 --- a/frontend/src/app/lightning/lightning.module.ts +++ b/frontend/src/app/lightning/lightning.module.ts @@ -34,7 +34,6 @@ import { OldestNodes } from '../lightning/nodes-ranking/oldest-nodes/oldest-node import { NodesRankingsDashboard } from '../lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component'; import { NodeChannels } from '../lightning/nodes-channels/node-channels.component'; import { GroupComponent } from './group/group.component'; -import { NodeOwnerComponent } from './node-owner/node-owner.component'; @NgModule({ declarations: [ @@ -67,7 +66,6 @@ import { NodeOwnerComponent } from './node-owner/node-owner.component'; NodesRankingsDashboard, NodeChannels, GroupComponent, - NodeOwnerComponent, ], imports: [ CommonModule, @@ -105,7 +103,6 @@ import { NodeOwnerComponent } from './node-owner/node-owner.component'; OldestNodes, NodesRankingsDashboard, NodeChannels, - NodeOwnerComponent, ], providers: [ LightningApiService, diff --git a/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.html b/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.html index 468b22acf..d8eb9e868 100644 --- a/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.html +++ b/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.html @@ -1,7 +1,7 @@

    Fee distribution

    -
    -
    +
    +
    diff --git a/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.scss b/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.scss index d738daa81..9fd7f2dba 100644 --- a/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.scss +++ b/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.scss @@ -1,5 +1,7 @@ .full-container { + position: relative; margin-top: 25px; margin-bottom: 25px; min-height: 100%; -} + min-height: 450px; +} \ No newline at end of file diff --git a/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.ts b/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.ts index ad667bcf6..f20a5123c 100644 --- a/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.ts +++ b/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.ts @@ -5,6 +5,7 @@ import { download } from '../../shared/graphs.utils'; import { LightningApiService } from '../lightning-api.service'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; +import { StateService } from '../../services/state.service'; @Component({ selector: 'app-node-fee-chart', @@ -33,6 +34,7 @@ export class NodeFeeChartComponent implements OnInit { constructor( @Inject(LOCALE_ID) public locale: string, private lightningApiService: LightningApiService, + public stateService: StateService, private activatedRoute: ActivatedRoute, private amountShortenerPipe: AmountShortenerPipe, ) { diff --git a/frontend/src/app/lightning/node-owner/node-owner.component.html b/frontend/src/app/lightning/node-owner/node-owner.component.html deleted file mode 100644 index e37b1e027..000000000 --- a/frontend/src/app/lightning/node-owner/node-owner.component.html +++ /dev/null @@ -1,17 +0,0 @@ -
    - -
    - -
    - - - -
    - -
    - Claim -
    - -
    - -
    \ No newline at end of file diff --git a/frontend/src/app/lightning/node-owner/node-owner.component.scss b/frontend/src/app/lightning/node-owner/node-owner.component.scss deleted file mode 100644 index 6734168cf..000000000 --- a/frontend/src/app/lightning/node-owner/node-owner.component.scss +++ /dev/null @@ -1,4 +0,0 @@ -.profile-photo { - width: 31px; - height: 31px; -} \ No newline at end of file diff --git a/frontend/src/app/lightning/node-owner/node-owner.component.ts b/frontend/src/app/lightning/node-owner/node-owner.component.ts deleted file mode 100644 index a03c04901..000000000 --- a/frontend/src/app/lightning/node-owner/node-owner.component.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { Observable } from 'rxjs'; -import { StateService } from '../../services/state.service'; - -@Component({ - selector: 'app-node-owner', - templateUrl: './node-owner.component.html', - styleUrls: ['./node-owner.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class NodeOwnerComponent{ - @Input() publicKey: string = ''; - @Input() alias: string = ''; - @Input() nodeOwner$: Observable; - - constructor( - public stateService: StateService - ) { - } -} diff --git a/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.html b/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.html index c5cad52fa..5edeaa9fd 100644 --- a/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.html +++ b/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.html @@ -1,7 +1,7 @@
    -
    -
    +
    +
    diff --git a/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.ts b/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.ts index a9308a887..f67c83367 100644 --- a/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.ts +++ b/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.ts @@ -8,6 +8,7 @@ import { StorageService } from '../../services/storage.service'; import { download } from '../../shared/graphs.utils'; import { LightningApiService } from '../lightning-api.service'; import { ActivatedRoute, ParamMap } from '@angular/router'; +import { StateService } from '../../services/state.service'; @Component({ selector: 'app-node-statistics-chart', @@ -48,6 +49,7 @@ export class NodeStatisticsChartComponent implements OnInit { @Inject(LOCALE_ID) public locale: string, private lightningApiService: LightningApiService, private storageService: StorageService, + public stateService: StateService, private activatedRoute: ActivatedRoute, ) { } diff --git a/frontend/src/app/lightning/node/node.component.html b/frontend/src/app/lightning/node/node.component.html index 445eba1c2..50dab25c7 100644 --- a/frontend/src/app/lightning/node/node.component.html +++ b/frontend/src/app/lightning/node/node.component.html @@ -5,7 +5,6 @@

    {{ node.alias }}

    -
    @@ -13,7 +12,6 @@ -
    diff --git a/frontend/src/app/lightning/node/node.component.ts b/frontend/src/app/lightning/node/node.component.ts index 56f48bf65..cc7c8e5b1 100644 --- a/frontend/src/app/lightning/node/node.component.ts +++ b/frontend/src/app/lightning/node/node.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, OnInit, ChangeDetectorRef } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; -import { Observable, of, EMPTY } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { catchError, map, switchMap, tap, share } from 'rxjs/operators'; import { SeoService } from '../../services/seo.service'; import { ApiService } from '../../services/api.service'; @@ -8,6 +8,7 @@ import { LightningApiService } from '../lightning-api.service'; import { GeolocationData } from '../../shared/components/geolocation/geolocation.component'; import { ILiquidityAd, parseLiquidityAdHex } from './liquidity-ad'; import { haversineDistance, kmToMiles } from '../../../app/shared/common.utils'; +import { ServicesApiServices } from '../../services/services-api.service'; interface CustomRecord { type: string; @@ -38,11 +39,11 @@ export class NodeComponent implements OnInit { tlvRecords: CustomRecord[]; avgChannelDistance$: Observable; showFeatures = false; - nodeOwner$: Observable; kmToMiles = kmToMiles; constructor( private apiService: ApiService, + private servicesApiService: ServicesApiServices, private lightningApiService: LightningApiService, private activatedRoute: ActivatedRoute, private seoService: SeoService, @@ -151,24 +152,6 @@ export class NodeComponent implements OnInit { return null; }) ) as Observable; - - this.nodeOwner$ = this.activatedRoute.paramMap - .pipe( - switchMap((params: ParamMap) => { - return this.apiService.getNodeOwner$(params.get('public_key')).pipe( - switchMap((response) => { - if (response.status === 204) { - return of(false); - } - return of(response.body); - }), - catchError(() => { - return of(false); - }) - ) - }), - share(), - ); } toggleShowDetails(): void { diff --git a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.html b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.html index 7237c709f..b8d0dc12f 100644 --- a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.html +++ b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.html @@ -1,12 +1,13 @@
    -
    -
    + +
    @@ -21,8 +22,10 @@ (Tor nodes excluded)
    -
    -
    + +
    +
    +
    diff --git a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.scss b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.scss index 558ad68a6..16482a0da 100644 --- a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.scss +++ b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.scss @@ -33,7 +33,6 @@ min-height: 400px; margin-top: 25px; margin-bottom: 25px; - min-height: 100%; } .full-container.widget { height: 250px; diff --git a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts index 296d60dec..18edc6ab1 100644 --- a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts +++ b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts @@ -1,13 +1,16 @@ import { ChangeDetectionStrategy, Component, Input, Output, EventEmitter, NgZone, OnInit } from '@angular/core'; import { SeoService } from '../../services/seo.service'; import { ApiService } from '../../services/api.service'; -import { delay, Observable, switchMap, tap, zip } from 'rxjs'; +import { delay, Observable, of, switchMap, tap, zip } from 'rxjs'; import { AssetsService } from '../../services/assets.service'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { StateService } from '../../services/state.service'; import { EChartsOption, echarts } from '../../graphs/echarts'; import { isMobile } from '../../shared/common.utils'; +import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; +import { getFlagEmoji } from '../../shared/common.utils'; +import { lerpColor } from '../../shared/graphs.utils'; @Component({ selector: 'app-nodes-channels-map', @@ -45,11 +48,12 @@ export class NodesChannelsMap implements OnInit { constructor( private seoService: SeoService, private apiService: ApiService, - private stateService: StateService, + public stateService: StateService, private assetsService: AssetsService, private router: Router, private zone: NgZone, private activatedRoute: ActivatedRoute, + private amountShortenerPipe: AmountShortenerPipe, ) { } @@ -86,10 +90,12 @@ export class NodesChannelsMap implements OnInit { return zip( this.assetsService.getWorldMapJson$, this.style !== 'channelpage' ? this.apiService.getChannelsGeo$(params.get('public_key') ?? undefined, this.style) : [''], - [params.get('public_key') ?? undefined] + [params.get('public_key') ?? undefined], + this.style === 'widget' ? of(undefined) : this.apiService.getWorldNodes$(), ).pipe(tap((data) => { echarts.registerMap('world', data[0]); + let maxLiquidity = data[3]?.maxLiquidity; const channelsLoc = []; const nodes = []; const nodesPubkeys = {}; @@ -197,13 +203,24 @@ export class NodesChannelsMap implements OnInit { this.zoom = -0.05 * distance + 8; } - this.prepareChartOptions(nodes, channelsLoc); + if (data[3]) { + for (const node of nodes) { + const foundNode = data[3].nodes.find((n) => n[2] === node[3]); + if (foundNode) { + node.push(foundNode[4], foundNode[5], foundNode[6]?.en, foundNode[7]); + maxLiquidity = Math.max(maxLiquidity ?? 0, foundNode[4]); + } + } + } + + maxLiquidity = Math.max(1, maxLiquidity); + this.prepareChartOptions(nodes, channelsLoc, maxLiquidity); })); }) ); } - prepareChartOptions(nodes, channels) { + prepareChartOptions(nodes, channels, maxLiquidity) { let title: object; if (channels.length === 0) { if (!this.placeholder) { @@ -267,7 +284,12 @@ export class NodesChannelsMap implements OnInit { data: nodes, coordinateSystem: 'geo', geoIndex: 0, - symbolSize: this.nodeSize, + symbolSize: (params) => { + if (maxLiquidity) { + return 10 * Math.pow(params[5] / maxLiquidity, 0.2) + 3; + } + return this.nodeSize; + }, tooltip: { show: true, backgroundColor: 'rgba(17, 19, 31, 1)', @@ -281,11 +303,25 @@ export class NodesChannelsMap implements OnInit { formatter: (value) => { const data = value.data; const alias = data[4].length > 0 ? data[4] : data[3].slice(0, 20); - return `${alias}`; - } + const liquidity = data[5] >= 100000000 ? + `${this.amountShortenerPipe.transform(data[5] / 100000000)} BTC` : + `${this.amountShortenerPipe.transform(data[5], 2)} sats`; + + return ` + ${alias}
    + ${liquidity}
    ` + + $localize`:@@205c1b86ac1cc419c4d0cca51fdde418c4ffdc20:${data[6]}:INTERPOLATION: channels` + `
    + ${getFlagEmoji(data[8])} ${data[7]} + `; + }, }, itemStyle: { - color: 'white', + color: (params) => { + if (!maxLiquidity) { + return 'white'; + } + return `${lerpColor('#1E88E5', '#D81B60', Math.pow(params.data[5] / maxLiquidity, 0.2))}`; + }, opacity: 1, borderColor: 'black', borderWidth: 0, @@ -361,8 +397,6 @@ export class NodesChannelsMap implements OnInit { } chartOptions.series[0].itemStyle.borderWidth = nodeBorder; - chartOptions.series[0].symbolSize += e.zoom > 1 ? speed * 15 : -speed * 15; - chartOptions.series[0].symbolSize = Math.max(4, Math.min(7, chartOptions.series[0].symbolSize)); chartOptions.series[1].lineStyle.opacity += e.zoom > 1 ? speed : -speed; chartOptions.series[1].lineStyle.width += e.zoom > 1 ? speed : -speed; diff --git a/frontend/src/app/lightning/nodes-channels/node-channels.component.html b/frontend/src/app/lightning/nodes-channels/node-channels.component.html index 8fc63793c..db4a3e07e 100644 --- a/frontend/src/app/lightning/nodes-channels/node-channels.component.html +++ b/frontend/src/app/lightning/nodes-channels/node-channels.component.html @@ -1,9 +1,11 @@ -
    -

    Active channels map

    -
    +
    +
    +

    Active channels map

    +
    +
    +
    + +
    +
    - -
    -
    -
    diff --git a/frontend/src/app/lightning/nodes-channels/node-channels.component.scss b/frontend/src/app/lightning/nodes-channels/node-channels.component.scss index 4d7b4de0e..78510203f 100644 --- a/frontend/src/app/lightning/nodes-channels/node-channels.component.scss +++ b/frontend/src/app/lightning/nodes-channels/node-channels.component.scss @@ -1,5 +1,13 @@ +.node-channels-container { + position: relative; +} + .loading-spinner { - min-height: 455px; + position: absolute; + top: 0; + left: 0; + right: 0; + width: 100%; z-index: 100; } diff --git a/frontend/src/app/lightning/nodes-channels/node-channels.component.ts b/frontend/src/app/lightning/nodes-channels/node-channels.component.ts index be596d8f9..1954e429a 100644 --- a/frontend/src/app/lightning/nodes-channels/node-channels.component.ts +++ b/frontend/src/app/lightning/nodes-channels/node-channels.component.ts @@ -33,7 +33,7 @@ export class NodeChannels implements OnChanges { private amountShortenerPipe: AmountShortenerPipe, private zone: NgZone, private router: Router, - private stateService: StateService, + public stateService: StateService, ) {} ngOnChanges(): void { diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.html b/frontend/src/app/lightning/nodes-map/nodes-map.component.html index 7fed096f5..d6b793e25 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.html +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.html @@ -7,8 +7,13 @@ (Tor nodes excluded)
    -
    + +
    +
    +
    +
    +
    diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.scss b/frontend/src/app/lightning/nodes-map/nodes-map.component.scss index a2f62e9c5..82362a257 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.scss +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.scss @@ -63,3 +63,13 @@ .chart.widget { padding: 0px; } + +.loading-spinner { + position: absolute; + top: 50%; + left: calc(50% - 15px); + z-index: 100; + @media (max-width: 767.98px) { + top: 550px; + } +} diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts index 5e655b584..054f1ab6f 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts @@ -26,6 +26,7 @@ export class NodesMap implements OnInit, OnChanges { inputNodes$: BehaviorSubject; nodes$: Observable; observable$: Observable; + isLoading: boolean = true; chartInstance = undefined; chartOptions: EChartsOption = {}; @@ -37,7 +38,7 @@ export class NodesMap implements OnInit, OnChanges { @Inject(LOCALE_ID) public locale: string, private seoService: SeoService, private apiService: ApiService, - private stateService: StateService, + public stateService: StateService, private assetsService: AssetsService, private router: Router, private zone: NgZone, @@ -88,7 +89,7 @@ export class NodesMap implements OnInit, OnChanges { node.public_key, node.alias, node.capacity, - node.active_channel_count, + node.channels, node.country, node.iso_code, ]); @@ -226,6 +227,7 @@ export class NodesMap implements OnInit, OnChanges { }, ] }; + this.isLoading = false; } onChartInit(ec) { @@ -235,6 +237,10 @@ export class NodesMap implements OnInit, OnChanges { this.chartInstance = ec; + this.chartInstance.on('finished', () => { + this.isLoading = false; + }); + this.chartInstance.on('click', (e) => { if (e.data) { this.zone.run(() => { diff --git a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.html b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.html index 908b98fda..7571beacf 100644 --- a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.html +++ b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.html @@ -35,8 +35,8 @@
    -
    -
    +
    +
    diff --git a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss index 0e6fb056d..aaea41265 100644 --- a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss +++ b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss @@ -56,7 +56,6 @@ } .chart-widget { width: 100%; - height: 145px; } .pool-distribution { diff --git a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts index 30f786b16..a7f6da5c2 100644 --- a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts +++ b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding, OnChanges, SimpleChanges } from '@angular/core'; import { echarts, EChartsOption, LineSeriesOption } from '../../graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; @@ -11,6 +11,7 @@ import { SeoService } from '../../services/seo.service'; import { LightningApiService } from '../lightning-api.service'; import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; import { isMobile } from '../../shared/common.utils'; +import { StateService } from '../../services/state.service'; @Component({ selector: 'app-nodes-networks-chart', @@ -26,7 +27,8 @@ import { isMobile } from '../../shared/common.utils'; `], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class NodesNetworksChartComponent implements OnInit { +export class NodesNetworksChartComponent implements OnInit, OnChanges { + @Input() height: number = 150; @Input() right: number | string = 45; @Input() left: number | string = 45; @Input() widget = false; @@ -47,6 +49,9 @@ export class NodesNetworksChartComponent implements OnInit { timespan = ''; chartInstance: any = undefined; + chartData: any; + maxYAxis: number; + constructor( @Inject(LOCALE_ID) public locale: string, private seoService: SeoService, @@ -54,6 +59,7 @@ export class NodesNetworksChartComponent implements OnInit { private formBuilder: UntypedFormBuilder, private storageService: StorageService, private miningService: MiningService, + public stateService: StateService, private amountShortenerPipe: AmountShortenerPipe, ) { } @@ -71,44 +77,49 @@ export class NodesNetworksChartComponent implements OnInit { this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); - this.nodesNetworkObservable$ = this.radioGroupForm.get('dateSpan').valueChanges - .pipe( - startWith(this.miningWindowPreference), - switchMap((timespan) => { - this.timespan = timespan; - if (!this.widget && !firstRun) { - this.storageService.setValue('lightningWindowPreference', timespan); - } - firstRun = false; - this.miningWindowPreference = timespan; - this.isLoading = true; - return this.lightningApiService.cachedRequest(this.lightningApiService.listStatistics$, 250, timespan) - .pipe( - tap((response:any) => { - const data = response.body; - const chartData = { - tor_nodes: data.map(val => [val.added * 1000, val.tor_nodes]), - clearnet_nodes: data.map(val => [val.added * 1000, val.clearnet_nodes]), - unannounced_nodes: data.map(val => [val.added * 1000, val.unannounced_nodes]), - clearnet_tor_nodes: data.map(val => [val.added * 1000, val.clearnet_tor_nodes]), - }; - let maxYAxis = 0; - for (const day of data) { - maxYAxis = Math.max(maxYAxis, day.tor_nodes + day.clearnet_nodes + day.unannounced_nodes + day.clearnet_tor_nodes); - } - maxYAxis = Math.ceil(maxYAxis / 3000) * 3000; - this.prepareChartOptions(chartData, maxYAxis); - this.isLoading = false; - }), - map((response) => { - return { - days: parseInt(response.headers.get('x-total-count'), 10), - }; - }), - ); - }), - share() - ); + this.nodesNetworkObservable$ = this.radioGroupForm.get('dateSpan').valueChanges.pipe( + startWith(this.miningWindowPreference), + switchMap((timespan) => { + this.timespan = timespan; + if (!this.widget && !firstRun) { + this.storageService.setValue('lightningWindowPreference', timespan); + } + firstRun = false; + this.miningWindowPreference = timespan; + this.isLoading = true; + return this.lightningApiService.cachedRequest(this.lightningApiService.listStatistics$, 250, timespan) + .pipe( + tap((response:any) => { + const data = response.body; + this.chartData = { + tor_nodes: data.map(val => [val.added * 1000, val.tor_nodes]), + clearnet_nodes: data.map(val => [val.added * 1000, val.clearnet_nodes]), + unannounced_nodes: data.map(val => [val.added * 1000, val.unannounced_nodes]), + clearnet_tor_nodes: data.map(val => [val.added * 1000, val.clearnet_tor_nodes]), + }; + this.maxYAxis = 0; + for (const day of data) { + this.maxYAxis = Math.max(this.maxYAxis, day.tor_nodes + day.clearnet_nodes + day.unannounced_nodes + day.clearnet_tor_nodes); + } + this.maxYAxis = Math.ceil(this.maxYAxis / 3000) * 3000; + this.prepareChartOptions(this.chartData, this.maxYAxis); + this.isLoading = false; + }), + map((response) => { + return { + days: parseInt(response.headers.get('x-total-count'), 10), + }; + }), + ); + }), + share() + ); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.height && this.chartData && this.maxYAxis != null) { + this.prepareChartOptions(this.chartData, this.maxYAxis); + } } prepareChartOptions(data, maxYAxis): void { @@ -228,7 +239,7 @@ export class NodesNetworksChartComponent implements OnInit { title: title, animation: false, grid: { - height: this.widget ? 90 : undefined, + height: this.widget ? ((this.height || 120) - 60) : undefined, top: this.widget ? 20 : 40, bottom: this.widget ? 0 : 70, right: (isMobile() && this.widget) ? 35 : this.right, diff --git a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.html b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.html index ed42595ef..191a3dbb1 100644 --- a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.html +++ b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.html @@ -12,12 +12,12 @@
    -
    -
    +
    diff --git a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts index e305d8959..5d80341fe 100644 --- a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts @@ -37,7 +37,7 @@ export class NodesPerCountryChartComponent implements OnInit { private seoService: SeoService, private amountShortenerPipe: AmountShortenerPipe, private zone: NgZone, - private stateService: StateService, + public stateService: StateService, private router: Router, ) { } diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html index 9318e925b..be7737894 100644 --- a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html @@ -64,8 +64,8 @@ Channels Location - - + + {{ node.alias }} @@ -116,5 +116,10 @@ + + +
    diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.scss b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.scss index 25e4cf7f3..97d42298c 100644 --- a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.scss +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.scss @@ -22,14 +22,14 @@ .timestamp-first { width: 20%; - @media (max-width: 576px) { + @media (max-width: 1060px) { display: none } } .timestamp-update { width: 16%; - @media (max-width: 576px) { + @media (max-width: 1060px) { display: none } } @@ -50,7 +50,7 @@ .city { max-width: 150px; - @media (max-width: 576px) { + @media (max-width: 675px) { display: none } } \ No newline at end of file diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts index 19dd999ee..619ee01c0 100644 --- a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts @@ -1,6 +1,6 @@ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { map, Observable, share } from 'rxjs'; +import { BehaviorSubject, combineLatest, map, Observable, share, tap } from 'rxjs'; import { ApiService } from '../../services/api.service'; import { SeoService } from '../../services/seo.service'; import { getFlagEmoji } from '../../shared/common.utils'; @@ -15,15 +15,22 @@ import { GeolocationData } from '../../shared/components/geolocation/geolocation export class NodesPerCountry implements OnInit { nodes$: Observable; country: {name: string, flag: string}; + nodesPagination$: Observable; + startingIndexSubject: BehaviorSubject = new BehaviorSubject(0); + page = 1; + pageSize = 15; + maxSize = window.innerWidth <= 767.98 ? 3 : 5; + isLoading = true; skeletonLines: number[] = []; constructor( private apiService: ApiService, private seoService: SeoService, + private cd: ChangeDetectorRef, private route: ActivatedRoute, ) { - for (let i = 0; i < 20; ++i) { + for (let i = 0; i < this.pageSize; ++i) { this.skeletonLines.push(i); } } @@ -31,6 +38,7 @@ export class NodesPerCountry implements OnInit { ngOnInit(): void { this.nodes$ = this.apiService.getNodeForCountry$(this.route.snapshot.params.country) .pipe( + tap(() => this.isLoading = true), map(response => { this.seoService.setTitle($localize`Lightning nodes in ${response.country.en}`); this.seoService.setDescription($localize`:@@meta.description.lightning.nodes-country:Explore all the Lightning nodes hosted in ${response.country.en} and see an overview of each node's capacity, number of open channels, and more.`); @@ -87,11 +95,24 @@ export class NodesPerCountry implements OnInit { ispCount: Object.keys(isps).length }; }), + tap(() => { + this.isLoading = false + this.cd.markForCheck(); + }), share() ); + + this.nodesPagination$ = combineLatest([this.nodes$, this.startingIndexSubject]).pipe( + map(([response, startingIndex]) => response.nodes.slice(startingIndex, startingIndex + this.pageSize)) + ); } trackByPublicKey(index: number, node: any): string { return node.public_key; } + + pageChange(page: number): void { + this.startingIndexSubject.next((page - 1) * this.pageSize); + this.page = page; + } } diff --git a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.html b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.html index 259c3503a..52d74c806 100644 --- a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.html +++ b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.html @@ -39,14 +39,10 @@
    -
    -
    -
    -
    -
    @@ -74,6 +70,9 @@
    +
    +
    +
    diff --git a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts index 8dcd5168a..5599bc255 100644 --- a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts @@ -18,6 +18,7 @@ import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pi changeDetection: ChangeDetectionStrategy.OnPush, }) export class NodesPerISPChartComponent implements OnInit { + @Input() height: number = 300; @Input() widget: boolean = false; isLoading = true; @@ -43,7 +44,7 @@ export class NodesPerISPChartComponent implements OnInit { private amountShortenerPipe: AmountShortenerPipe, private router: Router, private zone: NgZone, - private stateService: StateService, + public stateService: StateService, ) { } diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html index 3daafe4db..865d2d2dd 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html @@ -61,8 +61,8 @@ Channels Location - - + + {{ node.alias }} @@ -113,5 +113,10 @@ + + +
    diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.scss b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.scss index b829c5b59..b043d36f8 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.scss +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.scss @@ -24,7 +24,7 @@ .timestamp-first { width: 20%; - @media (max-width: 576px) { + @media (max-width: 1060px) { display: none } } @@ -32,7 +32,7 @@ .timestamp-update { width: 16%; - @media (max-width: 576px) { + @media (max-width: 1060px) { display: none } } @@ -56,7 +56,7 @@ .city { max-width: 150px; - @media (max-width: 576px) { + @media (max-width: 675px) { display: none } } diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts index d4f27975c..f6c61a9f6 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { map, Observable, share } from 'rxjs'; +import { BehaviorSubject, combineLatest, map, Observable, share, tap } from 'rxjs'; import { ApiService } from '../../services/api.service'; import { SeoService } from '../../services/seo.service'; import { getFlagEmoji } from '../../shared/common.utils'; @@ -15,6 +15,12 @@ import { GeolocationData } from '../../shared/components/geolocation/geolocation export class NodesPerISP implements OnInit { nodes$: Observable; isp: {name: string, id: number}; + nodesPagination$: Observable; + startingIndexSubject: BehaviorSubject = new BehaviorSubject(0); + page = 1; + pageSize = 15; + maxSize = window.innerWidth <= 767.98 ? 3 : 5; + isLoading = true; skeletonLines: number[] = []; @@ -23,7 +29,7 @@ export class NodesPerISP implements OnInit { private seoService: SeoService, private route: ActivatedRoute, ) { - for (let i = 0; i < 20; ++i) { + for (let i = 0; i < this.pageSize; ++i) { this.skeletonLines.push(i); } } @@ -31,6 +37,7 @@ export class NodesPerISP implements OnInit { ngOnInit(): void { this.nodes$ = this.apiService.getNodeForISP$(this.route.snapshot.params.isp) .pipe( + tap(() => this.isLoading = true), map(response => { this.isp = { name: response.isp, @@ -77,11 +84,21 @@ export class NodesPerISP implements OnInit { topCountry: topCountry, }; }), + tap(() => this.isLoading = false), share() ); + + this.nodesPagination$ = combineLatest([this.nodes$, this.startingIndexSubject]).pipe( + map(([response, startingIndex]) => response.nodes.slice(startingIndex, startingIndex + this.pageSize)) + ); } trackByPublicKey(index: number, node: any): string { return node.public_key; } + + pageChange(page: number): void { + this.startingIndexSubject.next((page - 1) * this.pageSize); + this.page = page; + } } diff --git a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.html b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.html index 68fe3ee64..5fd9d257e 100644 --- a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.html +++ b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.html @@ -42,9 +42,9 @@
    -
    -
    +
    diff --git a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss index c885e4839..923a8a3e4 100644 --- a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss +++ b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss @@ -56,7 +56,6 @@ } .chart-widget { width: 100%; - height: 145px; } .pool-distribution { diff --git a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts index 0e4f66ca0..2763c319f 100644 --- a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts +++ b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts @@ -1,6 +1,6 @@ -import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; +import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding, OnChanges, SimpleChanges } from '@angular/core'; import { echarts, EChartsOption } from '../../graphs/echarts'; -import { Observable } from 'rxjs'; +import { Observable, combineLatest, fromEvent } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { SeoService } from '../../services/seo.service'; import { formatNumber } from '@angular/common'; @@ -11,6 +11,7 @@ import { download } from '../../shared/graphs.utils'; import { LightningApiService } from '../lightning-api.service'; import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; import { isMobile } from '../../shared/common.utils'; +import { StateService } from '../../services/state.service'; @Component({ selector: 'app-lightning-statistics-chart', @@ -25,7 +26,8 @@ import { isMobile } from '../../shared/common.utils'; } `], }) -export class LightningStatisticsChartComponent implements OnInit { +export class LightningStatisticsChartComponent implements OnInit, OnChanges { + @Input() height: number = 150; @Input() right: number | string = 45; @Input() left: number | string = 45; @Input() widget = false; @@ -37,6 +39,7 @@ export class LightningStatisticsChartComponent implements OnInit { chartInitOptions = { renderer: 'svg', }; + chartData: any; @HostBinding('attr.dir') dir = 'ltr'; @@ -53,6 +56,7 @@ export class LightningStatisticsChartComponent implements OnInit { private formBuilder: UntypedFormBuilder, private storageService: StorageService, private miningService: MiningService, + public stateService: StateService, private amountShortenerPipe: AmountShortenerPipe, ) { } @@ -70,36 +74,42 @@ export class LightningStatisticsChartComponent implements OnInit { this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); - this.capacityObservable$ = this.radioGroupForm.get('dateSpan').valueChanges - .pipe( - startWith(this.miningWindowPreference), - switchMap((timespan) => { - this.timespan = timespan; - if (!this.widget && !firstRun) { - this.storageService.setValue('lightningWindowPreference', timespan); - } - firstRun = false; - this.miningWindowPreference = timespan; - this.isLoading = true; - return this.lightningApiService.cachedRequest(this.lightningApiService.listStatistics$, 250, timespan) - .pipe( - tap((response:any) => { - const data = response.body; - this.prepareChartOptions({ - channel_count: data.map(val => [val.added * 1000, val.channel_count]), - capacity: data.map(val => [val.added * 1000, val.total_capacity]), - }); - this.isLoading = false; - }), - map((response) => { - return { - days: parseInt(response.headers.get('x-total-count'), 10), - }; - }), - ); - }), - share(), - ); + this.capacityObservable$ = this.radioGroupForm.get('dateSpan').valueChanges.pipe( + startWith(this.miningWindowPreference), + switchMap((timespan) => { + this.timespan = timespan; + if (!this.widget && !firstRun) { + this.storageService.setValue('lightningWindowPreference', timespan); + } + firstRun = false; + this.miningWindowPreference = timespan; + this.isLoading = true; + return this.lightningApiService.cachedRequest(this.lightningApiService.listStatistics$, 250, timespan) + .pipe( + tap((response:any) => { + const data = response.body; + this.chartData = { + channel_count: data.map(val => [val.added * 1000, val.channel_count]), + capacity: data.map(val => [val.added * 1000, val.total_capacity]), + }; + this.prepareChartOptions(this.chartData); + this.isLoading = false; + }), + map((response) => { + return { + days: parseInt(response.headers.get('x-total-count'), 10), + }; + }), + ); + }), + share(), + ); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.height && this.chartData) { + this.prepareChartOptions(this.chartData); + } } prepareChartOptions(data): void { @@ -138,7 +148,7 @@ export class LightningStatisticsChartComponent implements OnInit { ]), ], grid: { - height: this.widget ? 90 : undefined, + height: this.widget ? ((this.height || 120) - 60) : undefined, top: this.widget ? 20 : 40, bottom: this.widget ? 0 : 70, right: (isMobile() && this.widget) ? 35 : this.right, diff --git a/frontend/src/app/liquid/liquid-master-page.module.ts b/frontend/src/app/liquid/liquid-master-page.module.ts index bb6e4cff8..5df9a5447 100644 --- a/frontend/src/app/liquid/liquid-master-page.module.ts +++ b/frontend/src/app/liquid/liquid-master-page.module.ts @@ -2,10 +2,11 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; import { SharedModule } from '../shared/shared.module'; +import { NgxEchartsModule } from 'ngx-echarts'; import { LiquidMasterPageComponent } from '../components/liquid-master-page/liquid-master-page.component'; + import { StartComponent } from '../components/start/start.component'; -import { AddressComponent } from '../components/address/address.component'; import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; import { BlocksList } from '../components/blocks-list/blocks-list.component'; import { AssetGroupComponent } from '../components/assets/asset-group/asset-group.component'; @@ -13,6 +14,12 @@ import { AssetsComponent } from '../components/assets/assets.component'; import { AssetsFeaturedComponent } from '../components/assets/assets-featured/assets-featured.component' import { AssetComponent } from '../components/asset/asset.component'; import { AssetsNavComponent } from '../components/assets/assets-nav/assets-nav.component'; +import { RecentPegsListComponent } from '../components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component'; +import { FederationWalletComponent } from '../components/liquid-reserves-audit/federation-wallet/federation-wallet.component'; +import { FederationUtxosListComponent } from '../components/liquid-reserves-audit/federation-utxos-list/federation-utxos-list.component'; +import { FederationAddressesListComponent } from '../components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component'; +import { ServerHealthComponent } from '../components/server-health/server-health.component'; +import { ServerStatusComponent } from '../components/server-health/server-status.component'; const routes: Routes = [ { @@ -43,15 +50,6 @@ const routes: Routes = [ path: 'trademark-policy', loadChildren: () => import('../components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), }, - { - path: 'address/:id', - children: [], - component: AddressComponent, - data: { - ogImage: true, - networkSpecific: true, - } - }, { path: 'tx', component: StartComponent, @@ -64,6 +62,32 @@ const routes: Routes = [ data: { preload: true, networkSpecific: true }, loadChildren: () => import('../components/block/block.module').then(m => m.BlockModule), }, + { + path: 'audit/wallet', + data: { networks: ['liquid'] }, + component: FederationWalletComponent, + children: [ + { + path: 'utxos', + data: { networks: ['liquid'] }, + component: FederationUtxosListComponent, + }, + { + path: 'addresses', + data: { networks: ['liquid'] }, + component: FederationAddressesListComponent, + }, + { + path: '**', + redirectTo: 'utxos' + } + ] + }, + { + path: 'audit/pegs', + data: { networks: ['liquid'] }, + component: RecentPegsListComponent, + }, { path: 'assets', data: { networks: ['liquid'] }, @@ -108,6 +132,19 @@ const routes: Routes = [ }, ]; +if (window['__env']?.OFFICIAL_MEMPOOL_SPACE) { + routes[0].children.push({ + path: 'nodes', + data: { networks: ['bitcoin', 'liquid'] }, + component: ServerHealthComponent + }); + routes[0].children.push({ + path: 'network', + data: { networks: ['bitcoin', 'liquid'] }, + component: ServerStatusComponent + }); +} + @NgModule({ imports: [ RouterModule.forChild(routes) @@ -123,9 +160,14 @@ export class LiquidRoutingModule { } CommonModule, LiquidRoutingModule, SharedModule, + NgxEchartsModule.forRoot({ + echarts: () => import('../graphs/echarts').then(m => m.echarts), + }) ], declarations: [ LiquidMasterPageComponent, + FederationWalletComponent, + FederationUtxosListComponent, ] }) export class LiquidMasterPageModule { } \ No newline at end of file diff --git a/frontend/src/app/master-page.module.ts b/frontend/src/app/master-page.module.ts index d7ec87030..018809d59 100644 --- a/frontend/src/app/master-page.module.ts +++ b/frontend/src/app/master-page.module.ts @@ -5,11 +5,12 @@ import { MasterPageComponent } from './components/master-page/master-page.compon import { SharedModule } from './shared/shared.module'; import { StartComponent } from './components/start/start.component'; -import { AddressComponent } from './components/address/address.component'; import { PushTransactionComponent } from './components/push-transaction/push-transaction.component'; import { CalculatorComponent } from './components/calculator/calculator.component'; import { BlocksList } from './components/blocks-list/blocks-list.component'; import { RbfList } from './components/rbf-list/rbf-list.component'; +import { ServerHealthComponent } from './components/server-health/server-health.component'; +import { ServerStatusComponent } from './components/server-health/server-status.component'; const browserWindow = window || {}; // @ts-ignore @@ -53,15 +54,6 @@ const routes: Routes = [ path: 'trademark-policy', loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), }, - { - path: 'address/:id', - children: [], - component: AddressComponent, - data: { - ogImage: true, - networkSpecific: true, - } - }, { path: 'tx', component: StartComponent, @@ -96,6 +88,19 @@ const routes: Routes = [ } ]; +if (window['__env']?.OFFICIAL_MEMPOOL_SPACE) { + routes[0].children.push({ + path: 'monitoring', + data: { networks: ['bitcoin', 'liquid'] }, + component: ServerHealthComponent + }); + routes[0].children.push({ + path: 'nodes', + data: { networks: ['bitcoin', 'liquid'] }, + component: ServerStatusComponent + }); +} + @NgModule({ imports: [ RouterModule.forChild(routes) diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 854d15c2a..9879255c1 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -1,17 +1,13 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators, - PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams } from '../interfaces/node-api.interface'; + PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams, CurrentPegs, AuditStatus, FederationAddress, FederationUtxo, RecentPeg, PegsVolume, AccelerationInfo } from '../interfaces/node-api.interface'; import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs'; import { StateService } from './state.service'; -import { IBackendInfo, WebsocketResponse } from '../interfaces/websocket.interface'; -import { Outspend, Transaction } from '../interfaces/electrs.interface'; +import { Transaction } from '../interfaces/electrs.interface'; import { Conversion } from './price.service'; -import { MenuGroup } from '../interfaces/services.interface'; import { StorageService } from './storage.service'; - -// Todo - move to config.json -const SERVICES_API_PREFIX = `/api/v1/services`; +import { WebsocketResponse } from '../interfaces/websocket.interface'; @Injectable({ providedIn: 'root' @@ -33,17 +29,8 @@ export class ApiService { } this.apiBasePath = ''; // assume mainnet by default this.stateService.networkChanged$.subscribe((network) => { - if (network === 'bisq' && !this.stateService.env.BISQ_SEPARATE_BACKEND) { - network = ''; - } this.apiBasePath = network ? '/' + network : ''; }); - - if (this.stateService.env.GIT_COMMIT_HASH_MEMPOOL_SPACE) { - this.getServicesBackendInfo$().subscribe(version => { - this.stateService.servicesBackendInfo$.next(version); - }) - } } private generateCacheKey(functionName: string, params: any[]): string { @@ -178,12 +165,69 @@ export class ApiService { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/' + (fullRbf ? 'fullrbf/' : '') + 'replacements/' + (after || '')); } + liquidPegs$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs'); + } + + pegsVolume$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/volume'); + } + listLiquidPegsMonth$(): Observable { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/month'); } - listFeaturedAssets$(): Observable { - return this.httpClient.get(this.apiBaseUrl + '/api/v1/assets/featured'); + liquidReserves$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves'); + } + + listLiquidReservesMonth$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves/month'); + } + + federationAuditSynced$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves/status'); + } + + federationAddresses$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves/addresses'); + } + + federationUtxos$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves/utxos'); + } + + expiredUtxos$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves/utxos/expired'); + } + + emergencySpentUtxos$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves/utxos/emergency-spent'); + } + + recentPegsList$(count: number = 0): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/list/' + count); + } + + pegsCount$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/count'); + } + + federationAddressesNumber$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves/addresses/total'); + } + + federationUtxosNumber$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves/utxos/total'); + } + + emergencySpentUtxosStats$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves/utxos/emergency-spent/stats'); + } + + listFeaturedAssets$(network: string = 'liquid'): Observable { + if (network === 'liquid') return this.httpClient.get(this.apiBaseUrl + '/api/v1/assets/featured'); + return of([]); } getAssetGroup$(id: string): Observable { @@ -194,6 +238,10 @@ export class ApiService { return this.httpClient.post(this.apiBaseUrl + this.apiBasePath + '/api/tx', hexPayload, { responseType: 'text' as 'json'}); } + getTransactionStatus$(txid: string): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/tx/' + txid + '/status'); + } + listPools$(interval: string | undefined) : Observable { return this.httpClient.get( this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/pools` + @@ -359,7 +407,7 @@ export class ApiService { ); } - getHistoricalPrice$(timestamp: number | undefined): Observable { + getHistoricalPrice$(timestamp: number | undefined, currency?: string): Observable { if (this.stateService.isAnyTestnet()) { return of({ prices: [], @@ -370,70 +418,79 @@ export class ApiService { USDCHF: 0, USDAUD: 0, USDJPY: 0, + USDBGN: 0, + USDBRL: 0, + USDCNY: 0, + USDCZK: 0, + USDDKK: 0, + USDHKD: 0, + USDHRK: 0, + USDHUF: 0, + USDIDR: 0, + USDILS: 0, + USDINR: 0, + USDISK: 0, + USDKRW: 0, + USDMXN: 0, + USDMYR: 0, + USDNOK: 0, + USDNZD: 0, + USDPHP: 0, + USDPLN: 0, + USDRON: 0, + USDRUB: 0, + USDSEK: 0, + USDSGD: 0, + USDTHB: 0, + USDTRY: 0, + USDZAR: 0, } }); } + const queryParams = []; + + if (timestamp) { + queryParams.push(`timestamp=${timestamp}`); + } + + if (currency) { + queryParams.push(`currency=${currency}`); + } return this.httpClient.get( - this.apiBaseUrl + this.apiBasePath + '/api/v1/historical-price' + - (timestamp ? `?timestamp=${timestamp}` : '') + `${this.apiBaseUrl}${this.apiBasePath}/api/v1/historical-price` + + (queryParams.length > 0 ? `?${queryParams.join('&')}` : '') ); } - /** - * Services - */ - - getNodeOwner$(publicKey: string): Observable { - let params = new HttpParams() - .set('node_public_key', publicKey); - return this.httpClient.get(`${SERVICES_API_PREFIX}/lightning/claim/current`, { params, observe: 'response' }); + getAccelerationsByPool$(slug: string): Observable { + return this.httpClient.get( + this.apiBaseUrl + this.apiBasePath + `/api/v1/accelerations/pool/${slug}` + ); } - getUserMenuGroups$(): Observable { - const auth = this.storageService.getAuth(); - if (!auth) { - return of(null); + getAccelerationsByHeight$(height: number): Observable { + return this.httpClient.get( + this.apiBaseUrl + this.apiBasePath + `/api/v1/accelerations/block/${height}` + ); + } + + getRecentAccelerations$(interval: string | undefined): Observable { + return this.httpClient.get( + this.apiBaseUrl + this.apiBasePath + '/api/v1/accelerations/interval' + (interval !== undefined ? `/${interval}` : '') + ); + } + + getAccelerationTotals$(pool?: string, interval?: string): Observable<{ cost: number, count: number }> { + const queryParams = new URLSearchParams(); + if (pool) { + queryParams.append('pool', pool); } - - return this.httpClient.get(`${SERVICES_API_PREFIX}/account/menu`); - } - - getUserInfo$(): Observable { - const auth = this.storageService.getAuth(); - if (!auth) { - return of(null); + if (interval) { + queryParams.append('interval', interval); } - - return this.httpClient.get(`${SERVICES_API_PREFIX}/account`); - } - - logout$(): Observable { - const auth = this.storageService.getAuth(); - if (!auth) { - return of(null); - } - - localStorage.removeItem('auth'); - return this.httpClient.post(`${SERVICES_API_PREFIX}/auth/logout`, {}); - } - - getServicesBackendInfo$(): Observable { - return this.httpClient.get(`${SERVICES_API_PREFIX}/version`); - } - - estimate$(txInput: string) { - return this.httpClient.post(`${SERVICES_API_PREFIX}/accelerator/estimate`, { txInput: txInput }, { observe: 'response' }); - } - - accelerate$(txInput: string, userBid: number) { - return this.httpClient.post(`${SERVICES_API_PREFIX}/accelerator/accelerate`, { txInput: txInput, userBid: userBid }); - } - - getAccelerations$(): Observable { - return this.httpClient.get(`${SERVICES_API_PREFIX}/accelerator/accelerations`); - } - - getAccelerationHistory$(params: AccelerationHistoryParams): Observable { - return this.httpClient.get(`${SERVICES_API_PREFIX}/accelerator/accelerations/history`, { params: { ...params } }); + const queryString = queryParams.toString(); + return this.httpClient.get<{ cost: number, count: number }>( + this.apiBaseUrl + this.apiBasePath + '/api/v1/accelerations/total' + (queryString?.length ? '?' + queryString : '') + ); } } diff --git a/frontend/src/app/services/electrs-api.service.ts b/frontend/src/app/services/electrs-api.service.ts index eaa1ab52d..57dcbb762 100644 --- a/frontend/src/app/services/electrs-api.service.ts +++ b/frontend/src/app/services/electrs-api.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; import { BehaviorSubject, Observable, catchError, filter, from, of, shareReplay, switchMap, take, tap } from 'rxjs'; -import { Transaction, Address, Outspend, Recent, Asset, ScriptHash } from '../interfaces/electrs.interface'; +import { Transaction, Address, Outspend, Recent, Asset, ScriptHash, AddressTxSummary } from '../interfaces/electrs.interface'; import { StateService } from './state.service'; import { BlockExtended } from '../interfaces/node-api.interface'; import { calcScriptHash$ } from '../bitcoin.utils'; @@ -25,9 +25,6 @@ export class ElectrsApiService { } this.apiBasePath = ''; // assume mainnet by default this.stateService.networkChanged$.subscribe((network) => { - if (network === 'bisq') { - network = ''; - } this.apiBasePath = network ? '/' + network : ''; }); } @@ -141,6 +138,14 @@ export class ElectrsApiService { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address + '/txs', { params }); } + getAddressSummary$(address: string, txid?: string): Observable { + let params = new HttpParams(); + if (txid) { + params = params.append('after_txid', txid); + } + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address + '/txs/summary', { params }); + } + getScriptHashTransactions$(script: string, txid?: string): Observable { let params = new HttpParams(); if (txid) { @@ -151,6 +156,16 @@ export class ElectrsApiService { ); } + getScriptHashSummary$(script: string, txid?: string): Observable { + let params = new HttpParams(); + if (txid) { + params = params.append('after_txid', txid); + } + return from(calcScriptHash$(script)).pipe( + switchMap(scriptHash => this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/scripthash/' + scriptHash + '/txs/summary', { params })), + ); + } + getAsset$(assetId: string): Observable { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/asset/' + assetId); } diff --git a/frontend/src/app/services/enterprise.service.ts b/frontend/src/app/services/enterprise.service.ts index 7e69af223..4ad31bd9f 100644 --- a/frontend/src/app/services/enterprise.service.ts +++ b/frontend/src/app/services/enterprise.service.ts @@ -4,6 +4,7 @@ import { ApiService } from './api.service'; import { SeoService } from './seo.service'; import { StateService } from './state.service'; import { ActivatedRoute } from '@angular/router'; +import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' @@ -11,9 +12,9 @@ import { ActivatedRoute } from '@angular/router'; export class EnterpriseService { exclusiveHostName = '.mempool.space'; subdomain: string | null = null; - info: object = {}; statsUrl: string; siteId: number; + info$: BehaviorSubject = new BehaviorSubject(null); constructor( @Inject(DOCUMENT) private document: Document, @@ -28,6 +29,7 @@ export class EnterpriseService { this.subdomain = subdomain; this.fetchSubdomainInfo(); this.disableSubnetworks(); + this.stateService.env.ACCELERATOR = false; } else { this.insertMatomo(); } @@ -42,14 +44,13 @@ export class EnterpriseService { this.stateService.env.LIQUID_ENABLED = false; this.stateService.env.LIQUID_TESTNET_ENABLED = false; this.stateService.env.SIGNET_ENABLED = false; - this.stateService.env.BISQ_ENABLED = false; } fetchSubdomainInfo(): void { this.apiService.getEnterpriseInfo$(this.subdomain).subscribe((info) => { - this.info = info; this.insertMatomo(info.site_id); this.seoService.setEnterpriseTitle(info.title); + this.info$.next(info); }, (error) => { if (error.status === 404) { @@ -79,14 +80,6 @@ export class EnterpriseService { siteId = 10; statsUrl = '//stats.liquid.network/'; break; - case 'bisq.markets': - siteId = 7; - statsUrl = '//stats.bisq.markets/'; - break; - case 'bisq.ninja': - statsUrl = '//stats.bisq.markets/'; - siteId = 11; - break; default: return; } @@ -139,6 +132,14 @@ export class EnterpriseService { this.getMatomo()?.trackGoal(id); } + page() { + const matomo = this.getMatomo(); + if (matomo) { + matomo.setCustomUrl(this.getCustomUrl()); + matomo.trackPageView(); + } + } + private getCustomUrl(): string { let url = window.location.origin + '/'; let route = this.activatedRoute; diff --git a/frontend/src/app/services/http-cache.interceptor.ts b/frontend/src/app/services/http-cache.interceptor.ts index 289919534..da683325e 100644 --- a/frontend/src/app/services/http-cache.interceptor.ts +++ b/frontend/src/app/services/http-cache.interceptor.ts @@ -1,8 +1,8 @@ -import { Inject, Injectable, PLATFORM_ID } from '@angular/core'; -import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler, HttpResponse } from '@angular/common/http'; +import { Inject, Injectable, PLATFORM_ID, makeStateKey, TransferState } from '@angular/core'; +import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler, HttpResponse, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; import { Observable, of } from 'rxjs'; -import { tap } from 'rxjs/operators'; -import { TransferState, makeStateKey } from '@angular/platform-browser'; +import { catchError, tap } from 'rxjs/operators'; + import { isPlatformBrowser } from '@angular/common'; @Injectable() @@ -17,14 +17,18 @@ export class HttpCacheInterceptor implements HttpInterceptor { intercept(request: HttpRequest, next: HttpHandler): Observable> { if (this.isBrowser && request.method === 'GET') { - const cachedResponse = this.transferState.get(makeStateKey(request.url), null); - if (cachedResponse) { + const { response, headers } = this.transferState.get(makeStateKey(request.url), null) || {}; + if (response) { + const httpHeaders = new HttpHeaders(); + for (const [k,v] of Object.entries(headers)) { + httpHeaders.set(k,v as string[]); + } const modifiedResponse = new HttpResponse({ - headers: cachedResponse.headers, - body: cachedResponse.body, - status: cachedResponse.status, - statusText: cachedResponse.statusText, - url: cachedResponse.url + headers: httpHeaders, + body: response.body, + status: response.status, + statusText: response.statusText, + url: response.url }); this.transferState.remove(makeStateKey(request.url)); return of(modifiedResponse); @@ -32,11 +36,41 @@ export class HttpCacheInterceptor implements HttpInterceptor { } return next.handle(request) - .pipe(tap((event: HttpEvent) => { - if (!this.isBrowser && event instanceof HttpResponse) { - let keyId = request.url.split('/').slice(3).join('/'); - this.transferState.set(makeStateKey('/' + keyId), event); - } - })); + .pipe( + tap((event: HttpEvent) => { + if (!this.isBrowser && event instanceof HttpResponse) { + let keyId = request.url.split('/').slice(3).join('/'); + const headers = {}; + for (const k of event.headers.keys()) { + headers[k] = event.headers.getAll(k); + } + this.transferState.set(makeStateKey('/' + keyId), { response: event, headers }); + } + }), + catchError((e) => { + if (e instanceof HttpErrorResponse) { + if (e.status === 0) { + throw new HttpErrorResponse({ + error: 'Unknown error', + headers: e.headers, + status: 0, + statusText: 'Unknown error', + url: e.url, + }); + } else { + throw e; + } + } else { + const msg = e?.['message'] || 'Unknown error'; + throw new HttpErrorResponse({ + error: msg, + headers: new HttpHeaders(), + status: 0, + statusText: msg, + url: '', + }); + } + }) + ); } } diff --git a/frontend/src/app/services/navigation.service.ts b/frontend/src/app/services/navigation.service.ts index 661a8c38f..2db3f6025 100644 --- a/frontend/src/app/services/navigation.service.ts +++ b/frontend/src/app/services/navigation.service.ts @@ -17,12 +17,7 @@ const networkModules = { { name: 'liquid', path: '' }, { name: 'liquidtestnet', path: '/testnet' }, ], - }, - bisq: { - subnets: [ - { name: 'bisq', path: '' }, - ], - }, + } }; const networks = Object.keys(networkModules); @@ -44,7 +39,7 @@ export class NavigationService { }); } - // For each network (bitcoin/liquid/bisq), find and save the longest url path compatible with the current route + // For each network (bitcoin/liquid), find and save the longest url path compatible with the current route updateSubnetPaths(root: ActivatedRouteSnapshot): void { let path = ''; const networkPaths = {}; diff --git a/frontend/src/app/services/opengraph.service.ts b/frontend/src/app/services/opengraph.service.ts index 9e2fef781..5e429ed70 100644 --- a/frontend/src/app/services/opengraph.service.ts +++ b/frontend/src/app/services/opengraph.service.ts @@ -25,7 +25,7 @@ export class OpenGraphService { ) { // save og:image tag from original template const initialOgImageTag = metaService.getTag("property='og:image'"); - this.defaultImageUrl = initialOgImageTag?.content || 'https://mempool.space/resources/mempool-space-preview.png'; + this.defaultImageUrl = initialOgImageTag?.content || 'https://mempool.space/resources/previews/mempool-space-preview.jpg'; this.router.events.pipe( filter(event => event instanceof NavigationEnd), map(() => this.activatedRoute), @@ -53,7 +53,7 @@ export class OpenGraphService { const lang = this.LanguageService.getLanguage(); const ogImageUrl = `${window.location.protocol}//${window.location.host}/render/${lang}/preview${this.router.url}`; this.metaService.updateTag({ property: 'og:image', content: ogImageUrl }); - this.metaService.updateTag({ property: 'twitter:image:src', content: ogImageUrl }); + this.metaService.updateTag({ name: 'twitter:image', content: ogImageUrl }); this.metaService.updateTag({ property: 'og:image:type', content: 'image/png' }); this.metaService.updateTag({ property: 'og:image:width', content: '1200' }); this.metaService.updateTag({ property: 'og:image:height', content: '600' }); @@ -61,12 +61,21 @@ export class OpenGraphService { clearOgImage() { this.metaService.updateTag({ property: 'og:image', content: this.defaultImageUrl }); - this.metaService.updateTag({ property: 'twitter:image:src', content: this.defaultImageUrl }); + this.metaService.updateTag({ name: 'twitter:image', content: this.defaultImageUrl }); this.metaService.updateTag({ property: 'og:image:type', content: 'image/png' }); this.metaService.updateTag({ property: 'og:image:width', content: '1000' }); this.metaService.updateTag({ property: 'og:image:height', content: '500' }); } + setManualOgImage(imageFilename) { + const ogImage = `${window.location.protocol}//${window.location.host}/resources/previews/${imageFilename}`; + this.metaService.updateTag({ property: 'og:image', content: ogImage }); + this.metaService.updateTag({ property: 'og:image:type', content: 'image/jpeg' }); + this.metaService.updateTag({ property: 'og:image:width', content: '2000' }); + this.metaService.updateTag({ property: 'og:image:height', content: '1000' }); + this.metaService.updateTag({ name: 'twitter:image', content: ogImage }); + } + /// register an event that needs to resolve before we can take a screenshot waitFor(event) { if (!this.previewLoadingEvents[event]) { diff --git a/frontend/src/app/services/price.service.ts b/frontend/src/app/services/price.service.ts index 4236205ca..a27c65df8 100644 --- a/frontend/src/app/services/price.service.ts +++ b/frontend/src/app/services/price.service.ts @@ -13,6 +13,32 @@ export interface ApiPrice { CHF: number, AUD: number, JPY: number, + BGN?: number, + BRL?: number, + CNY?: number, + CZK?: number, + DKK?: number, + HKD?: number, + HRK?: number, + HUF?: number, + IDR?: number, + ILS?: number, + INR?: number, + ISK?: number, + KRW?: number, + MXN?: number, + MYR?: number, + NOK?: number, + NZD?: number, + PHP?: number, + PLN?: number, + RON?: number, + RUB?: number, + SEK?: number, + SGD?: number, + THB?: number, + TRY?: number, + ZAR?: number, } export interface ExchangeRates { USDEUR: number, @@ -21,6 +47,32 @@ export interface ExchangeRates { USDCHF: number, USDAUD: number, USDJPY: number, + USDBGN?: number, + USDBRL?: number, + USDCNY?: number, + USDCZK?: number, + USDDKK?: number, + USDHKD?: number, + USDHRK?: number, + USDHUF?: number, + USDIDR?: number, + USDILS?: number, + USDINR?: number, + USDISK?: number, + USDKRW?: number, + USDMXN?: number, + USDMYR?: number, + USDNOK?: number, + USDNZD?: number, + USDPHP?: number, + USDPLN?: number, + USDRON?: number, + USDRUB?: number, + USDSEK?: number, + USDSGD?: number, + USDTHB?: number, + USDTRY?: number, + USDZAR?: number, } export interface Conversion { prices: ApiPrice[], @@ -46,6 +98,12 @@ export class PriceService { lastQueriedTimestamp: number; lastPriceHistoryUpdate: number; + lastQueriedCurrency: string; + lastQueriedHistoricalCurrency: string; + + network: string; + networkChangedSinceLastQuery = false; + networkChangedSinceLastSingleQuery = false; historicalPrice: ConversionDict = { prices: null, @@ -56,20 +114,36 @@ export class PriceService { private apiService: ApiService, private stateService: StateService ) { + this.stateService.networkChanged$.subscribe((network: string) => { + if (this.network !== network) { + this.network = network; + this.networkChangedSinceLastQuery = true; + this.networkChangedSinceLastSingleQuery = true; + } + }); } getEmptyPrice(): Price { return { - price: { + price: this.stateService.env.ADDITIONAL_CURRENCIES ? { + USD: 0, EUR: 0, GBP: 0, CAD: 0, CHF: 0, AUD: 0, JPY: 0, BGN: 0, BRL: 0, CNY: 0, CZK: 0, DKK: 0, HKD: 0, HRK: 0, HUF: 0, IDR: 0, + ILS: 0, INR: 0, ISK: 0, KRW: 0, MXN: 0, MYR: 0, NOK: 0, NZD: 0, PHP: 0, PLN: 0, RON: 0, RUB: 0, SEK: 0, SGD: 0, THB: 0, TRY: 0, + ZAR: 0 + } : + { USD: 0, EUR: 0, GBP: 0, CAD: 0, CHF: 0, AUD: 0, JPY: 0, }, - exchangeRates: { + exchangeRates: this.stateService.env.ADDITIONAL_CURRENCIES ? { + USDEUR: 0, USDGBP: 0, USDCAD: 0, USDCHF: 0, USDAUD: 0, USDJPY: 0, USDBGN: 0, USDBRL: 0, USDCNY: 0, USDCZK: 0, USDDKK: 0, USDHKD: 0, + USDHRK: 0, USDHUF: 0, USDIDR: 0, USDILS: 0, USDINR: 0, USDISK: 0, USDKRW: 0, USDMXN: 0, USDMYR: 0, USDNOK: 0, USDNZD: 0, USDPHP: 0, + USDPLN: 0, USDRON: 0, USDRUB: 0, USDSEK: 0, USDSGD: 0, USDTHB: 0, USDTRY: 0, USDZAR: 0 + } : { USDEUR: 0, USDGBP: 0, USDCAD: 0, USDCHF: 0, USDAUD: 0, USDJPY: 0, }, }; } - getBlockPrice$(blockTimestamp: number, singlePrice = false): Observable { + getBlockPrice$(blockTimestamp: number, singlePrice = false, currency: string): Observable { if (this.stateService.env.BASE_MODULE !== 'mempool' || !this.stateService.env.HISTORICAL_PRICE) { return of(undefined); } @@ -81,9 +155,11 @@ export class PriceService { * query a different timestamp than the last one */ if (singlePrice) { - if (!this.singlePriceObservable$ || (this.singlePriceObservable$ && blockTimestamp !== this.lastQueriedTimestamp)) { - this.singlePriceObservable$ = this.apiService.getHistoricalPrice$(blockTimestamp).pipe(shareReplay()); + if (!this.singlePriceObservable$ || (this.singlePriceObservable$ && (blockTimestamp !== this.lastQueriedTimestamp || currency !== this.lastQueriedCurrency || this.networkChangedSinceLastSingleQuery))) { + this.singlePriceObservable$ = this.apiService.getHistoricalPrice$(blockTimestamp, currency).pipe(shareReplay()); this.lastQueriedTimestamp = blockTimestamp; + this.lastQueriedCurrency = currency; + this.networkChangedSinceLastSingleQuery = false; } return this.singlePriceObservable$.pipe( @@ -92,7 +168,17 @@ export class PriceService { return undefined; } return { - price: { + price: this.stateService.env.ADDITIONAL_CURRENCIES ? { + USD: conversion.prices[0].USD, EUR: conversion.prices[0].EUR, GBP: conversion.prices[0].GBP, CAD: conversion.prices[0].CAD, + CHF: conversion.prices[0].CHF, AUD: conversion.prices[0].AUD, JPY: conversion.prices[0].JPY, BGN: conversion.prices[0].BGN, + BRL: conversion.prices[0].BRL, CNY: conversion.prices[0].CNY, CZK: conversion.prices[0].CZK, DKK: conversion.prices[0].DKK, + HKD: conversion.prices[0].HKD, HRK: conversion.prices[0].HRK, HUF: conversion.prices[0].HUF, IDR: conversion.prices[0].IDR, + ILS: conversion.prices[0].ILS, INR: conversion.prices[0].INR, ISK: conversion.prices[0].ISK, KRW: conversion.prices[0].KRW, + MXN: conversion.prices[0].MXN, MYR: conversion.prices[0].MYR, NOK: conversion.prices[0].NOK, NZD: conversion.prices[0].NZD, + PHP: conversion.prices[0].PHP, PLN: conversion.prices[0].PLN, RON: conversion.prices[0].RON, RUB: conversion.prices[0].RUB, + SEK: conversion.prices[0].SEK, SGD: conversion.prices[0].SGD, THB: conversion.prices[0].THB, TRY: conversion.prices[0].TRY, + ZAR: conversion.prices[0].ZAR + } : { USD: conversion.prices[0].USD, EUR: conversion.prices[0].EUR, GBP: conversion.prices[0].GBP, CAD: conversion.prices[0].CAD, CHF: conversion.prices[0].CHF, AUD: conversion.prices[0].AUD, JPY: conversion.prices[0].JPY }, @@ -106,9 +192,11 @@ export class PriceService { * Query all price history only once. The observable is invalidated after 1 hour */ else { - if (!this.priceObservable$ || (this.priceObservable$ && (now - this.lastPriceHistoryUpdate > 3600))) { - this.priceObservable$ = this.apiService.getHistoricalPrice$(undefined).pipe(shareReplay()); + if (!this.priceObservable$ || (this.priceObservable$ && (now - this.lastPriceHistoryUpdate > 3600 || currency !== this.lastQueriedHistoricalCurrency || this.networkChangedSinceLastQuery))) { + this.priceObservable$ = this.apiService.getHistoricalPrice$(undefined, currency).pipe(shareReplay()); this.lastPriceHistoryUpdate = new Date().getTime() / 1000; + this.lastQueriedHistoricalCurrency = currency; + this.networkChangedSinceLastQuery = false; } return this.priceObservable$.pipe( @@ -122,9 +210,15 @@ export class PriceService { exchangeRates: conversion.exchangeRates, }; for (const price of conversion.prices) { - historicalPrice.prices[price.time] = { - USD: price.USD, EUR: price.EUR, GBP: price.GBP, CAD: price.CAD, - CHF: price.CHF, AUD: price.AUD, JPY: price.JPY + historicalPrice.prices[price.time] = this.stateService.env.ADDITIONAL_CURRENCIES ? { + USD: price.USD, EUR: price.EUR, GBP: price.GBP, CAD: price.CAD, CHF: price.CHF, AUD: price.AUD, + JPY: price.JPY, BGN: price.BGN, BRL: price.BRL, CNY: price.CNY, CZK: price.CZK, DKK: price.DKK, + HKD: price.HKD, HRK: price.HRK, HUF: price.HUF, IDR: price.IDR, ILS: price.ILS, INR: price.INR, + ISK: price.ISK, KRW: price.KRW, MXN: price.MXN, MYR: price.MYR, NOK: price.NOK, NZD: price.NZD, + PHP: price.PHP, PLN: price.PLN, RON: price.RON, RUB: price.RUB, SEK: price.SEK, SGD: price.SGD, + THB: price.THB, TRY: price.TRY, ZAR: price.ZAR + } : { + USD: price.USD, EUR: price.EUR, GBP: price.GBP, CAD: price.CAD, CHF: price.CHF, AUD: price.AUD, JPY: price.JPY }; } diff --git a/frontend/src/app/services/seo.service.ts b/frontend/src/app/services/seo.service.ts index 7830690ff..45d62ebdd 100644 --- a/frontend/src/app/services/seo.service.ts +++ b/frontend/src/app/services/seo.service.ts @@ -39,14 +39,14 @@ export class SeoService { setTitle(newTitle: string): void { this.titleService.setTitle(newTitle + ' - ' + this.getTitle()); this.metaService.updateTag({ property: 'og:title', content: newTitle}); - this.metaService.updateTag({ property: 'twitter:title', content: newTitle}); + this.metaService.updateTag({ name: 'twitter:title', content: newTitle}); this.metaService.updateTag({ property: 'og:meta:ready', content: 'ready'}); } resetTitle(): void { this.titleService.setTitle(this.getTitle()); this.metaService.updateTag({ property: 'og:title', content: this.getTitle()}); - this.metaService.updateTag({ property: 'twitter:title', content: this.getTitle()}); + this.metaService.updateTag({ name: 'twitter:title', content: this.getTitle()}); this.metaService.updateTag({ property: 'og:meta:ready', content: 'ready'}); } @@ -71,8 +71,6 @@ export class SeoService { let domain = 'mempool.space'; if (this.stateService.env.BASE_MODULE === 'liquid') { domain = 'liquid.network'; - } else if (this.stateService.env.BASE_MODULE === 'bisq') { - domain = 'bisq.markets'; } this.canonicalLink.setAttribute('href', 'https://' + domain + path); } @@ -86,8 +84,6 @@ export class SeoService { return this.baseTitle + ' - Liquid Network'; if (this.network === 'liquidtestnet') return this.baseTitle + ' - Liquid Testnet'; - if (this.network === 'bisq') - return this.baseTitle + ' - Bisq Markets'; return this.baseTitle + ' - ' + (this.network ? this.ucfirst(this.network) : 'Bitcoin') + ' Explorer'; } @@ -96,8 +92,6 @@ export class SeoService { return this.baseDescription + ' See the real-time status of your transactions, browse network stats, and more.'; if ( (this.network === 'liquid') || (this.network === 'liquidtestnet') ) return this.baseDescription + ' See Liquid transactions & assets, get network info, and more.'; - if (this.network === 'bisq') - return this.baseDescription + ' See Bisq market prices, trading activity, and more.'; } ucfirst(str: string) { diff --git a/frontend/src/app/services/services-api.service.ts b/frontend/src/app/services/services-api.service.ts new file mode 100644 index 000000000..0caa06168 --- /dev/null +++ b/frontend/src/app/services/services-api.service.ts @@ -0,0 +1,154 @@ +import { Router, NavigationStart } from '@angular/router'; +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { StateService } from './state.service'; +import { StorageService } from './storage.service'; +import { MenuGroup } from '../interfaces/services.interface'; +import { Observable, of, ReplaySubject, tap, catchError, share, filter, switchMap } from 'rxjs'; +import { IBackendInfo } from '../interfaces/websocket.interface'; +import { Acceleration, AccelerationHistoryParams } from '../interfaces/node-api.interface'; +import { AccelerationStats } from '../components/acceleration/acceleration-stats/acceleration-stats.component'; + +export type ProductType = 'enterprise' | 'community' | 'mining_pool' | 'custom'; +export interface IUser { + username: string; + email: string | null; + passwordIsSet: boolean; + snsId: string; + type: ProductType; + subscription_tag: string; + status: 'pending' | 'verified' | 'disabled'; + features: string | null; + fullName: string | null; + countryCode: string | null; + imageMd5: string; + ogRank: number | null; +} + +// Todo - move to config.json +const SERVICES_API_PREFIX = `/api/v1/services`; + +@Injectable({ + providedIn: 'root' +}) +export class ServicesApiServices { + apiBaseUrl: string; // base URL is protocol, hostname, and port + apiBasePath: string; // network path is /testnet, etc. or '' for mainnet + + userSubject$ = new ReplaySubject(1); + currentAuth = null; + + constructor( + private httpClient: HttpClient, + private stateService: StateService, + private storageService: StorageService, + private router: Router, + ) { + this.currentAuth = localStorage.getItem('auth'); + + this.apiBaseUrl = ''; // use relative URL by default + if (!stateService.isBrowser) { // except when inside AU SSR process + this.apiBaseUrl = this.stateService.env.NGINX_PROTOCOL + '://' + this.stateService.env.NGINX_HOSTNAME + ':' + this.stateService.env.NGINX_PORT; + } + this.apiBasePath = ''; // assume mainnet by default + this.stateService.networkChanged$.subscribe((network) => { + this.apiBasePath = network ? '/' + network : ''; + }); + + if (this.stateService.env.GIT_COMMIT_HASH_MEMPOOL_SPACE) { + this.getServicesBackendInfo$().subscribe(version => { + this.stateService.servicesBackendInfo$.next(version); + }) + } + + this.getUserInfo$().subscribe(); + this.router.events.pipe( + filter((event) => event instanceof NavigationStart && this.currentAuth !== localStorage.getItem('auth')), + switchMap(() => this.getUserInfo$()), + ).subscribe(); + } + + /** + * Do not call directly, userSubject$ instead + */ + private getUserInfo$() { + return this.getUserInfoApi$().pipe( + tap((user) => { + this.userSubject$.next(user); + }), + catchError((e) => { + if (e.error === 'User does not exists') { + this.userSubject$.next(null); + this.logout$().subscribe(); + return of(null); + } + this.userSubject$.next(null); + return of(null); + }), + share(), + ) + } + + /** + * Do not call directly, userSubject$ instead + */ + private getUserInfoApi$(): Observable { + const auth = this.storageService.getAuth(); + if (!auth) { + return of(null); + } + + return this.httpClient.get(`${SERVICES_API_PREFIX}/account`); + } + + getUserMenuGroups$(): Observable { + const auth = this.storageService.getAuth(); + if (!auth) { + return of(null); + } + + return this.httpClient.get(`${SERVICES_API_PREFIX}/account/menu`); + } + + logout$(): Observable { + const auth = this.storageService.getAuth(); + if (!auth) { + return of(null); + } + + localStorage.removeItem('auth'); + return this.httpClient.post(`${SERVICES_API_PREFIX}/auth/logout`, {}); + } + + getServicesBackendInfo$(): Observable { + return this.httpClient.get(`${SERVICES_API_PREFIX}/version`); + } + + estimate$(txInput: string) { + return this.httpClient.post(`${SERVICES_API_PREFIX}/accelerator/estimate`, { txInput: txInput }, { observe: 'response' }); + } + + accelerate$(txInput: string, userBid: number) { + return this.httpClient.post(`${SERVICES_API_PREFIX}/accelerator/accelerate`, { txInput: txInput, userBid: userBid }); + } + + getAccelerations$(): Observable { + return this.httpClient.get(`${SERVICES_API_PREFIX}/accelerator/accelerations`); + } + + getAggregatedAccelerationHistory$(params: AccelerationHistoryParams): Observable { + return this.httpClient.get(`${SERVICES_API_PREFIX}/accelerator/accelerations/history/aggregated`, { params: { ...params }, observe: 'response' }); + } + + getAccelerationHistory$(params: AccelerationHistoryParams): Observable { + return this.httpClient.get(`${SERVICES_API_PREFIX}/accelerator/accelerations/history`, { params: { ...params } }); + } + + getAccelerationHistoryObserveResponse$(params: AccelerationHistoryParams): Observable { + return this.httpClient.get(`${SERVICES_API_PREFIX}/accelerator/accelerations/history`, { params: { ...params }, observe: 'response'}); + } + + getAccelerationStats$(): Observable { + return this.httpClient.get(`${SERVICES_API_PREFIX}/accelerator/accelerations/stats`); + } +} diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index f87a3dc31..ee03246f7 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -1,14 +1,14 @@ import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core'; import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable, merge } from 'rxjs'; import { Transaction } from '../interfaces/electrs.interface'; -import { IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, TransactionStripped } from '../interfaces/websocket.interface'; -import { BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface'; +import { HealthCheckHost, IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo } from '../interfaces/websocket.interface'; +import { BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree, TransactionStripped } from '../interfaces/node-api.interface'; import { Router, NavigationStart } from '@angular/router'; import { isPlatformBrowser } from '@angular/common'; import { filter, map, scan, shareReplay } from 'rxjs/operators'; import { StorageService } from './storage.service'; import { hasTouchScreen } from '../shared/pipes/bytes-pipe/utils'; -import { ApiService } from './api.service'; +import { ActiveFilter } from '../shared/filters.utils'; export interface MarkBlockState { blockHeight?: number; @@ -25,8 +25,6 @@ export interface Env { SIGNET_ENABLED: boolean; LIQUID_ENABLED: boolean; LIQUID_TESTNET_ENABLED: boolean; - BISQ_ENABLED: boolean; - BISQ_SEPARATE_BACKEND: boolean; ITEMS_PER_PAGE: number; KEEP_BLOCKS_AMOUNT: number; OFFICIAL_MEMPOOL_SPACE: boolean; @@ -40,7 +38,6 @@ export interface Env { PACKAGE_JSON_VERSION: string; MEMPOOL_WEBSITE_URL: string; LIQUID_WEBSITE_URL: string; - BISQ_WEBSITE_URL: string; MINING_DASHBOARD: boolean; LIGHTNING: boolean; AUDIT: boolean; @@ -49,6 +46,7 @@ export interface Env { SIGNET_BLOCK_AUDIT_START_HEIGHT: number; HISTORICAL_PRICE: boolean; ACCELERATOR: boolean; + ADDITIONAL_CURRENCIES: boolean; GIT_COMMIT_HASH_MEMPOOL_SPACE?: string; PACKAGE_JSON_VERSION_MEMPOOL_SPACE?: string; } @@ -59,8 +57,6 @@ const defaultEnv: Env = { 'LIQUID_ENABLED': false, 'LIQUID_TESTNET_ENABLED': false, 'BASE_MODULE': 'mempool', - 'BISQ_ENABLED': false, - 'BISQ_SEPARATE_BACKEND': false, 'ITEMS_PER_PAGE': 10, 'KEEP_BLOCKS_AMOUNT': 8, 'OFFICIAL_MEMPOOL_SPACE': false, @@ -73,7 +69,6 @@ const defaultEnv: Env = { 'PACKAGE_JSON_VERSION': '', 'MEMPOOL_WEBSITE_URL': 'https://mempool.space', 'LIQUID_WEBSITE_URL': 'https://liquid.network', - 'BISQ_WEBSITE_URL': 'https://bisq.markets', 'MINING_DASHBOARD': true, 'LIGHTNING': false, 'AUDIT': false, @@ -82,6 +77,7 @@ const defaultEnv: Env = { 'SIGNET_BLOCK_AUDIT_START_HEIGHT': 0, 'HISTORICAL_PRICE': true, 'ACCELERATOR': false, + 'ADDITIONAL_CURRENCIES': false, }; @Injectable({ @@ -89,6 +85,8 @@ const defaultEnv: Env = { }) export class StateService { isBrowser: boolean = isPlatformBrowser(this.platformId); + isMempoolSpaceBuild = window['isMempoolSpaceBuild'] ?? false; + backend: 'esplora' | 'electrum' | 'none' = 'esplora'; network = ''; lightning = false; blockVSize: number; @@ -96,11 +94,12 @@ export class StateService { latestBlockHeight = -1; blocks: BlockExtended[] = []; + backend$ = new BehaviorSubject<'esplora' | 'electrum' | 'none'>('esplora'); networkChanged$ = new ReplaySubject(1); lightningChanged$ = new ReplaySubject(1); blocksSubject$ = new BehaviorSubject([]); blocks$: Observable; - transactions$ = new ReplaySubject(6); + transactions$ = new BehaviorSubject(null); conversions$ = new ReplaySubject(1); bsqPrice$ = new ReplaySubject(1); mempoolInfo$ = new ReplaySubject(1); @@ -118,6 +117,7 @@ export class StateService { mempoolTransactions$ = new Subject(); mempoolTxPosition$ = new Subject<{ txid: string, position: MempoolPosition, cpfp: CpfpInfo | null}>(); mempoolRemovedTransactions$ = new Subject(); + multiAddressTransactions$ = new Subject<{ [address: string]: { mempool: Transaction[], confirmed: Transaction[], removed: Transaction[] }}>(); blockTransactions$ = new Subject(); isLoadingWebSocket$ = new ReplaySubject(1); isLoadingMempool$ = new BehaviorSubject(true); @@ -128,6 +128,7 @@ export class StateService { loadingIndicators$ = new ReplaySubject(1); recommendedFees$ = new ReplaySubject(1); chainTip$ = new ReplaySubject(-1); + serverHealth$ = new Subject(); live2Chart$ = new Subject(); @@ -146,11 +147,12 @@ export class StateService { hideAudit: BehaviorSubject; fiatCurrency$: BehaviorSubject; rateUnits$: BehaviorSubject; + showMiningInfo$: BehaviorSubject = new BehaviorSubject(false); searchFocus$: Subject = new Subject(); menuOpen$: BehaviorSubject = new BehaviorSubject(false); - activeGoggles$: BehaviorSubject = new BehaviorSubject([]); + activeGoggles$: BehaviorSubject = new BehaviorSubject({ mode: 'and', filters: [] }); constructor( @Inject(PLATFORM_ID) private platformId: any, @@ -208,13 +210,8 @@ export class StateService { } }, {})); - if (this.env.BASE_MODULE === 'bisq') { - this.network = this.env.BASE_MODULE; - this.networkChanged$.next(this.env.BASE_MODULE); - } - this.networkChanged$.subscribe((network) => { - this.transactions$ = new ReplaySubject(6); + this.transactions$ = new BehaviorSubject(null); this.blocksSubject$.next([]); }); @@ -251,6 +248,10 @@ export class StateService { const rateUnitPreference = this.storageService.getValue('rate-unit-preference'); this.rateUnits$ = new BehaviorSubject(rateUnitPreference || 'vb'); + + this.backend$.subscribe(backend => { + this.backend = backend; + }); } setNetworkBasedonUrl(url: string) { @@ -261,9 +262,9 @@ export class StateService { // /^\/ starts with a forward slash... // (?:[a-z]{2}(?:-[A-Z]{2})?\/)? optional locale prefix (non-capturing) // (?:preview\/)? optional "preview" prefix (non-capturing) - // (bisq|testnet|liquidtestnet|liquid|signet)/ network string (captured as networkMatches[1]) + // (testnet|liquidtestnet|liquid|signet)/ network string (captured as networkMatches[1]) // ($|\/) network string must end or end with a slash - const networkMatches = url.match(/^\/(?:[a-z]{2}(?:-[A-Z]{2})?\/)?(?:preview\/)?(bisq|testnet|liquidtestnet|liquid|signet)($|\/)/); + const networkMatches = url.match(/^\/(?:[a-z]{2}(?:-[A-Z]{2})?\/)?(?:preview\/)?(testnet|liquidtestnet|liquid|signet)($|\/)/); switch (networkMatches && networkMatches[1]) { case 'liquid': if (this.network !== 'liquid') { @@ -294,12 +295,6 @@ export class StateService { } } return; - case 'bisq': - if (this.network !== 'bisq') { - this.network = 'bisq'; - this.networkChanged$.next('bisq'); - } - return; default: if (this.env.BASE_MODULE !== 'mempool') { if (this.network !== this.env.BASE_MODULE) { diff --git a/frontend/src/app/services/websocket.service.ts b/frontend/src/app/services/websocket.service.ts index 3c72252db..de5170cbe 100644 --- a/frontend/src/app/services/websocket.service.ts +++ b/frontend/src/app/services/websocket.service.ts @@ -3,11 +3,12 @@ import { webSocket, WebSocketSubject } from 'rxjs/webSocket'; import { WebsocketResponse } from '../interfaces/websocket.interface'; import { StateService } from './state.service'; import { Transaction } from '../interfaces/electrs.interface'; -import { Subscription } from 'rxjs'; +import { firstValueFrom, Subscription } from 'rxjs'; import { ApiService } from './api.service'; import { take } from 'rxjs/operators'; -import { TransferState, makeStateKey } from '@angular/platform-browser'; +import { TransferState, makeStateKey } from '@angular/core'; import { CacheService } from './cache.service'; +import { uncompressDeltaChange, uncompressTx } from '../shared/common.utils'; const OFFLINE_RETRY_AFTER_MS = 2000; const OFFLINE_PING_CHECK_AFTER_MS = 30000; @@ -31,6 +32,7 @@ export class WebsocketService { private isTrackingRbf: 'all' | 'fullRbf' | false = false; private isTrackingRbfSummary = false; private isTrackingAddress: string | false = false; + private isTrackingAddresses: string[] | false = false; private trackingMempoolBlock: number; private latestGitCommit = ''; private onlineCheckTimeout: number; @@ -52,11 +54,16 @@ export class WebsocketService { .pipe(take(1)) .subscribe((response) => this.handleResponse(response)); } else { - this.network = this.stateService.network === 'bisq' && !this.stateService.env.BISQ_SEPARATE_BACKEND ? '' : this.stateService.network; + this.network = this.stateService.network; this.websocketSubject = webSocket(this.webSocketUrl.replace('{network}', this.network ? '/' + this.network : '')); - const theInitData = this.transferState.get(initData, null); + const { response: theInitData } = this.transferState.get(initData, null) || {}; if (theInitData) { + if (theInitData.body.blocks) { + theInitData.body.blocks = theInitData.body.blocks.reverse(); + } + this.stateService.backend$.next(theInitData.backend); + this.stateService.isLoadingWebSocket$.next(false); this.handleResponse(theInitData.body); this.startSubscription(false, true); } else { @@ -64,9 +71,6 @@ export class WebsocketService { } this.stateService.networkChanged$.subscribe((network) => { - if (network === 'bisq' && !this.stateService.env.BISQ_SEPARATE_BACKEND) { - network = ''; - } if (network === this.network) { return; } @@ -114,7 +118,7 @@ export class WebsocketService { this.startMultiTrackTransaction(this.trackingTxId); } if (this.isTrackingMempoolBlock) { - this.startTrackMempoolBlock(this.trackingMempoolBlock); + this.startTrackMempoolBlock(this.trackingMempoolBlock, true); } if (this.isTrackingRbf) { this.startTrackRbf(this.isTrackingRbf); @@ -125,6 +129,9 @@ export class WebsocketService { if (this.isTrackingAddress) { this.startTrackAddress(this.isTrackingAddress); } + if (this.isTrackingAddresses) { + this.startTrackAddresses(this.isTrackingAddresses); + } this.stateService.connectionState$.next(2); } @@ -174,6 +181,16 @@ export class WebsocketService { this.isTrackingAddress = false; } + startTrackAddresses(addresses: string[]) { + this.websocketSubject.next({ 'track-addresses': addresses }); + this.isTrackingAddresses = addresses; + } + + stopTrackingAddresses() { + this.websocketSubject.next({ 'track-addresses': [] }); + this.isTrackingAddresses = false; + } + startTrackAsset(asset: string) { this.websocketSubject.next({ 'track-asset': asset }); } @@ -182,9 +199,9 @@ export class WebsocketService { this.websocketSubject.next({ 'track-asset': 'stop' }); } - startTrackMempoolBlock(block: number) { + startTrackMempoolBlock(block: number, force: boolean = false) { // skip duplicate tracking requests - if (this.trackingMempoolBlock !== block) { + if (force || this.trackingMempoolBlock !== block) { this.websocketSubject.next({ 'track-mempool-block': block }); this.isTrackingMempoolBlock = true; this.trackingMempoolBlock = block; @@ -208,6 +225,7 @@ export class WebsocketService { } startTrackRbfSummary() { + this.initRbfSummary(); this.websocketSubject.next({ 'track-rbf-summary': true }); this.isTrackingRbfSummary = true; } @@ -217,14 +235,6 @@ export class WebsocketService { this.isTrackingRbfSummary = false; } - startTrackBisqMarket(market: string) { - this.websocketSubject.next({ 'track-bisq-market': market }); - } - - stopTrackingBisqMarket() { - this.websocketSubject.next({ 'track-bisq-market': 'stop' }); - } - fetchStatistics(historicalDate: string) { this.websocketSubject.next({ historicalDate }); } @@ -270,6 +280,10 @@ export class WebsocketService { handleResponse(response: WebsocketResponse) { let reinitBlocks = false; + if (response.backend) { + this.stateService.backend$.next(response.backend); + } + if (response.blocks && response.blocks.length) { const blocks = response.blocks; this.stateService.resetBlocks(blocks); @@ -315,8 +329,8 @@ export class WebsocketService { this.stateService.rbfLatest$.next(response.rbfLatest); } - if (response.rbfLatestSummary) { - this.stateService.rbfLatestSummary$.next(response.rbfLatestSummary); + if (response.rbfLatestSummary !== undefined) { + this.stateService.rbfLatestSummary$.next(response.rbfLatestSummary || []); } if (response.txReplaced) { @@ -328,7 +342,7 @@ export class WebsocketService { } if (response.transactions) { - response.transactions.forEach((tx) => this.stateService.transactions$.next(tx)); + this.stateService.transactions$.next(response.transactions.slice(0, 6)); } if (response['bsq-price']) { @@ -373,6 +387,10 @@ export class WebsocketService { }); } + if (response['multi-address-transactions']) { + this.stateService.multiAddressTransactions$.next(response['multi-address-transactions']); + } + if (response['block-transactions']) { response['block-transactions'].forEach((addressTransaction: Transaction) => { this.stateService.blockTransactions$.next(addressTransaction); @@ -382,9 +400,9 @@ export class WebsocketService { if (response['projected-block-transactions']) { if (response['projected-block-transactions'].index == this.trackingMempoolBlock) { if (response['projected-block-transactions'].blockTransactions) { - this.stateService.mempoolBlockTransactions$.next(response['projected-block-transactions'].blockTransactions); + this.stateService.mempoolBlockTransactions$.next(response['projected-block-transactions'].blockTransactions.map(uncompressTx)); } else if (response['projected-block-transactions'].delta) { - this.stateService.mempoolBlockDelta$.next(response['projected-block-transactions'].delta); + this.stateService.mempoolBlockDelta$.next(uncompressDeltaChange(response['projected-block-transactions'].delta)); } } } @@ -414,6 +432,10 @@ export class WebsocketService { this.stateService.previousRetarget$.next(response.previousRetarget); } + if (response['tomahawk']) { + this.stateService.serverHealth$.next(response['tomahawk']); + } + if (response['git-commit']) { this.stateService.backendInfo$.next(response['git-commit']); } @@ -422,4 +444,30 @@ export class WebsocketService { this.websocketSubject.next({'refresh-blocks': true}); } } + + async initRbfSummary(): Promise { + if (!this.stateService.isBrowser) { + const rbfList = await firstValueFrom(this.apiService.getRbfList$(false)); + if (rbfList) { + const rbfSummary = rbfList.slice(0, 6).map(rbfTree => { + let oldFee = 0; + let oldVsize = 0; + for (const replaced of rbfTree.replaces) { + oldFee += replaced.tx.fee; + oldVsize += replaced.tx.vsize; + } + return { + txid: rbfTree.tx.txid, + mined: !!rbfTree.tx.mined, + fullRbf: !!rbfTree.tx.fullRbf, + oldFee, + oldVsize, + newFee: rbfTree.tx.fee, + newVsize: rbfTree.tx.vsize, + }; + }); + this.stateService.rbfLatestSummary$.next(rbfSummary); + } + } + } } diff --git a/frontend/src/app/services/zone-shim.service.ts b/frontend/src/app/services/zone-shim.service.ts new file mode 100644 index 000000000..003d39f71 --- /dev/null +++ b/frontend/src/app/services/zone-shim.service.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class ZoneService { + + constructor() { } + + wrapObservable(obs: Observable): Observable { + return obs; + } +} diff --git a/frontend/src/app/services/zone.service.ts b/frontend/src/app/services/zone.service.ts new file mode 100644 index 000000000..210c5f8f0 --- /dev/null +++ b/frontend/src/app/services/zone.service.ts @@ -0,0 +1,60 @@ +import { ApplicationRef, Injectable, NgZone } from '@angular/core'; +import { Observable, Subscriber } from 'rxjs'; + +// global Zone object provided by zone.js +declare const Zone: any; + +@Injectable({ + providedIn: 'root' +}) +export class ZoneService { + + constructor( + private ngZone: NgZone, + private appRef: ApplicationRef, + ) { } + + wrapObservable(obs: Observable): Observable { + return new Observable((subscriber: Subscriber) => { + let task: any; + + this.ngZone.run(() => { + task = Zone.current.scheduleMacroTask('wrapObservable', () => {}, {}, () => {}, () => {}); + }); + + const subscription = obs.subscribe( + value => { + subscriber.next(value); + if (task) { + this.ngZone.run(() => { + this.appRef.tick(); + }); + task.invoke(); + } + }, + err => { + subscriber.error(err); + if (task) { + this.appRef.tick(); + task.invoke(); + } + }, + () => { + subscriber.complete(); + if (task) { + this.appRef.tick(); + task.invoke(); + } + } + ); + + return () => { + subscription.unsubscribe(); + if (task) { + this.appRef.tick(); + task.invoke(); + } + }; + }); + } +} diff --git a/frontend/src/app/shared/common.utils.ts b/frontend/src/app/shared/common.utils.ts index a04fa1663..be1e32c73 100644 --- a/frontend/src/app/shared/common.utils.ts +++ b/frontend/src/app/shared/common.utils.ts @@ -1,3 +1,6 @@ +import { MempoolBlockDelta, MempoolBlockDeltaCompressed, MempoolDeltaChange, TransactionCompressed } from "../interfaces/websocket.interface"; +import { TransactionStripped } from "../interfaces/node-api.interface"; + export function isMobile(): boolean { return (window.innerWidth <= 767.98); } @@ -152,4 +155,30 @@ export function seoDescriptionNetwork(network: string): string { return ' ' + network.charAt(0).toUpperCase() + network.slice(1); } return ''; +} + +export function uncompressTx(tx: TransactionCompressed): TransactionStripped { + return { + txid: tx[0], + fee: tx[1], + vsize: tx[2], + value: tx[3], + rate: tx[4], + flags: tx[5], + time: tx[6], + acc: !!tx[7], + }; +} + +export function uncompressDeltaChange(delta: MempoolBlockDeltaCompressed): MempoolBlockDelta { + return { + added: delta.added.map(uncompressTx), + removed: delta.removed, + changed: delta.changed.map(tx => ({ + txid: tx[0], + rate: tx[1], + flags: tx[2], + acc: !!tx[3], + })) + }; } \ No newline at end of file diff --git a/frontend/src/app/shared/components/fee-rate/fee-rate.component.html b/frontend/src/app/shared/components/fee-rate/fee-rate.component.html index c3e1615ff..f9e86cc2b 100644 --- a/frontend/src/app/shared/components/fee-rate/fee-rate.component.html +++ b/frontend/src/app/shared/components/fee-rate/fee-rate.component.html @@ -1,4 +1,10 @@ - {{ fee / (weight / 4) | feeRounding:rounding }} sat/vB - {{ fee / weight | feeRounding:rounding }} sat/WU + + {{ fee / (weight / 4) | feeRounding:rounding }} sat/vB + {{ fee / weight | feeRounding:rounding }} sat/WU + + + - sat/vB + - sat/WU + \ No newline at end of file diff --git a/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts b/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts index 4d65ef0c2..b1d143e7f 100644 --- a/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts +++ b/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts @@ -8,7 +8,7 @@ import { StateService } from '../../../services/state.service'; styleUrls: ['./fee-rate.component.scss'] }) export class FeeRateComponent implements OnInit { - @Input() fee: number; + @Input() fee: number | undefined; @Input() weight: number = 4; @Input() rounding: string = null; @Input() showUnit: boolean = true; diff --git a/frontend/src/app/shared/components/global-footer/global-footer.component.html b/frontend/src/app/shared/components/global-footer/global-footer.component.html index 7080c29ac..cc9cb3538 100644 --- a/frontend/src/app/shared/components/global-footer/global-footer.component.html +++ b/frontend/src/app/shared/components/global-footer/global-footer.component.html @@ -21,12 +21,12 @@
    - - @@ -62,7 +62,6 @@

    Signet Explorer

    Liquid Testnet Explorer

    Liquid Explorer

    -

    Bisq Explorer