diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 0cd95e568..732923ce2 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -21,7 +21,7 @@ jobs: - name: Setup node uses: actions/setup-node@v2 with: - node-version: 16.10.0 + node-version: 16.15.0 cache: 'npm' cache-dependency-path: frontend/package-lock.json - name: ${{ matrix.browser }} browser tests (Mempool) diff --git a/.nvmrc b/.nvmrc index 56bfee434..7fd023741 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.10.0 +v16.15.0 diff --git a/README.md b/README.md index db921d86d..ae0e2191e 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,7 @@ It is an open-source project developed and operated for the benefit of the Bitco Mempool can be self-hosted on a wide variety of your own hardware, ranging from a simple one-click installation on a Raspberry Pi full-node distro all the way to a robust production instance on a powerful FreeBSD server. -We support the following installation methods, ranked in order from simple to advanced: - -1) [One-click installation on full-node distros](#one-click-installation) -2) [Docker installation on Linux using docker-compose](./docker) -3) [Manual installation on Linux or FreeBSD](#manual-installation) -4) [Production installation on a powerful FreeBSD server](./production) - -This doc offers install notes on the one-click method and manual install method. Follow the links above for install notes on Docker and production installations. +**Most people should use a one-click install method.** Other install methods are meant for developers and others with experience managing servers. ## One-Click Installation @@ -29,167 +22,12 @@ Mempool can be conveniently installed on the following full-node distros: - [myNode](https://github.com/mynodebtc/mynode) - [Start9](https://github.com/Start9Labs/embassy-os) - -## Manual Installation +**We highly recommend you deploy your own Mempool instance this way.** No matter which option you pick, you'll be able to get your own fully-sovereign instance of Mempool up quickly without needing to fiddle with any settings. -The following instructions are for a manual installation on Linux or FreeBSD. You may need to change file and directory paths to match your OS. +## Advanced Installation Methods -You will need [Bitcoin Core](https://github.com/bitcoin/bitcoin), [Electrum Server](https://github.com/romanz/electrs), [Node.js](https://github.com/nodejs/node), [MariaDB](https://github.com/mariadb/server), and [Nginx](https://github.com/nginx/nginx). Below, we walk through how to configure each of these. +Mempool can be installed in other ways too, but we only recommend doing so if you're a developer, have experience managing servers, or otherwise know what you're doing. -### 1. Get Latest Mempool Release - -Clone the Mempool repo, and checkout the latest release tag: - -```bash -git clone https://github.com/mempool/mempool -cd mempool -latestrelease=$(curl -s https://api.github.com/repos/mempool/mempool/releases/latest|grep tag_name|head -1|cut -d '"' -f4) -git checkout $latestrelease -``` - -### 2. Configure Bitcoin Core - -Enable RPC and txindex in `bitcoin.conf`: - -```bash -rpcuser=mempool -rpcpassword=mempool -txindex=1 -``` - -### 3. Get & Configure MySQL - -Install MariaDB from your OS package manager: - -```bash -# Debian, Ubuntu, etc. -apt-get install mariadb-server mariadb-client - -# macOS -brew install mariadb -mysql.server start -``` - -Create a database and grant privileges: - -```bash -MariaDB [(none)]> drop database mempool; -Query OK, 0 rows affected (0.00 sec) - -MariaDB [(none)]> create database mempool; -Query OK, 1 row affected (0.00 sec) - -MariaDB [(none)]> grant all privileges on mempool.* to 'mempool'@'%' identified by 'mempool'; -Query OK, 0 rows affected (0.00 sec) -``` - -### 4. Build Mempool Backend - -Install Mempool dependencies with npm and build the backend: - -```bash -cd backend -npm install --prod -npm run build -``` - -In the `backend` folder, make a copy of the sample config: - -```bash -cp mempool-config.sample.json mempool-config.json -``` - -Edit `mempool-config.json` with your Bitcoin Core node RPC credentials: - -```bash -{ - "MEMPOOL": { - "NETWORK": "mainnet", - "BACKEND": "electrum", - "HTTP_PORT": 8999 - }, - "CORE_RPC": { - "HOST": "127.0.0.1", - "PORT": 8332, - "USERNAME": "mempool", - "PASSWORD": "mempool" - }, - "ELECTRUM": { - "HOST": "127.0.0.1", - "PORT": 50002, - "TLS_ENABLED": true - }, - "DATABASE": { - "ENABLED": true, - "HOST": "127.0.0.1", - "PORT": 3306, - "USERNAME": "mempool", - "PASSWORD": "mempool", - "DATABASE": "mempool" - } -} -``` - -Start the backend: - -```bash -npm run start -``` - -When it's running, you should see output like this: - -```bash -Mempool updated in 0.189 seconds -Updating mempool -Mempool updated in 0.096 seconds -Updating mempool -Mempool updated in 0.099 seconds -Updating mempool -Calculated fee for transaction 1 / 10 -Calculated fee for transaction 2 / 10 -Calculated fee for transaction 3 / 10 -Calculated fee for transaction 4 / 10 -Calculated fee for transaction 5 / 10 -Calculated fee for transaction 6 / 10 -Calculated fee for transaction 7 / 10 -Calculated fee for transaction 8 / 10 -Calculated fee for transaction 9 / 10 -Calculated fee for transaction 10 / 10 -Mempool updated in 0.243 seconds -Updating mempool -``` - -### 5. Build Mempool Frontend - -Install the Mempool dependencies with npm and build the frontend: - -```bash -cd frontend -npm install --prod -npm run build -``` - -Install the output into the nginx webroot folder: - -```bash -sudo rsync -av --delete dist/ /var/www/ -``` - -### 6. `nginx` + `certbot` - -Install the supplied `nginx.conf` and `nginx-mempool.conf` in `/etc/nginx`: - -```bash -# install nginx and certbot -apt-get install -y nginx python3-certbot-nginx - -# install the mempool configuration for nginx -cp nginx.conf nginx-mempool.conf /etc/nginx/ - -# replace example.com with your domain name -certbot --nginx -d example.com -``` - -If everything went well, you should see the beautiful mempool :grin: - -If you get stuck on "loading blocks", this means the websocket can't connect. Check your nginx proxy setup, firewalls, etc. and open an issue if you need help. +- See the [`docker/`](./docker/) directory for instructions on deploying Mempool with Docker. +- See the [`backend/`](./backend/) and [`frontend/`](./frontend/) directories for manual install instructions oriented for developers and small-scale deployments. +- See the [`production/`](./production/) directory for guidance on setting up a more serious Mempool instance designed for high performance at scale. \ No newline at end of file diff --git a/backend/README.md b/backend/README.md index e65205d67..cbda2478b 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,22 +1,161 @@ -# Setup backend watchers +# Mempool Backend -The backend is static. Typescript scripts are compiled into the `dist` folder and served through a node web server. +These instructions are mostly intended for developers, but can be used as a basis for personal or small-scale production setups. -You can avoid the manual shutdown/recompile/restart command line cycle by using a watcher. +If you choose to use these instructions for a production setup, be aware that you will still probably need to do additional configuration for your specific OS, environment, use-case, etc. We do our best here to provide a good starting point, but only proceed if you know what you're doing. Mempool does not provide support for custom setups. -Make sure you are in the `backend` directory `cd backend`. +See other ways to set up Mempool on [the main README](/../../#installation-methods). -1. Install nodemon and ts-node +Jump to a section in this doc: +- [Set Up the Backend](#setup) +- [Development Tips](#development-tips) + +## Setup + +### 1. Clone Mempool Repository + +Get the latest Mempool code: ``` -sudo npm install -g ts-node nodemon +git clone https://github.com/mempool/mempool +cd mempool ``` -2. Run the watcher +Check out the latest release: -> Note: You can find your npm global binary folder using `npm -g bin`, where nodemon will be installed. +``` +latestrelease=$(curl -s https://api.github.com/repos/mempool/mempool/releases/latest|grep tag_name|head -1|cut -d '"' -f4) +git checkout $latestrelease +``` + +### 2. Configure Bitcoin Core + +Turn on `txindex`, enable RPC, and set RPC credentials in `bitcoin.conf`: + +``` +txindex=1 +server=1 +rpcuser=mempool +rpcpassword=mempool +``` + +### 3. Configure Electrum Server + +[Pick an Electrum Server implementation](https://mempool.space/docs/faq#address-lookup-issues), configure it, and make sure it's synced. + +**This step is optional.** You can run Mempool without configuring an Electrum Server for it, but address lookups will be disabled. + +### 4. Configure MariaDB + +_Mempool needs MariaDB v10.5 or later. If you already have MySQL installed, make sure to migrate any existing databases **before** installing MariaDB._ + +Get MariaDB from your operating system's package manager: + +``` +# Debian, Ubuntu, etc. +apt-get install mariadb-server mariadb-client + +# macOS +brew install mariadb +mysql.server start +``` + +Create a database and grant privileges: + +``` +MariaDB [(none)]> drop database mempool; +Query OK, 0 rows affected (0.00 sec) + +MariaDB [(none)]> create database mempool; +Query OK, 1 row affected (0.00 sec) + +MariaDB [(none)]> grant all privileges on mempool.* to 'mempool'@'%' identified by 'mempool'; +Query OK, 0 rows affected (0.00 sec) +``` + +### 5. Prepare Mempool Backend + +#### Build + +_Node.js 16 and npm 7 are recommended._ + +Install dependencies with `npm` and build the backend: + +``` +cd backend +npm install # add --prod for production +npm run build +``` + +#### Configure + +In the backend folder, make a copy of the sample config file: + +``` +cp mempool-config.sample.json mempool-config.json +``` + +Edit `mempool-config.json` as needed. + +In particular, make sure: +- the correct Bitcoin Core RPC credentials are specified in `CORE_RPC` +- the correct `BACKEND` is specified in `MEMPOOL`: + - "electrum" if you're using [romanz/electrs](https://github.com/romanz/electrs) or [cculianu/Fulcrum](https://github.com/cculianu/Fulcrum) + - "esplora" if you're using [Blockstream/electrs](https://github.com/Blockstream/electrs) + - "none" if you're not using any Electrum Server + +### 6. Run Mempool Backend + +Run the Mempool backend: + +``` +npm run start +``` + +When it's running, you should see output like this: + +``` +Mempool updated in 0.189 seconds +Updating mempool +Mempool updated in 0.096 seconds +Updating mempool +Mempool updated in 0.099 seconds +Updating mempool +Calculated fee for transaction 1 / 10 +Calculated fee for transaction 2 / 10 +Calculated fee for transaction 3 / 10 +Calculated fee for transaction 4 / 10 +Calculated fee for transaction 5 / 10 +Calculated fee for transaction 6 / 10 +Calculated fee for transaction 7 / 10 +Calculated fee for transaction 8 / 10 +Calculated fee for transaction 9 / 10 +Calculated fee for transaction 10 / 10 +Mempool updated in 0.243 seconds +Updating mempool +``` + +### 7. Set Up Mempool Frontend +With the backend configured and running, proceed to set up the [Mempool frontend](../frontend#manual-setup). + +## Development Tips + +### Set Up Backend Watchers + +The Mempool backend is static. TypeScript scripts are compiled into the `dist` folder and served through a Node.js web server. + +As a result, for development purposes, you may find it helpful to set up backend watchers to avoid the manual shutdown/recompile/restart command-line cycle. + +First, install `nodemon` and `ts-node`: + +``` +npm install -g ts-node nodemon +``` + +Then, run the watcher: ``` nodemon src/index.ts --ignore cache/ --ignore pools.json ``` +`nodemon` should be in npm's global binary folder. If needed, you can determine where that is with `npm -g bin`. diff --git a/backend/package-lock.json b/backend/package-lock.json index 08a566a35..75e72aa75 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -17,8 +17,8 @@ "mysql2": "2.3.3", "node-worker-threads-pool": "^1.5.1", "socks-proxy-agent": "^6.2.0", - "typescript": "~4.6.3", - "ws": "~8.6.0" + "typescript": "~4.7.2", + "ws": "~8.7.0" }, "devDependencies": { "@types/compression": "^1.7.2", @@ -1469,9 +1469,9 @@ "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" }, "node_modules/typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.2.tgz", + "integrity": "sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1532,9 +1532,9 @@ "dev": true }, "node_modules/ws": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz", - "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz", + "integrity": "sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==", "engines": { "node": ">=10.0.0" }, @@ -2708,9 +2708,9 @@ "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" }, "typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==" + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.2.tgz", + "integrity": "sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==" }, "unpipe": { "version": "1.0.0", @@ -2755,9 +2755,9 @@ "dev": true }, "ws": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz", - "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz", + "integrity": "sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==", "requires": {} }, "yallist": { diff --git a/backend/package.json b/backend/package.json index 97ce387db..465f35557 100644 --- a/backend/package.json +++ b/backend/package.json @@ -36,8 +36,8 @@ "mysql2": "2.3.3", "node-worker-threads-pool": "^1.5.1", "socks-proxy-agent": "^6.2.0", - "typescript": "~4.6.3", - "ws": "~8.6.0" + "typescript": "~4.7.2", + "ws": "~8.7.0" }, "devDependencies": { "@types/compression": "^1.7.2", diff --git a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts index 266be5f1e..71269da31 100644 --- a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts +++ b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts @@ -2,7 +2,7 @@ import { IEsploraApi } from './esplora-api.interface'; export interface AbstractBitcoinApi { $getRawMempool(): Promise; - $getRawTransaction(txId: string, skipConversion?: boolean, addPrevout?: boolean, blockHash?: string): Promise; + $getRawTransaction(txId: string, skipConversion?: boolean, addPrevout?: boolean, lazyPrevouts?: boolean): Promise; $getBlockHeightTip(): Promise; $getTxIdsForBlock(hash: string): Promise; $getBlockHash(height: number): Promise; diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index 51ed99b6c..41671ede1 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -14,14 +14,32 @@ class BitcoinApi implements AbstractBitcoinApi { this.bitcoindClient = bitcoinClient; } - $getRawTransaction(txId: string, skipConversion = false, addPrevout = false, blockHash?: string): Promise { + static convertBlock(block: IBitcoinApi.Block): IEsploraApi.Block { + return { + id: block.hash, + height: block.height, + version: block.version, + timestamp: block.time, + bits: parseInt(block.bits, 16), + nonce: block.nonce, + difficulty: block.difficulty, + merkle_root: block.merkleroot, + tx_count: block.nTx, + size: block.size, + weight: block.weight, + previousblockhash: block.previousblockhash, + }; + } + + + $getRawTransaction(txId: string, skipConversion = false, addPrevout = false, lazyPrevouts = false): Promise { // If the transaction is in the mempool we already converted and fetched the fee. Only prevouts are missing const txInMempool = mempool.getMempool()[txId]; if (txInMempool && addPrevout) { return this.$addPrevouts(txInMempool); } - return this.bitcoindClient.getRawTransaction(txId, true, blockHash) + return this.bitcoindClient.getRawTransaction(txId, true) .then((transaction: IBitcoinApi.Transaction) => { if (skipConversion) { transaction.vout.forEach((vout) => { @@ -29,7 +47,7 @@ class BitcoinApi implements AbstractBitcoinApi { }); return transaction; } - return this.$convertTransaction(transaction, addPrevout); + return this.$convertTransaction(transaction, addPrevout, lazyPrevouts); }) .catch((e: Error) => { if (e.message.startsWith('The genesis block coinbase')) { @@ -109,7 +127,7 @@ class BitcoinApi implements AbstractBitcoinApi { const outSpends: IEsploraApi.Outspend[] = []; const tx = await this.$getRawTransaction(txId, true, false); for (let i = 0; i < tx.vout.length; i++) { - if (tx.status && tx.status.block_height == 0) { + if (tx.status && tx.status.block_height === 0) { outSpends.push({ spent: false }); @@ -128,7 +146,7 @@ class BitcoinApi implements AbstractBitcoinApi { return this.bitcoindClient.getNetworkHashPs(120, blockHeight); } - protected async $convertTransaction(transaction: IBitcoinApi.Transaction, addPrevout: boolean): Promise { + protected async $convertTransaction(transaction: IBitcoinApi.Transaction, addPrevout: boolean, lazyPrevouts = false): Promise { let esploraTransaction: IEsploraApi.Transaction = { txid: transaction.txid, version: transaction.version, @@ -174,35 +192,15 @@ class BitcoinApi implements AbstractBitcoinApi { }; } - if (transaction.confirmations) { - esploraTransaction = await this.$calculateFeeFromInputs(esploraTransaction, addPrevout); - } else { + if (addPrevout) { + esploraTransaction = await this.$calculateFeeFromInputs(esploraTransaction, false, lazyPrevouts); + } else if (!transaction.confirmations) { esploraTransaction = await this.$appendMempoolFeeData(esploraTransaction); - if (addPrevout) { - esploraTransaction = await this.$calculateFeeFromInputs(esploraTransaction, addPrevout); - } } return esploraTransaction; } - static convertBlock(block: IBitcoinApi.Block): IEsploraApi.Block { - return { - id: block.hash, - height: block.height, - version: block.version, - timestamp: block.time, - bits: parseInt(block.bits, 16), - nonce: block.nonce, - difficulty: block.difficulty, - merkle_root: block.merkleroot, - tx_count: block.nTx, - size: block.size, - weight: block.weight, - previousblockhash: block.previousblockhash, - }; - } - private translateScriptPubKeyType(outputType: string): string { const map = { 'pubkey': 'p2pk', @@ -245,7 +243,7 @@ class BitcoinApi implements AbstractBitcoinApi { if (vin.prevout) { continue; } - const innerTx = await this.$getRawTransaction(vin.txid, false); + const innerTx = await this.$getRawTransaction(vin.txid, false, false); vin.prevout = innerTx.vout[vin.vout]; this.addInnerScriptsToVin(vin); } @@ -271,22 +269,30 @@ class BitcoinApi implements AbstractBitcoinApi { return this.bitcoindClient.getRawMemPool(true); } - private async $calculateFeeFromInputs(transaction: IEsploraApi.Transaction, addPrevout: boolean): Promise { + + private async $calculateFeeFromInputs(transaction: IEsploraApi.Transaction, addPrevout: boolean, lazyPrevouts: boolean): Promise { if (transaction.vin[0].is_coinbase) { transaction.fee = 0; return transaction; } let totalIn = 0; - for (const vin of transaction.vin) { - const innerTx = await this.$getRawTransaction(vin.txid, !addPrevout); - if (addPrevout) { - vin.prevout = innerTx.vout[vin.vout]; - this.addInnerScriptsToVin(vin); + + for (let i = 0; i < transaction.vin.length; i++) { + if (lazyPrevouts && i > 12) { + transaction.vin[i].lazy = true; + continue; } - totalIn += innerTx.vout[vin.vout].value; + const innerTx = await this.$getRawTransaction(transaction.vin[i].txid, false, false); + transaction.vin[i].prevout = innerTx.vout[transaction.vin[i].vout]; + this.addInnerScriptsToVin(transaction.vin[i]); + totalIn += innerTx.vout[transaction.vin[i].vout].value; + } + if (lazyPrevouts && transaction.vin.length > 12) { + transaction.fee = -1; + } else { + const totalOut = transaction.vout.reduce((p, output) => p + output.value, 0); + transaction.fee = parseFloat((totalIn - totalOut).toFixed(8)); } - const totalOut = transaction.vout.reduce((p, output) => p + output.value, 0); - transaction.fee = parseFloat((totalIn - totalOut).toFixed(8)); return transaction; } diff --git a/backend/src/api/bitcoin/esplora-api.interface.ts b/backend/src/api/bitcoin/esplora-api.interface.ts index 73ccbd88f..f825c60f9 100644 --- a/backend/src/api/bitcoin/esplora-api.interface.ts +++ b/backend/src/api/bitcoin/esplora-api.interface.ts @@ -33,6 +33,8 @@ export namespace IEsploraApi { // Elements is_pegin?: boolean; issuance?: Issuance; + // Custom + lazy?: boolean; } interface Issuance { diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 6af631382..814d967f3 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -15,6 +15,9 @@ import BitcoinApi from './bitcoin/bitcoin-api'; import { prepareBlock } from '../utils/blocks-utils'; import BlocksRepository from '../repositories/BlocksRepository'; import HashratesRepository from '../repositories/HashratesRepository'; +import indexer from '../indexer'; +import fiatConversion from './fiat-conversion'; +import RatesRepository from '../repositories/RatesRepository'; class Blocks { private blocks: BlockExtended[] = []; @@ -23,9 +26,6 @@ class Blocks { private lastDifficultyAdjustmentTime = 0; private previousDifficultyRetarget = 0; private newBlockCallbacks: ((block: BlockExtended, txIds: string[], transactions: TransactionExtended[]) => void)[] = []; - private blockIndexingStarted = false; - public blockIndexingCompleted = false; - public reindexFlag = false; constructor() { } @@ -134,7 +134,7 @@ class Blocks { blockExtended.extras.avgFeeRate = stats.avgfeerate; } - if (Common.indexingEnabled()) { + if (['mainnet', 'testnet', 'signet', 'regtest'].includes(config.MEMPOOL.NETWORK)) { let pool: PoolTag; if (blockExtended.extras?.coinbaseTx !== undefined) { pool = await this.$findBlockMiner(blockExtended.extras?.coinbaseTx); @@ -143,7 +143,8 @@ class Blocks { } if (!pool) { // We should never have this situation in practise - logger.warn(`Cannot assign pool to block ${blockExtended.height} and 'unknown' pool does not exist. Check your "pools" table entries`); + logger.warn(`Cannot assign pool to block ${blockExtended.height} and 'unknown' pool does not exist. ` + + `Check your "pools" table entries`); return blockExtended; } @@ -196,24 +197,15 @@ class Blocks { * [INDEXING] Index all blocks metadata for the mining dashboard */ public async $generateBlockDatabase() { - if (this.blockIndexingStarted && !this.reindexFlag) { - return; - } - - this.reindexFlag = false; - const blockchainInfo = await bitcoinClient.getBlockchainInfo(); if (blockchainInfo.blocks !== blockchainInfo.headers) { // Wait for node to sync return; } - this.blockIndexingStarted = true; - this.blockIndexingCompleted = false; - try { let currentBlockHeight = blockchainInfo.blocks; - let indexingBlockAmount = config.MEMPOOL.INDEXING_BLOCKS_AMOUNT; + let indexingBlockAmount = Math.min(config.MEMPOOL.INDEXING_BLOCKS_AMOUNT, blockchainInfo.blocks); if (indexingBlockAmount <= -1) { indexingBlockAmount = currentBlockHeight + 1; } @@ -274,14 +266,14 @@ class Blocks { loadingIndicators.setProgress('block-indexing', 100); } catch (e) { logger.err('Block indexing failed. Trying again later. Reason: ' + (e instanceof Error ? e.message : e)); - this.blockIndexingStarted = false; loadingIndicators.setProgress('block-indexing', 100); return; } const chainValid = await BlocksRepository.$validateChain(); - this.reindexFlag = !chainValid; - this.blockIndexingCompleted = chainValid; + if (!chainValid) { + indexer.reindex(); + } } public async $updateBlocks() { @@ -298,6 +290,8 @@ class Blocks { logger.info(`${blockHeightTip - this.currentBlockHeight} blocks since tip. Fast forwarding to the ${config.MEMPOOL.INITIAL_BLOCKS_AMOUNT} recent blocks`); this.currentBlockHeight = blockHeightTip - config.MEMPOOL.INITIAL_BLOCKS_AMOUNT; fastForwarded = true; + logger.info(`Re-indexing skipped blocks and corresponding hashrates data`); + indexer.reindex(); // Make sure to index the skipped blocks #1619 } if (!this.lastDifficultyAdjustmentTime) { @@ -349,6 +343,9 @@ class Blocks { await blocksRepository.$saveBlockInDatabase(blockExtended); } } + if (fiatConversion.ratesInitialized === true) { + await RatesRepository.$saveRate(blockExtended.height, fiatConversion.getConversionRates()); + } if (block.height % 2016 === 0) { this.previousDifficultyRetarget = (block.difficulty - this.currentDifficulty) / this.currentDifficulty * 100; @@ -389,10 +386,43 @@ class Blocks { return prepareBlock(blockExtended); } - public async $getBlocksExtras(fromHeight?: number, limit: number = 15): Promise { - // Note - This API is breaking if indexing is not available. For now it is okay because we only - // use it for the mining pages, and mining pages should not be available if indexing is turned off. - // I'll need to fix it before we refactor the block(s) related pages + /** + * Index a block by hash if it's missing from the database. Returns the block after indexing + */ + public async $getBlock(hash: string): Promise { + // Check the memory cache + const blockByHash = this.getBlocks().find((b) => b.id === hash); + if (blockByHash) { + return blockByHash; + } + + // Block has already been indexed + if (Common.indexingEnabled()) { + const dbBlock = await blocksRepository.$getBlockByHash(hash); + if (dbBlock != null) { + return prepareBlock(dbBlock); + } + } + + const block = await bitcoinApi.$getBlock(hash); + + // Not Bitcoin network, return the block as it + if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) { + return block; + } + + // Bitcoin network, add our custom data on top + const transactions = await this.$getTransactionsExtended(hash, block.height, true); + const blockExtended = await this.$getBlockExtended(block, transactions); + if (Common.indexingEnabled()) { + delete(blockExtended['coinbaseTx']); + await blocksRepository.$saveBlockInDatabase(blockExtended); + } + + return blockExtended; + } + + public async $getBlocks(fromHeight?: number, limit: number = 15): Promise { try { let currentHeight = fromHeight !== undefined ? fromHeight : this.getCurrentBlockHeight(); const returnBlocks: BlockExtended[] = []; @@ -401,25 +431,32 @@ class Blocks { return returnBlocks; } + if (currentHeight === 0 && Common.indexingEnabled()) { + currentHeight = await blocksRepository.$mostRecentBlockHeight(); + } + // Check if block height exist in local cache to skip the hash lookup const blockByHeight = this.getBlocks().find((b) => b.height === currentHeight); let startFromHash: string | null = null; if (blockByHeight) { startFromHash = blockByHeight.id; - } else { + } else if (!Common.indexingEnabled()) { startFromHash = await bitcoinApi.$getBlockHash(currentHeight); } let nextHash = startFromHash; for (let i = 0; i < limit && currentHeight >= 0; i++) { let block = this.getBlocks().find((b) => b.height === currentHeight); - if (!block && Common.indexingEnabled()) { + if (block) { + returnBlocks.push(block); + } else if (Common.indexingEnabled()) { block = await this.$indexBlock(currentHeight); - } else if (!block) { + returnBlocks.push(block); + } else if (nextHash != null) { block = prepareBlock(await bitcoinApi.$getBlock(nextHash)); + nextHash = block.previousblockhash; + returnBlocks.push(block); } - returnBlocks.push(block); - nextHash = block.previousblockhash; currentHeight--; } diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index 45ef5f576..d4b57f204 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -169,12 +169,12 @@ export class Common { default: return null; } } - + static indexingEnabled(): boolean { return ( - ['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) && + ['mainnet', 'testnet', 'signet', 'regtest'].includes(config.MEMPOOL.NETWORK) && config.DATABASE.ENABLED === true && - config.MEMPOOL.INDEXING_BLOCKS_AMOUNT != 0 + config.MEMPOOL.INDEXING_BLOCKS_AMOUNT !== 0 ); } } diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index 80c338a20..680acec08 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -4,7 +4,7 @@ import logger from '../logger'; import { Common } from './common'; class DatabaseMigration { - private static currentVersion = 17; + private static currentVersion = 19; private queryTimeout = 120000; private statisticsAddedIndexed = false; @@ -180,6 +180,14 @@ class DatabaseMigration { if (databaseSchemaVersion < 17 && isBitcoin === true) { await this.$executeQuery('ALTER TABLE `pools` ADD `slug` CHAR(50) NULL'); } + + if (databaseSchemaVersion < 18 && isBitcoin === true) { + await this.$executeQuery('ALTER TABLE `blocks` ADD INDEX `hash` (`hash`);'); + } + + if (databaseSchemaVersion < 19) { + await this.$executeQuery(this.getCreateRatesTableQuery(), await this.$checkIfTableExists('rates')); + } } catch (e) { throw e; } @@ -462,6 +470,14 @@ class DatabaseMigration { ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`; } + private getCreateRatesTableQuery(): string { + return `CREATE TABLE IF NOT EXISTS rates ( + height int(10) unsigned NOT NULL, + bisq_rates JSON NOT NULL, + PRIMARY KEY (height) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`; + } + public async $truncateIndexedData(tables: string[]) { const allowedTables = ['blocks', 'hashrates']; diff --git a/backend/src/api/difficulty-adjustment.ts b/backend/src/api/difficulty-adjustment.ts index eea0e9b42..1f85fdb80 100644 --- a/backend/src/api/difficulty-adjustment.ts +++ b/backend/src/api/difficulty-adjustment.ts @@ -32,7 +32,7 @@ class DifficultyAdjustmentApi { } } - let timeAvgMins = blocksInEpoch ? diff / blocksInEpoch / 60 : 10; + let timeAvgMins = blocksInEpoch && blocksInEpoch > 146 ? diff / blocksInEpoch / 60 : 10; // Testnet difficulty is set to 1 after 20 minutes of no blocks, // therefore the time between blocks will always be below 20 minutes (1200s). diff --git a/backend/src/api/fee-api.ts b/backend/src/api/fee-api.ts index 7bc7d06f3..82778825e 100644 --- a/backend/src/api/fee-api.ts +++ b/backend/src/api/fee-api.ts @@ -1,4 +1,3 @@ -import config from '../config'; import { MempoolBlock } from '../mempool.interfaces'; import { Common } from './common'; import mempool from './mempool'; @@ -19,6 +18,7 @@ class FeeApi { 'fastestFee': this.defaultFee, 'halfHourFee': this.defaultFee, 'hourFee': this.defaultFee, + 'economyFee': minimumFee, 'minimumFee': minimumFee, }; } @@ -31,6 +31,7 @@ class FeeApi { 'fastestFee': firstMedianFee, 'halfHourFee': secondMedianFee, 'hourFee': thirdMedianFee, + 'economyFee': Math.min(2 * minimumFee, thirdMedianFee), 'minimumFee': minimumFee, }; } diff --git a/backend/src/api/fiat-conversion.ts b/backend/src/api/fiat-conversion.ts index 5fdde7274..3562760f1 100644 --- a/backend/src/api/fiat-conversion.ts +++ b/backend/src/api/fiat-conversion.ts @@ -6,12 +6,19 @@ import backendInfo from './backend-info'; import { SocksProxyAgent } from 'socks-proxy-agent'; class FiatConversion { - private conversionRates: IConversionRates = { - 'USD': 0 - }; + private debasingFiatCurrencies = ['AED', 'AUD', 'BDT', 'BHD', 'BMD', 'BRL', 'CAD', 'CHF', 'CLP', + 'CNY', 'CZK', 'DKK', 'EUR', 'GBP', 'HKD', 'HUF', 'IDR', 'ILS', 'INR', 'JPY', 'KRW', 'KWD', + 'LKR', 'MMK', 'MXN', 'MYR', 'NGN', 'NOK', 'NZD', 'PHP', 'PKR', 'PLN', 'RUB', 'SAR', 'SEK', + 'SGD', 'THB', 'TRY', 'TWD', 'UAH', 'USD', 'VND', 'ZAR']; + private conversionRates: IConversionRates = {}; private ratesChangedCallback: ((rates: IConversionRates) => void) | undefined; + public ratesInitialized = false; // If true, it means rates are ready for use - constructor() { } + constructor() { + for (const fiat of this.debasingFiatCurrencies) { + this.conversionRates[fiat] = 0; + } + } public setProgressChangedCallback(fn: (rates: IConversionRates) => void) { this.ratesChangedCallback = fn; @@ -62,13 +69,14 @@ class FiatConversion { response = await axios.get(fiatConversionUrl, { headers: headers, timeout: 10000 }); } - const usd = response.data.data.find((item: any) => item.currencyCode === 'USD'); + for (const rate of response.data.data) { + if (this.debasingFiatCurrencies.includes(rate.currencyCode) && rate.provider === 'Bisq-Aggregate') { + this.conversionRates[rate.currencyCode] = Math.round(100 * rate.price) / 100; + } + } - this.conversionRates = { - 'USD': usd.price, - }; - - logger.debug(`USD Conversion Rate: ${usd.price}`); + this.ratesInitialized = true; + logger.debug(`USD Conversion Rate: ${this.conversionRates.USD}`); if (this.ratesChangedCallback) { this.ratesChangedCallback(this.conversionRates); diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index cbd412068..be786db42 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -4,14 +4,11 @@ import PoolsRepository from '../repositories/PoolsRepository'; import HashratesRepository from '../repositories/HashratesRepository'; import bitcoinClient from './bitcoin/bitcoin-client'; import logger from '../logger'; -import blocks from './blocks'; import { Common } from './common'; import loadingIndicators from './loading-indicators'; +import { escape } from 'mysql2'; class Mining { - hashrateIndexingStarted = false; - weeklyHashrateIndexingStarted = false; - constructor() { } @@ -92,14 +89,18 @@ class Mining { }); poolsStatistics['pools'] = poolsStats; - poolsStatistics['oldestIndexedBlockTimestamp'] = await BlocksRepository.$oldestBlockTimestamp(); const blockCount: number = await BlocksRepository.$blockCount(null, interval); poolsStatistics['blockCount'] = blockCount; const totalBlock24h: number = await BlocksRepository.$blockCount(null, '24h'); - const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(totalBlock24h); - poolsStatistics['lastEstimatedHashrate'] = lastBlockHashrate; + + try { + poolsStatistics['lastEstimatedHashrate'] = await bitcoinClient.getNetworkHashPs(totalBlock24h); + } catch (e) { + poolsStatistics['lastEstimatedHashrate'] = 0; + logger.debug('Bitcoin Core is not available, using zeroed value for current hashrate'); + } return poolsStatistics; } @@ -110,7 +111,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`); + throw new Error('This mining pool does not exist ' + escape(slug)); } const blockCount: number = await BlocksRepository.$blockCount(pool.id); @@ -122,7 +123,12 @@ class Mining { const blockCount1w: number = await BlocksRepository.$blockCount(pool.id, '1w'); const totalBlock1w: number = await BlocksRepository.$blockCount(null, '1w'); - const currentEstimatedkHashrate = await bitcoinClient.getNetworkHashPs(totalBlock24h); + let currentEstimatedHashrate = 0; + try { + currentEstimatedHashrate = await bitcoinClient.getNetworkHashPs(totalBlock24h); + } catch (e) { + logger.debug('Bitcoin Core is not available, using zeroed value for current hashrate'); + } return { pool: pool, @@ -136,7 +142,7 @@ class Mining { '24h': blockCount24h / totalBlock24h, '1w': blockCount1w / totalBlock1w, }, - estimatedHashrate: currentEstimatedkHashrate * (blockCount24h / totalBlock24h), + estimatedHashrate: currentEstimatedHashrate * (blockCount24h / totalBlock24h), reportedHashrate: null, }; } @@ -152,14 +158,9 @@ class Mining { * [INDEXING] Generate weekly mining pool hashrate history */ public async $generatePoolHashrateHistory(): Promise { - if (!blocks.blockIndexingCompleted || this.hashrateIndexingStarted || this.weeklyHashrateIndexingStarted) { - return; - } - const now = new Date(); try { - this.weeklyHashrateIndexingStarted = true; const lastestRunDate = await HashratesRepository.$getLatestRun('last_weekly_hashrates_indexing'); // Run only if: @@ -167,11 +168,9 @@ class Mining { // * we started a new week (around Monday midnight) const runIndexing = lastestRunDate === 0 || now.getUTCDay() === 1 && lastestRunDate !== now.getUTCDate(); if (!runIndexing) { - this.weeklyHashrateIndexingStarted = false; return; } } catch (e) { - this.weeklyHashrateIndexingStarted = false; throw e; } @@ -191,6 +190,7 @@ class Mining { const startedAt = new Date().getTime() / 1000; let timer = new Date().getTime() / 1000; + logger.debug(`Indexing weekly mining pool hashrate`); loadingIndicators.setProgress('weekly-hashrate-indexing', 0); while (toTimestamp > genesisTimestamp) { @@ -255,7 +255,6 @@ class Mining { ++indexedThisRun; ++totalIndexed; } - this.weeklyHashrateIndexingStarted = false; await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', new Date().getUTCDate()); if (newlyIndexed > 0) { logger.info(`Indexed ${newlyIndexed} pools weekly hashrate`); @@ -263,7 +262,6 @@ class Mining { loadingIndicators.setProgress('weekly-hashrate-indexing', 100); } catch (e) { loadingIndicators.setProgress('weekly-hashrate-indexing', 100); - this.weeklyHashrateIndexingStarted = false; throw e; } } @@ -272,22 +270,14 @@ class Mining { * [INDEXING] Generate daily hashrate data */ public async $generateNetworkHashrateHistory(): Promise { - if (!blocks.blockIndexingCompleted || this.hashrateIndexingStarted) { - return; - } - try { - this.hashrateIndexingStarted = true; - // We only run this once a day around midnight const latestRunDate = await HashratesRepository.$getLatestRun('last_hashrates_indexing'); const now = new Date().getUTCDate(); if (now === latestRunDate) { - this.hashrateIndexingStarted = false; return; } } catch (e) { - this.hashrateIndexingStarted = false; throw e; } @@ -305,6 +295,7 @@ class Mining { const startedAt = new Date().getTime() / 1000; let timer = new Date().getTime() / 1000; + logger.debug(`Indexing daily network hashrate`); loadingIndicators.setProgress('daily-hashrate-indexing', 0); while (toTimestamp > genesisTimestamp) { @@ -376,14 +367,12 @@ class Mining { await HashratesRepository.$saveHashrates(hashrates); await HashratesRepository.$setLatestRun('last_hashrates_indexing', new Date().getUTCDate()); - this.hashrateIndexingStarted = false; if (newlyIndexed > 0) { logger.info(`Indexed ${newlyIndexed} day of network hashrate`); } loadingIndicators.setProgress('daily-hashrate-indexing', 100); } catch (e) { loadingIndicators.setProgress('daily-hashrate-indexing', 100); - this.hashrateIndexingStarted = false; throw e; } } diff --git a/backend/src/api/transaction-utils.ts b/backend/src/api/transaction-utils.ts index 2e669d709..5b92cea5f 100644 --- a/backend/src/api/transaction-utils.ts +++ b/backend/src/api/transaction-utils.ts @@ -21,8 +21,8 @@ class TransactionUtils { }; } - public async $getTransactionExtended(txId: string, addPrevouts = false): Promise { - const transaction: IEsploraApi.Transaction = await bitcoinApi.$getRawTransaction(txId, false, addPrevouts); + public async $getTransactionExtended(txId: string, addPrevouts = false, lazyPrevouts = false): Promise { + const transaction: IEsploraApi.Transaction = await bitcoinApi.$getRawTransaction(txId, false, addPrevouts, lazyPrevouts); return this.extendTransaction(transaction); } diff --git a/backend/src/index.ts b/backend/src/index.ts index 8560064a9..9e90edbc4 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -25,10 +25,8 @@ import databaseMigration from './api/database-migration'; import syncAssets from './sync-assets'; import icons from './api/liquid/icons'; import { Common } from './api/common'; -import mining from './api/mining'; -import HashratesRepository from './repositories/HashratesRepository'; -import BlocksRepository from './repositories/BlocksRepository'; import poolsUpdater from './tasks/pools-updater'; +import indexer from './indexer'; class Server { private wss: WebSocket.Server | undefined; @@ -99,7 +97,7 @@ class Server { } await databaseMigration.$initializeOrMigrateDatabase(); if (Common.indexingEnabled()) { - await this.$resetHashratesIndexingState(); + await indexer.$resetHashratesIndexingState(); } } catch (e) { throw new Error(e instanceof Error ? e.message : 'Error'); @@ -154,7 +152,7 @@ class Server { await poolsUpdater.updatePoolsJson(); await blocks.$updateBlocks(); await memPool.$updateMempool(); - this.$runIndexingWhenReady(); + indexer.$run(); setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS); this.currentBackendRetryInterval = 5; @@ -173,29 +171,6 @@ class Server { } } - async $resetHashratesIndexingState() { - try { - await HashratesRepository.$setLatestRun('last_hashrates_indexing', 0); - await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', 0); - } catch (e) { - logger.err(`Cannot reset hashrate indexing timestamps. Reason: ` + (e instanceof Error ? e.message : e)); - } - } - - async $runIndexingWhenReady() { - if (!Common.indexingEnabled() || mempool.hasPriority()) { - return; - } - - try { - await blocks.$generateBlockDatabase(); - await mining.$generateNetworkHashrateHistory(); - await mining.$generatePoolHashrateHistory(); - } catch (e) { - logger.err(`Indexing failed, trying again later. Reason: ` + (e instanceof Error ? e.message : e)); - } - } - setUpWebsocketHandling() { if (this.wss) { websocketHandler.setWebsocketServer(this.wss); @@ -300,24 +275,12 @@ class Server { if (Common.indexingEnabled()) { this.app - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/24h', routes.$getPools.bind(routes, '24h')) - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/3d', routes.$getPools.bind(routes, '3d')) - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/1w', routes.$getPools.bind(routes, '1w')) - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/1m', routes.$getPools.bind(routes, '1m')) - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/3m', routes.$getPools.bind(routes, '3m')) - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/6m', routes.$getPools.bind(routes, '6m')) - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/1y', routes.$getPools.bind(routes, '1y')) - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/2y', routes.$getPools.bind(routes, '2y')) - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/3y', routes.$getPools.bind(routes, '3y')) - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/all', routes.$getPools.bind(routes, 'all')) + .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/:interval', routes.$getPools) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:slug/hashrate', routes.$getPoolHistoricalHashrate) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:slug/blocks', routes.$getPoolBlocks) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:slug/blocks/:height', routes.$getPoolBlocks) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:slug', routes.$getPool) - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:slug/:interval', routes.$getPool) - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/hashrate/pools', routes.$getPoolsHistoricalHashrate) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/hashrate/pools/:interval', routes.$getPoolsHistoricalHashrate) - .get(config.MEMPOOL.API_URL_PREFIX + 'mining/hashrate', routes.$getHistoricalHashrate) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/hashrate/:interval', routes.$getHistoricalHashrate) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/reward-stats/:blockCount', routes.$getRewardStats) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/fees/:interval', routes.$getHistoricalBlockFees) @@ -349,8 +312,9 @@ class Server { } this.app - .get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras', routes.getBlocksExtras) - .get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras/:height', routes.getBlocksExtras); + .get(config.MEMPOOL.API_URL_PREFIX + 'blocks', routes.getBlocks.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', routes.getBlocks.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', routes.getBlock); if (config.MEMPOOL.BACKEND !== 'esplora') { this.app @@ -362,10 +326,7 @@ class Server { .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/hex', routes.getRawTransaction) .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/status', routes.getTransactionStatus) .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/outspends', routes.getTransactionOutspends) - .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', routes.getBlock) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/header', routes.getBlockHeader) - .get(config.MEMPOOL.API_URL_PREFIX + 'blocks', routes.getBlocks) - .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', routes.getBlocks) .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/tip/height', routes.getBlockTipHeight) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/txs', routes.getBlockTransactions) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/txs/:index', routes.getBlockTransactions) diff --git a/backend/src/indexer.ts b/backend/src/indexer.ts new file mode 100644 index 000000000..7ddc2a47f --- /dev/null +++ b/backend/src/indexer.ts @@ -0,0 +1,52 @@ +import { Common } from './api/common'; +import blocks from './api/blocks'; +import mempool from './api/mempool'; +import mining from './api/mining'; +import logger from './logger'; +import HashratesRepository from './repositories/HashratesRepository'; + +class Indexer { + runIndexer = true; + indexerRunning = false; + + constructor() { + } + + public reindex() { + this.runIndexer = true; + } + + public async $run() { + if (!Common.indexingEnabled() || this.runIndexer === false || + this.indexerRunning === true || mempool.hasPriority() + ) { + return; + } + + this.runIndexer = false; + this.indexerRunning = true; + + try { + await blocks.$generateBlockDatabase(); + await this.$resetHashratesIndexingState(); + await mining.$generateNetworkHashrateHistory(); + await mining.$generatePoolHashrateHistory(); + } catch (e) { + this.reindex(); + logger.err(`Indexer failed, trying again later. Reason: ` + (e instanceof Error ? e.message : e)); + } + + this.indexerRunning = false; + } + + async $resetHashratesIndexingState() { + try { + await HashratesRepository.$setLatestRun('last_hashrates_indexing', 0); + await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', 0); + } catch (e) { + logger.err(`Cannot reset hashrate indexing timestamps. Reason: ` + (e instanceof Error ? e.message : e)); + } + } +} + +export default new Indexer(); diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index 0081bd34f..60b07da1b 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -81,7 +81,7 @@ export interface TransactionStripped { export interface BlockExtension { totalFees?: number; - medianFee?: number; // Actually the median fee rate that we compute ourself + medianFee?: number; feeRange?: number[]; reward?: number; coinbaseTx?: TransactionMinerInfo; diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index 0792f130f..1d5843d6e 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -5,6 +5,7 @@ import { Common } from '../api/common'; import { prepareBlock } from '../utils/blocks-utils'; import PoolsRepository from './PoolsRepository'; import HashratesRepository from './HashratesRepository'; +import { escape } from 'mysql2'; class BlocksRepository { /** @@ -120,6 +121,19 @@ class BlocksRepository { } } + /** + * Return most recent block height + */ + public async $mostRecentBlockHeight(): Promise { + try { + const [row] = await DB.query('SELECT MAX(height) as maxHeight from blocks'); + return row[0]['maxHeight']; + } catch (e) { + logger.err(`Cannot count blocks for this pool (using offset). Reason: ` + (e instanceof Error ? e.message : e)); + throw e; + } + } + /** * Get blocks count for a period */ @@ -235,12 +249,30 @@ 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`); + throw new Error('This mining pool does not exist ' + escape(slug)); } const params: any[] = []; - let query = ` SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, - previous_block_hash as previousblockhash + let query = ` SELECT + height, + hash as id, + UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, + size, + weight, + tx_count, + coinbase_raw, + difficulty, + fees, + fee_span, + median_fee, + reward, + version, + bits, + nonce, + merkle_root, + previous_block_hash as previousblockhash, + avg_fee, + avg_fee_rate FROM blocks WHERE pool_id = ?`; params.push(pool.id); @@ -273,11 +305,32 @@ class BlocksRepository { */ public async $getBlockByHeight(height: number): Promise { try { - const [rows]: any[] = await DB.query(` - SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, - pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.slug as pool_slug, - pools.addresses as pool_addresses, pools.regexes as pool_regexes, - previous_block_hash as previousblockhash + const [rows]: any[] = await DB.query(`SELECT + height, + hash as id, + UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, + size, + weight, + tx_count, + coinbase_raw, + difficulty, + pools.id as pool_id, + pools.name as pool_name, + pools.link as pool_link, + pools.slug as pool_slug, + pools.addresses as pool_addresses, + pools.regexes as pool_regexes, + fees, + fee_span, + median_fee, + reward, + version, + bits, + nonce, + merkle_root, + previous_block_hash as previousblockhash, + avg_fee, + avg_fee_rate FROM blocks JOIN pools ON blocks.pool_id = pools.id WHERE height = ${height}; @@ -287,6 +340,7 @@ class BlocksRepository { return null; } + rows[0].fee_span = JSON.parse(rows[0].fee_span); return rows[0]; } catch (e) { logger.err(`Cannot get indexed block ${height}. Reason: ` + (e instanceof Error ? e.message : e)); @@ -294,6 +348,34 @@ class BlocksRepository { } } + /** + * Get one block by hash + */ + public async $getBlockByHash(hash: string): Promise { + try { + const query = ` + SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, hash as id, + pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.slug as pool_slug, + pools.addresses as pool_addresses, pools.regexes as pool_regexes, + previous_block_hash as previousblockhash + FROM blocks + JOIN pools ON blocks.pool_id = pools.id + WHERE hash = '${hash}'; + `; + const [rows]: any[] = await DB.query(query); + + if (rows.length <= 0) { + return null; + } + + rows[0].fee_span = JSON.parse(rows[0].fee_span); + return rows[0]; + } catch (e) { + logger.err(`Cannot get indexed block ${hash}. Reason: ` + (e instanceof Error ? e.message : e)); + throw e; + } + } + /** * Return blocks difficulty */ @@ -397,18 +479,28 @@ class BlocksRepository { const [blocks]: any[] = await DB.query(`SELECT height, hash, previous_block_hash, UNIX_TIMESTAMP(blockTimestamp) as timestamp FROM blocks ORDER BY height`); - let currentHeight = 1; - while (currentHeight < blocks.length) { - if (blocks[currentHeight].previous_block_hash !== blocks[currentHeight - 1].hash) { - logger.warn(`Chain divergence detected at block ${blocks[currentHeight - 1].height}, re-indexing newer blocks and hashrates`); - await this.$deleteBlocksFrom(blocks[currentHeight - 1].height); - await HashratesRepository.$deleteHashratesFromTimestamp(blocks[currentHeight - 1].timestamp - 604800); + let partialMsg = false; + let idx = 1; + while (idx < blocks.length) { + if (blocks[idx].height - 1 !== blocks[idx - 1].height) { + if (partialMsg === false) { + logger.info('Some blocks are not indexed, skipping missing blocks during chain validation'); + partialMsg = true; + } + ++idx; + continue; + } + + if (blocks[idx].previous_block_hash !== blocks[idx - 1].hash) { + logger.warn(`Chain divergence detected at block ${blocks[idx - 1].height}, re-indexing newer blocks and hashrates`); + await this.$deleteBlocksFrom(blocks[idx - 1].height); + await HashratesRepository.$deleteHashratesFromTimestamp(blocks[idx - 1].timestamp - 604800); return false; } - ++currentHeight; + ++idx; } - logger.info(`${currentHeight} blocks hash validated in ${new Date().getTime() - start} ms`); + logger.info(`${idx} blocks hash validated in ${new Date().getTime() - start} ms`); return true; } catch (e) { logger.err('Cannot validate chain of block hash. Reason: ' + (e instanceof Error ? e.message : e)); @@ -435,9 +527,9 @@ class BlocksRepository { public async $getHistoricalBlockFees(div: number, interval: string | null): Promise { try { let query = `SELECT - CAST(AVG(height) as INT) as avg_height, + CAST(AVG(height) as INT) as avgHeight, CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, - CAST(AVG(fees) as INT) as avg_fees + CAST(AVG(fees) as INT) as avgFees FROM blocks`; if (interval !== null) { @@ -457,12 +549,12 @@ class BlocksRepository { /** * Get the historical averaged block rewards */ - public async $getHistoricalBlockRewards(div: number, interval: string | null): Promise { + public async $getHistoricalBlockRewards(div: number, interval: string | null): Promise { try { let query = `SELECT - CAST(AVG(height) as INT) as avg_height, + CAST(AVG(height) as INT) as avgHeight, CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, - CAST(AVG(reward) as INT) as avg_rewards + CAST(AVG(reward) as INT) as avgRewards FROM blocks`; if (interval !== null) { @@ -485,15 +577,15 @@ class BlocksRepository { public async $getHistoricalBlockFeeRates(div: number, interval: string | null): Promise { try { let query = `SELECT - CAST(AVG(height) as INT) as avg_height, + CAST(AVG(height) as INT) as avgHeight, CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, - CAST(AVG(JSON_EXTRACT(fee_span, '$[0]')) as INT) as avg_fee_0, - CAST(AVG(JSON_EXTRACT(fee_span, '$[1]')) as INT) as avg_fee_10, - CAST(AVG(JSON_EXTRACT(fee_span, '$[2]')) as INT) as avg_fee_25, - CAST(AVG(JSON_EXTRACT(fee_span, '$[3]')) as INT) as avg_fee_50, - CAST(AVG(JSON_EXTRACT(fee_span, '$[4]')) as INT) as avg_fee_75, - CAST(AVG(JSON_EXTRACT(fee_span, '$[5]')) as INT) as avg_fee_90, - CAST(AVG(JSON_EXTRACT(fee_span, '$[6]')) as INT) as avg_fee_100 + CAST(AVG(JSON_EXTRACT(fee_span, '$[0]')) as INT) as avgFee_0, + CAST(AVG(JSON_EXTRACT(fee_span, '$[1]')) as INT) as avgFee_10, + CAST(AVG(JSON_EXTRACT(fee_span, '$[2]')) as INT) as avgFee_25, + CAST(AVG(JSON_EXTRACT(fee_span, '$[3]')) as INT) as avgFee_50, + CAST(AVG(JSON_EXTRACT(fee_span, '$[4]')) as INT) as avgFee_75, + CAST(AVG(JSON_EXTRACT(fee_span, '$[5]')) as INT) as avgFee_90, + CAST(AVG(JSON_EXTRACT(fee_span, '$[6]')) as INT) as avgFee_100 FROM blocks`; if (interval !== null) { @@ -516,9 +608,9 @@ class BlocksRepository { public async $getHistoricalBlockSizes(div: number, interval: string | null): Promise { try { let query = `SELECT - CAST(AVG(height) as INT) as avg_height, + CAST(AVG(height) as INT) as avgHeight, CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, - CAST(AVG(size) as INT) as avg_size + CAST(AVG(size) as INT) as avgSize FROM blocks`; if (interval !== null) { @@ -541,9 +633,9 @@ class BlocksRepository { public async $getHistoricalBlockWeights(div: number, interval: string | null): Promise { try { let query = `SELECT - CAST(AVG(height) as INT) as avg_height, + CAST(AVG(height) as INT) as avgHeight, CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, - CAST(AVG(weight) as INT) as avg_weight + CAST(AVG(weight) as INT) as avgWeight FROM blocks`; if (interval !== null) { diff --git a/backend/src/repositories/HashratesRepository.ts b/backend/src/repositories/HashratesRepository.ts index 661535aa3..531b6cdcf 100644 --- a/backend/src/repositories/HashratesRepository.ts +++ b/backend/src/repositories/HashratesRepository.ts @@ -1,3 +1,4 @@ +import { escape } from 'mysql2'; import { Common } from '../api/common'; import DB from '../database'; import logger from '../logger'; @@ -105,7 +106,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`); + throw new Error('This mining pool does not exist ' + escape(slug)); } // Find hashrate boundaries diff --git a/backend/src/repositories/PoolsRepository.ts b/backend/src/repositories/PoolsRepository.ts index 037a6250a..c7cc6cba3 100644 --- a/backend/src/repositories/PoolsRepository.ts +++ b/backend/src/repositories/PoolsRepository.ts @@ -78,7 +78,6 @@ class PoolsRepository { const [rows]: any[] = await DB.query(query, [slug]); if (rows.length < 1) { - logger.debug(`This slug does not match any known pool`); return null; } diff --git a/backend/src/repositories/RatesRepository.ts b/backend/src/repositories/RatesRepository.ts new file mode 100644 index 000000000..e84ef2827 --- /dev/null +++ b/backend/src/repositories/RatesRepository.ts @@ -0,0 +1,21 @@ +import DB from '../database'; +import logger from '../logger'; +import { IConversionRates } from '../mempool.interfaces'; + +class RatesRepository { + public async $saveRate(height: number, rates: IConversionRates) { + try { + await DB.query(`INSERT INTO rates(height, bisq_rates) VALUE (?, ?)`, [height, JSON.stringify(rates)]); + } catch (e: any) { + if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart + logger.debug(`Rate already exists for block ${height}, ignoring`); + } else { + logger.err(`Cannot save exchange rate into db for block ${height} Reason: ` + (e instanceof Error ? e.message : e)); + throw e; + } + } + } +} + +export default new RatesRepository(); + diff --git a/backend/src/routes.ts b/backend/src/routes.ts index 72bf3b483..91c41faa6 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -572,9 +572,9 @@ class Routes { } } - public async $getPools(interval: string, req: Request, res: Response) { + public async $getPools(req: Request, res: Response) { try { - const stats = await miningStats.$getPoolsStats(interval); + const stats = await miningStats.$getPoolsStats(req.params.interval); const blockCount = await BlocksRepository.$blockCount(null, null); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); @@ -588,7 +588,7 @@ class Routes { public async $getPoolsHistoricalHashrate(req: Request, res: Response) { try { - const hashrates = await HashratesRepository.$getPoolsWeeklyHashrate(req.params.interval ?? null); + const hashrates = await HashratesRepository.$getPoolsWeeklyHashrate(req.params.interval); const blockCount = await BlocksRepository.$blockCount(null, null); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); @@ -619,9 +619,17 @@ class Routes { } public async $getHistoricalHashrate(req: Request, res: Response) { + let currentHashrate = 0, currentDifficulty = 0; try { - const hashrates = await HashratesRepository.$getNetworkDailyHashrate(req.params.interval ?? null); - const difficulty = await BlocksRepository.$getBlocksDifficulty(req.params.interval ?? null); + currentHashrate = await bitcoinClient.getNetworkHashPs(); + currentDifficulty = await bitcoinClient.getDifficulty(); + } catch (e) { + logger.debug('Bitcoin Core is not available, using zeroed value for current hashrate and difficulty'); + } + + try { + const hashrates = await HashratesRepository.$getNetworkDailyHashrate(req.params.interval); + const difficulty = await BlocksRepository.$getBlocksDifficulty(req.params.interval); const blockCount = await BlocksRepository.$blockCount(null, null); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); @@ -630,8 +638,8 @@ class Routes { res.json({ hashrates: hashrates, difficulty: difficulty, - currentHashrate: await bitcoinClient.getNetworkHashPs(), - currentDifficulty: await bitcoinClient.getDifficulty(), + currentHashrate: currentHashrate, + currentDifficulty: currentDifficulty, }); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); @@ -640,12 +648,12 @@ class Routes { public async $getHistoricalBlockFees(req: Request, res: Response) { try { - const blockFees = await mining.$getHistoricalBlockFees(req.params.interval ?? null); + const blockFees = await mining.$getHistoricalBlockFees(req.params.interval); const blockCount = await BlocksRepository.$blockCount(null, null); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); res.header('X-total-count', blockCount.toString()); - res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString()); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(blockFees); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); @@ -654,12 +662,12 @@ class Routes { public async $getHistoricalBlockRewards(req: Request, res: Response) { try { - const blockRewards = await mining.$getHistoricalBlockRewards(req.params.interval ?? null); + const blockRewards = await mining.$getHistoricalBlockRewards(req.params.interval); const blockCount = await BlocksRepository.$blockCount(null, null); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); res.header('X-total-count', blockCount.toString()); - res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString()); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(blockRewards); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); @@ -668,15 +676,13 @@ class Routes { public async $getHistoricalBlockFeeRates(req: Request, res: Response) { try { - const blockFeeRates = await mining.$getHistoricalBlockFeeRates(req.params.interval ?? null); - const oldestIndexedBlockTimestamp = await BlocksRepository.$oldestBlockTimestamp(); + const blockFeeRates = await mining.$getHistoricalBlockFeeRates(req.params.interval); + const blockCount = await BlocksRepository.$blockCount(null, null); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); - res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString()); - res.json({ - oldestIndexedBlockTimestamp: oldestIndexedBlockTimestamp, - blockFeeRates: blockFeeRates, - }); + res.header('X-total-count', blockCount.toString()); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); + res.json(blockFeeRates); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); } @@ -684,13 +690,13 @@ class Routes { public async $getHistoricalBlockSizeAndWeight(req: Request, res: Response) { try { - const blockSizes = await mining.$getHistoricalBlockSizes(req.params.interval ?? null); - const blockWeights = await mining.$getHistoricalBlockWeights(req.params.interval ?? null); + const blockSizes = await mining.$getHistoricalBlockSizes(req.params.interval); + const blockWeights = await mining.$getHistoricalBlockWeights(req.params.interval); const blockCount = await BlocksRepository.$blockCount(null, null); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); res.header('X-total-count', blockCount.toString()); - res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString()); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json({ sizes: blockSizes, weights: blockWeights @@ -702,8 +708,9 @@ class Routes { public async getBlock(req: Request, res: Response) { try { - const result = await bitcoinApi.$getBlock(req.params.hash); - res.json(result); + const block = await blocks.$getBlock(req.params.hash); + res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString()); + res.json(block); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); } @@ -719,19 +726,22 @@ class Routes { } } - public async getBlocksExtras(req: Request, res: Response) { + public async getBlocks(req: Request, res: Response) { try { - const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10); - res.json(await blocks.$getBlocksExtras(height, 15)); + if (['mainnet', 'testnet', 'signet', 'regtest'].includes(config.MEMPOOL.NETWORK)) { // Bitcoin + 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 + return await this.getLegacyBlocks(req, res); + } } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); } } - - public async getBlocks(req: Request, res: Response) { - try { - loadingIndicators.setProgress('blocks', 0); + public async getLegacyBlocks(req: Request, res: Response) { + try { const returnBlocks: IEsploraApi.Block[] = []; const fromHeight = parseInt(req.params.height, 10) || blocks.getCurrentBlockHeight(); @@ -755,16 +765,15 @@ class Routes { returnBlocks.push(block); nextHash = block.previousblockhash; } - loadingIndicators.setProgress('blocks', i / 10 * 100); } + res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(returnBlocks); } catch (e) { - loadingIndicators.setProgress('blocks', 100); res.status(500).send(e instanceof Error ? e.message : e); } } - + public async getBlockTransactions(req: Request, res: Response) { try { loadingIndicators.setProgress('blocktxs-' + req.params.hash, 0); @@ -776,9 +785,9 @@ class Routes { const endIndex = Math.min(startingIndex + 10, txIds.length); for (let i = startingIndex; i < endIndex; i++) { try { - const transaction = await transactionUtils.$getTransactionExtended(txIds[i], true); + const transaction = await transactionUtils.$getTransactionExtended(txIds[i], true, true); transactions.push(transaction); - loadingIndicators.setProgress('blocktxs-' + req.params.hash, (i + 1) / endIndex * 100); + loadingIndicators.setProgress('blocktxs-' + req.params.hash, (i - startingIndex + 1) / (endIndex - startingIndex) * 100); } catch (e) { logger.debug('getBlockTransactions error: ' + (e instanceof Error ? e.message : e)); } @@ -1001,6 +1010,7 @@ class Routes { public async $getRewardStats(req: Request, res: Response) { try { const response = await mining.$getRewardStats(parseInt(req.params.blockCount, 10)); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(response); } catch (e) { res.status(500).end(); diff --git a/backend/src/tasks/pools-updater.ts b/backend/src/tasks/pools-updater.ts index aee786ff9..05a1da5dc 100644 --- a/backend/src/tasks/pools-updater.ts +++ b/backend/src/tasks/pools-updater.ts @@ -1,8 +1,10 @@ -const https = require('https'); +import axios from 'axios'; import poolsParser from '../api/pools-parser'; import config from '../config'; import DB from '../database'; import logger from '../logger'; +import { SocksProxyAgent } from 'socks-proxy-agent'; +import * as https from 'https'; /** * Maintain the most recent version of pools.json @@ -14,7 +16,7 @@ class PoolsUpdater { } public async updatePoolsJson() { - if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) { + if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false || config.DATABASE.ENABLED === false) { return; } @@ -28,6 +30,13 @@ class PoolsUpdater { this.lastRun = now; + logger.info('Updating latest mining pools from Github'); + if (config.SOCKS5PROXY.ENABLED) { + logger.info('List of public pools will be queried over the Tor network'); + } else { + logger.info('List of public pools will be queried over clearnet'); + } + try { const dbSha = await this.getShaFromDb(); const githubSha = await this.fetchPoolsSha(); // Fetch pools.json sha from github @@ -41,7 +50,10 @@ class PoolsUpdater { } logger.warn('Pools.json is outdated, fetch latest from github'); - const poolsJson = await this.fetchPools(); + const poolsJson = await this.query('https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json'); + if (poolsJson === undefined) { + return; + } await poolsParser.migratePoolsJson(poolsJson); await this.updateDBSha(githubSha); logger.notice('PoolsUpdater completed'); @@ -52,14 +64,6 @@ class PoolsUpdater { } } - /** - * Fetch pools.json from github repo - */ - private async fetchPools(): Promise { - const response = await this.query('/repos/mempool/mining-pools/contents/pools.json'); - return JSON.parse(Buffer.from(response['content'], 'base64').toString('utf8')); - } - /** * Fetch our latest pools.json sha from the db */ @@ -90,11 +94,13 @@ class PoolsUpdater { * Fetch our latest pools.json sha from github */ private async fetchPoolsSha(): Promise { - const response = await this.query('/repos/mempool/mining-pools/git/trees/master'); + const response = await this.query('https://api.github.com/repos/mempool/mining-pools/git/trees/master'); - for (const file of response['tree']) { - if (file['path'] === 'pools.json') { - return file['sha']; + if (response !== undefined) { + for (const file of response['tree']) { + if (file['path'] === 'pools.json') { + return file['sha']; + } } } @@ -105,35 +111,45 @@ class PoolsUpdater { /** * Http request wrapper */ - private query(path): Promise { - return new Promise((resolve, reject) => { - const options = { - host: 'api.github.com', - path: path, - method: 'GET', - headers: { 'user-agent': 'node.js' } + private async query(path): Promise { + type axiosOptions = { + httpsAgent?: https.Agent; + } + const setDelay = (secs: number = 1): Promise => new Promise(resolve => setTimeout(() => resolve(), secs * 1000)); + const axiosOptions: axiosOptions = {}; + let retry = 0; + + if (config.SOCKS5PROXY.ENABLED) { + const socksOptions: any = { + agentOptions: { + keepAlive: true, + }, + hostname: config.SOCKS5PROXY.HOST, + port: config.SOCKS5PROXY.PORT }; - logger.debug('Querying: api.github.com' + path); + if (config.SOCKS5PROXY.USERNAME && config.SOCKS5PROXY.PASSWORD) { + socksOptions.username = config.SOCKS5PROXY.USERNAME; + socksOptions.password = config.SOCKS5PROXY.PASSWORD; + } - const request = https.get(options, (response) => { - const chunks_of_data: any[] = []; - response.on('data', (fragments) => { - chunks_of_data.push(fragments); - }); - response.on('end', () => { - resolve(JSON.parse(Buffer.concat(chunks_of_data).toString())); - }); - response.on('error', (error) => { - reject(error); - }); - }); + axiosOptions.httpsAgent = new SocksProxyAgent(socksOptions); + } - request.on('error', (error) => { - logger.err('Github API query failed. Reason: ' + error); - reject(error); - }); - }); + while(retry < 5) { + try { + const data = await axios.get(path, axiosOptions); + if (data.statusText !== 'OK' || !data.data) { + throw new Error(`Could not fetch data from Github, Error: ${data.status}`); + } + return data.data; + } catch (e) { + logger.err('Could not connect to Github. Reason: ' + (e instanceof Error ? e.message : e)); + retry++; + } + await setDelay(); + } + return undefined; } } diff --git a/backend/src/utils/blocks-utils.ts b/backend/src/utils/blocks-utils.ts index 7b5c0b23a..937a37448 100644 --- a/backend/src/utils/blocks-utils.ts +++ b/backend/src/utils/blocks-utils.ts @@ -1,4 +1,4 @@ -import { BlockExtended } from "../mempool.interfaces"; +import { BlockExtended } from '../mempool.interfaces'; export function prepareBlock(block: any): BlockExtended { return { @@ -15,11 +15,13 @@ export function prepareBlock(block: any): BlockExtended { weight: block.weight, previousblockhash: block.previousblockhash, extras: { - coinbaseRaw: block.coinbase_raw ?? block.extras.coinbaseRaw, + coinbaseRaw: block.coinbase_raw ?? block.extras?.coinbaseRaw, medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee, - feeRange: block.feeRange ?? block.fee_range ?? block?.extras?.feeSpan, + feeRange: block.feeRange ?? block.fee_span, reward: block.reward ?? block?.extras?.reward, - totalFees: block.totalFees ?? block?.fees ?? block?.extras.totalFees, + totalFees: block.totalFees ?? block?.fees ?? block?.extras?.totalFees, + avgFee: block?.extras?.avgFee ?? block.avg_fee, + avgFeeRate: block?.avgFeeRate ?? block.avg_fee_rate, pool: block?.extras?.pool ?? (block?.pool_id ? { id: block.pool_id, name: block.pool_name, diff --git a/contributors/ayanamidev.txt b/contributors/ayanamidev.txt new file mode 100644 index 000000000..c397f7286 --- /dev/null +++ b/contributors/ayanamidev.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 May 15, 2022. + +Signed: ayanamidev diff --git a/docker/README.md b/docker/README.md index 37a2cc079..45bdff615 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,18 +1,23 @@ # Docker Installation -This directory contains the Dockerfiles used to build and release the official images and a `docker-compose.yml` for end users to run a Mempool instance with minimal effort. +This directory contains the Dockerfiles used to build and release the official images, as well as a `docker-compose.yml` to configure environment variables and other settings. -You can choose to configure Mempool to run with a basic backend powered by just `bitcoind`, or with `bitcoind` along with an Electrum-compatible server for full functionality. +If you are looking to use these Docker images to deploy your own instance of Mempool, note that they only containerize Mempool's frontend and backend. You will still need to deploy and configure Bitcoin Core and an Electrum Server separately, along with any other utilities specific to your use case (e.g., a reverse proxy, etc). Such configuration is mostly beyond the scope of the Mempool project, so please only proceed if you know what you're doing. -## `bitcoind`-only Configuration +Jump to a section in this doc: +- [Configure with Bitcoin Core Only](#configure-with-bitcoin-core-only) +- [Configure with Bitcoin Core + Electrum Server](#configure-with-bitcoin-core--electrum-server) +- [Further Configuration](#further-configuration) -_Note: address lookups require an Electrum server and will not work with this configuration._ +## Configure with Bitcoin Core Only -Make sure `bitcoind` is running and synced. +_Note: address lookups require an Electrum Server and will not work with this configuration. [Add an Electrum Server](#configure-with-bitcoin-core--electrum-server) to your backend for full functionality._ -The default Docker configuration assumes you have added RPC credentials for a `mempool` user with a `mempool` password in your `bitcoin.conf` file, like so: +The default Docker configuration assumes you have the following configuration in your `bitcoin.conf` file: ``` +txindex=1 +server=1 rpcuser=mempool rpcpassword=mempool ``` @@ -31,6 +36,8 @@ If you want to use different credentials, specify them in the `docker-compose.ym The IP address in the example above refers to Docker's default gateway IP address so that the container can hit the `bitcoind` instance running on the host machine. If your setup is different, update it accordingly. +Make sure `bitcoind` is running and synced. + Now, run: ```bash @@ -39,11 +46,11 @@ docker-compose up Your Mempool instance should be running at http://localhost. The graphs will be populated as new transactions are detected. -## `bitcoind` + Electrum Server Configuration +## Configure with Bitcoin Core + Electrum Server -First, configure `bitcoind` as specified above, and make sure your Electrum server is running and synced. +First, configure `bitcoind` as specified above, and make sure your Electrum Server is running and synced. See [this FAQ](https://mempool.space/docs/faq#address-lookup-issues) if you need help picking an Electrum Server implementation. -Then, make sure the following variables are set in `docker-compose.yml`, as shown below, so Mempool can connect to your Electrum server: +Then, set the following variables in `docker-compose.yml` so Mempool can connect to your Electrum Server: ``` api: @@ -54,6 +61,11 @@ Then, make sure the following variables are set in `docker-compose.yml`, as show ELECTRUM_TLS_ENABLED: "false" ``` +Eligible values for `MEMPOOL_BACKEND`: + - "electrum" if you're using [romanz/electrs](https://github.com/romanz/electrs) or [cculianu/Fulcrum](https://github.com/cculianu/Fulcrum) + - "esplora" if you're using [Blockstream/electrs](https://github.com/Blockstream/electrs) + - "none" if you're not using any Electrum Server + Of course, if your Docker host IP address is different, update accordingly. With `bitcoind` and Electrum Server set up, run Mempool with: diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index c013fc23a..31acff047 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16.10.0-buster-slim AS builder +FROM node:16.15.0-buster-slim AS builder ARG commitHash ENV DOCKER_COMMIT_HASH=${commitHash} @@ -11,7 +11,7 @@ RUN apt-get install -y build-essential python3 pkg-config RUN npm install RUN npm run build -FROM node:16.10.0-buster-slim +FROM node:16.15.0-buster-slim WORKDIR /backend diff --git a/docker/frontend/Dockerfile b/docker/frontend/Dockerfile index 34c41119c..e2874ff4e 100644 --- a/docker/frontend/Dockerfile +++ b/docker/frontend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16.10.0-buster-slim AS builder +FROM node:16.15.0-buster-slim AS builder ARG commitHash ENV DOCKER_COMMIT_HASH=${commitHash} diff --git a/frontend/README.md b/frontend/README.md index f7fb08552..e223f17c2 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,8 +1,30 @@ -# mempool-frontend +# Mempool Frontend -## Contributing +You can build and run the Mempool frontend and proxy to the production Mempool backend (for easier frontend development), or you can connect it to your own backend for a full Mempool development instance, custom deployment, etc. -This package is used for the https://mempool.space, https://liquid.network and https://bisq.markets websites - there are npm scripts to setup all three, which effectively change how BASE_MODULE is configured: +Jump to a section in this doc: +- [Quick Setup for Frontend Development](#quick-setup-for-frontend-development) +- [Manual Frontend Setup](#manual-setup) +- [Translations](#translations-transifex-project) + +## Quick Setup for Frontend Development + +If you want to quickly improve the UI, fix typos, or make other updates that don't require any backend changes, you don't need to set up an entire backend—you can simply run the Mempool frontend locally and proxy to the mempool.space backend. + +### 1. Clone Mempool Repository + +Get the latest Mempool code: + +``` +git clone https://github.com/mempool/mempool +cd mempool +``` + +### 2. Specify Website + +The same frontend codebase is used for https://mempool.space, https://liquid.network and https://bisq.markets. + +Configure the frontend for the site you want by running the corresponding command: ``` $ npm run config:defaults:mempool @@ -10,18 +32,22 @@ $ npm run config:defaults:liquid $ npm run config:defaults:bisq ``` -Changes that affect the frontend codebase only can be done using the production backend so you don't need to spin up the entire Mempool infrastructure. This is very convenient in case you want to quickly improve the UI, fix typos or implement new features that don't require any backend changes. +### 3. Run the Frontend -Make your changes, install the project dependencies and run the frontend server as follows: +_Node.js 16 and npm 7 are recommended._ + +Install project dependencies and run the frontend server: ``` $ npm install $ npm run serve:local-prod ``` -The frontend will be available at http://localhost:4200/ and all API requests will be proxied to the production server at https://mempool.space +The frontend will be available at http://localhost:4200/ and all API requests will be proxied to the production server at https://mempool.space. -After making your changes, you can run our end-to-end automation suite and check for possible regressions: +### 4. Test + +After making your changes, you can run our end-to-end automation suite and check for possible regressions. Headless: @@ -37,11 +63,43 @@ $ npm run config:defaults:mempool && npm run cypress:open This will open the Cypress test runner, where you can select any of the test files to run. -If all tests are green, submit your PR and it will be reviewed by someone on the team as soon as possible. +If all tests are green, submit your PR, and it will be reviewed by someone on the team as soon as possible. + +## Manual Setup + +Set up the [Mempool backend](../backend/) first, if you haven't already. + +### 1. Build the Frontend + +_Node.js 16 and npm 7 are recommended._ + +Build the frontend: + +``` +cd frontend +npm install # add --prod for production +npm run build +``` + +### 2. Run the Frontend + +#### Development + +To run your local Mempool frontend with your local Mempool backend: + +``` +npm run serve +``` + +#### Production + +The `npm run build` command from step 1 above should have generated a `dist` directory. Put the contents of `dist/` onto your web server. + +You will probably want to set up a reverse proxy, TLS, etc. There are sample nginx configuration files in the top level of the repository for reference, but note that support for such tasks is outside the scope of this project. ## Translations: Transifex Project -The mempool frontend strings are localized into 20+ locales: +The Mempool frontend strings are localized into 20+ locales: https://www.transifex.com/mempool/mempool/dashboard/ ### Translators diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 87d57666c..9abadc0eb 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,26 +9,26 @@ "version": "2.4.0-dev", "license": "GNU Affero General Public License v3.0", "dependencies": { - "@angular-devkit/build-angular": "^13.3.4", - "@angular/animations": "~13.3.5", - "@angular/cli": "~13.3.4", - "@angular/common": "~13.3.5", - "@angular/compiler": "~13.3.5", - "@angular/core": "~13.3.5", - "@angular/forms": "~13.3.5", - "@angular/localize": "^13.3.5", - "@angular/platform-browser": "~13.3.5", - "@angular/platform-browser-dynamic": "~13.3.5", - "@angular/platform-server": "~13.3.5", - "@angular/router": "~13.3.5", - "@fortawesome/angular-fontawesome": "0.10.1", - "@fortawesome/fontawesome-common-types": "0.3.0", - "@fortawesome/fontawesome-svg-core": "1.3.0", - "@fortawesome/free-solid-svg-icons": "6.0.0", + "@angular-devkit/build-angular": "~13.3.7", + "@angular/animations": "~13.3.10", + "@angular/cli": "~13.3.7", + "@angular/common": "~13.3.10", + "@angular/compiler": "~13.3.10", + "@angular/core": "~13.3.10", + "@angular/forms": "~13.3.10", + "@angular/localize": "~13.3.10", + "@angular/platform-browser": "~13.3.10", + "@angular/platform-browser-dynamic": "~13.3.10", + "@angular/platform-server": "~13.3.10", + "@angular/router": "~13.3.10", + "@fortawesome/angular-fontawesome": "~0.10.2", + "@fortawesome/fontawesome-common-types": "~6.1.1", + "@fortawesome/fontawesome-svg-core": "~6.1.1", + "@fortawesome/free-solid-svg-icons": "~6.1.1", "@juggle/resize-observer": "^3.3.1", "@mempool/mempool.js": "2.3.0", "@ng-bootstrap/ng-bootstrap": "^11.0.0", - "@nguniversal/express-engine": "12.1.3", + "@nguniversal/express-engine": "~13.1.1", "@types/qrcode": "~1.4.2", "bootstrap": "~4.5.0", "browserify": "^17.0.0", @@ -41,24 +41,24 @@ "ngx-echarts": "8.0.1", "ngx-infinite-scroll": "^10.0.1", "qrcode": "1.5.0", - "rxjs": "^6.6.7", + "rxjs": "~7.5.5", "tinyify": "^3.0.0", "tlite": "^0.1.9", - "tslib": "^2.2.0", - "zone.js": "~0.11.4" + "tslib": "~2.4.0", + "zone.js": "~0.11.5" }, "devDependencies": { - "@angular/compiler-cli": "~13.3.5", - "@angular/language-service": "~13.3.5", - "@nguniversal/builders": "~13.1.0", + "@angular/compiler-cli": "~13.3.10", + "@angular/language-service": "~13.3.10", + "@nguniversal/builders": "~13.1.1", "@types/express": "^4.17.0", - "@types/jasmine": "~3.6.0", - "@types/jasminewd2": "~2.0.3", + "@types/jasmine": "~4.0.3", + "@types/jasminewd2": "~2.0.10", "@types/node": "^12.11.1", - "codelyzer": "^6.0.1", + "codelyzer": "~6.0.2", "http-proxy-middleware": "^1.0.5", - "jasmine-core": "~3.6.0", - "jasmine-spec-reporter": "~5.0.0", + "jasmine-core": "~4.1.0", + "jasmine-spec-reporter": "~7.0.0", "karma": "~6.3.19", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.0.3", @@ -66,11 +66,11 @@ "karma-jasmine-html-reporter": "^1.5.0", "ts-node": "~8.3.0", "tslint": "~6.1.0", - "typescript": "~4.4.4" + "typescript": "~4.6.4" }, "optionalDependencies": { "@cypress/schematic": "^1.3.0", - "cypress": "^9.5.2", + "cypress": "^9.6.1", "cypress-fail-on-console-error": "^2.1.3", "cypress-wait-until": "^1.7.1", "mock-socket": "^9.0.3", @@ -78,23 +78,23 @@ } }, "node_modules/@ampproject/remapping": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.1.1.tgz", - "integrity": "sha512-YVAcA4DKLOj296CF5SrQ8cYiMRiUGc2sqFpLxsDGWE34suHqhGP/5yMsDHKsrh8hs8I5TiRVXNwKPWQpX3iGjw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "sourcemap-codec": "1.4.8" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@angular-devkit/architect": { - "version": "0.1303.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1303.4.tgz", - "integrity": "sha512-d6YmIWdYvwk6WaknHRcJgiXeJvX9K5i8uPMAaL2P2/LU8n3moIQ59C7SP0uULcHuuiREEmFWOyyrWnGxZCI9bg==", + "version": "0.1303.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1303.7.tgz", + "integrity": "sha512-xr35v7AuJygRdiaFhgoBSLN2ZMUri8x8Qx9jkmCkD3WLKz33TSFyAyqwdNNmOO9riK8ePXMH/QcSv0wY12pFBw==", "dependencies": { - "@angular-devkit/core": "13.3.4", + "@angular-devkit/core": "13.3.7", "rxjs": "6.6.7" }, "engines": { @@ -103,15 +103,31 @@ "yarn": ">= 1.13.0" } }, - "node_modules/@angular-devkit/build-angular": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.3.4.tgz", - "integrity": "sha512-z74cmDi2V+5XpvyZKFlUXxvQ446shxyZk5aGdToG6n+0/IJWkDXSiryQkCo8nblGMze7HKf75i3DsGWYQZLDnQ==", + "node_modules/@angular-devkit/architect/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dependencies": { - "@ampproject/remapping": "1.1.1", - "@angular-devkit/architect": "0.1303.4", - "@angular-devkit/build-webpack": "0.1303.4", - "@angular-devkit/core": "13.3.4", + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@angular-devkit/build-angular": { + "version": "13.3.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.3.7.tgz", + "integrity": "sha512-XUmiq/3zpuna+r0UOqNSvA9kEcPwsLblEmNLUYyZXL9v/aGWUHOSH0nhGVrNRrSud4ryklEnxfkxkxlZlT4mjQ==", + "dependencies": { + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1303.7", + "@angular-devkit/build-webpack": "0.1303.7", + "@angular-devkit/core": "13.3.7", "@babel/core": "7.16.12", "@babel/generator": "7.16.8", "@babel/helper-annotate-as-pure": "7.16.7", @@ -122,9 +138,9 @@ "@babel/runtime": "7.16.7", "@babel/template": "7.16.7", "@discoveryjs/json-ext": "0.5.6", - "@ngtools/webpack": "13.3.4", + "@ngtools/webpack": "13.3.7", "ansi-colors": "4.1.1", - "babel-loader": "8.2.3", + "babel-loader": "8.2.5", "babel-plugin-istanbul": "6.1.1", "browserslist": "^4.9.1", "cacache": "15.3.0", @@ -212,70 +228,19 @@ } } }, - "node_modules/@angular-devkit/build-angular/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" - }, + "node_modules/@angular-devkit/build-angular/node_modules/@ngtools/webpack": { + "version": "13.3.7", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.3.7.tgz", + "integrity": "sha512-KtNMHOGZIU2oaNTzk97ZNwTnJLbvnSpwyG3/+VW9xN92b2yw8gG9tHPKW2fsFrfzF9Mz8kqJeF31ftvkYuKtuA==", "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@angular-devkit/build-angular/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/@angular-devkit/build-angular/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/@angular-devkit/build-angular/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/@angular-devkit/build-angular/node_modules/critters": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", - "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", - "dependencies": { - "chalk": "^4.1.0", - "css-select": "^4.2.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "postcss": "^8.3.7", - "pretty-bytes": "^5.3.0" - } - }, - "node_modules/@angular-devkit/build-angular/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" + "peerDependencies": { + "@angular/compiler-cli": "^13.0.0", + "typescript": ">=4.4.3 <4.7", + "webpack": "^5.30.0" } }, "node_modules/@angular-devkit/build-angular/node_modules/loader-utils": { @@ -286,23 +251,33 @@ "node": ">= 12.13.0" } }, - "node_modules/@angular-devkit/build-angular/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==", + "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dependencies": { - "has-flag": "^4.0.0" + "tslib": "^1.9.0" }, "engines": { - "node": ">=8" + "npm": ">=2.0.0" } }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1303.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1303.4.tgz", - "integrity": "sha512-3F10P9XshRXkI/PEmJUcgP4yK4sobaoInQfifzPNOemrS5nXs8y3uEiQuxzyswYx/dymZLV+19sV/eh1WfXnBA==", + "version": "0.1303.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1303.7.tgz", + "integrity": "sha512-5vF399cPdwuCbzbxS4yNGgChdAzEM0/By21P0uiqBcIe/Zxuz3IUPapjvcyhkAo5OTu+d7smY9eusLHqoq1WFQ==", "dependencies": { - "@angular-devkit/architect": "0.1303.4", + "@angular-devkit/architect": "0.1303.7", "rxjs": "6.6.7" }, "engines": { @@ -315,10 +290,26 @@ "webpack-dev-server": "^4.0.0" } }, + "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@angular-devkit/core": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.4.tgz", - "integrity": "sha512-gj6i8ksPaT2bvYwI7wKJxLX53pHfTmZc1RaNbAGfZB1/zFNnb3MPj8utTcJSk4qMsGXuDDhiB7hpTKBw8ROaGA==", + "version": "13.3.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.7.tgz", + "integrity": "sha512-Ucy4bJmlgCoBenuVeGMdtW9dE8+cD+guWCgqexsFIG21KJ/l0ShZEZ/dGC1XibzaIs1HbKiTr/T1MOjInCV1rA==", "dependencies": { "ajv": "8.9.0", "ajv-formats": "2.1.1", @@ -377,6 +368,22 @@ "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/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@angular-devkit/schematics": { "version": "12.2.6", "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-12.2.6.tgz", @@ -451,10 +458,28 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "optional": true }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "optional": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, "node_modules/@angular/animations": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.3.5.tgz", - "integrity": "sha512-BYXX80N1hxkATWqv2IZddfKvqxomktxH5fREv+KjwIYFkyK9KBnRBILqynyIJaXXut5KE6QNOf1zWEZxdOnc3A==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.3.10.tgz", + "integrity": "sha512-V/0h3xepWPBRjWroFXYrNIE3iZPREjv0hiB3gskF/2KLlx5jvpUWlaBx0rEYRa8XXIPJyAaKBGwWSBnT/Z88TQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -462,19 +487,19 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/core": "13.3.5" + "@angular/core": "13.3.10" } }, "node_modules/@angular/cli": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.3.4.tgz", - "integrity": "sha512-4S5FNjkZgq98zcBVgwkYtMgMRMSVsprCgq7dM8yTxIQh+Np3fYgj5eRJ1+mfFG/kankH2z/TFyuoYiILh2D9Uw==", + "version": "13.3.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.3.7.tgz", + "integrity": "sha512-XIp0w0YOwhHp4Je3npHAs0W4rjHvFnG2w/lDO2M/UNp5634S4PRMFmVVMt6DQBj1cbffYVKFqffqesyCqNuvAQ==", "hasInstallScript": true, "dependencies": { - "@angular-devkit/architect": "0.1303.4", - "@angular-devkit/core": "13.3.4", - "@angular-devkit/schematics": "13.3.4", - "@schematics/angular": "13.3.4", + "@angular-devkit/architect": "0.1303.7", + "@angular-devkit/core": "13.3.7", + "@angular-devkit/schematics": "13.3.7", + "@schematics/angular": "13.3.7", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.1", "debug": "4.3.3", @@ -501,11 +526,11 @@ } }, "node_modules/@angular/cli/node_modules/@angular-devkit/schematics": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.3.4.tgz", - "integrity": "sha512-gKNpMMoZJjLKdXxjuVembic4GWa4AYV7kU1ou3ZuZoDKtKcig9URISr1wjS+nrhKYz+miFy0zIqSGMMattDlDQ==", + "version": "13.3.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.3.7.tgz", + "integrity": "sha512-6TKpFMwiiXmPhiVdbkSJrkBXj8n7SVVhsHl2GodDLVTb8OT3fxYIB9EU8Il07AMfDcjpydOcJduCFPOsQYd7BA==", "dependencies": { - "@angular-devkit/core": "13.3.4", + "@angular-devkit/core": "13.3.7", "jsonc-parser": "3.0.0", "magic-string": "0.25.7", "ora": "5.4.1", @@ -518,12 +543,12 @@ } }, "node_modules/@angular/cli/node_modules/@schematics/angular": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.3.4.tgz", - "integrity": "sha512-Cta11k965Igz2kWj60KQ/9z6RFAg9FjZ8i1TH4nyROJs9nWemWPQNA+OJFuXrEy6Ldpk7yJ5cWgJsyryGB25PA==", + "version": "13.3.7", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.3.7.tgz", + "integrity": "sha512-OAny1e/yliku52xG7vfWs1hNYSgCNTPpMv9fS8zz9eF5/GrKv28WFSy20mUXqLZ91VsbGSs6X0mI6pdNnpVtJA==", "dependencies": { - "@angular-devkit/core": "13.3.4", - "@angular-devkit/schematics": "13.3.4", + "@angular-devkit/core": "13.3.7", + "@angular-devkit/schematics": "13.3.7", "jsonc-parser": "3.0.0" }, "engines": { @@ -556,10 +581,26 @@ "node": ">=10" } }, + "node_modules/@angular/cli/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular/cli/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@angular/common": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-13.3.5.tgz", - "integrity": "sha512-teG+itdlw2sOMwYeXkeFe8h32SsNqN0qHHz/v6I9qKHgCLkC/or8A7NtsoCwYSTymIEJJ7DZ1w9VWhM7DSYd1w==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-13.3.10.tgz", + "integrity": "sha512-KWw91QzmCDZ6uq1Z58v7vQQ57Ux7A2UkPdIBOyvpOgtQPTvlvKsePkUVCC+dum+W9mOy4kq2falO5T7Gi7SJgw==", "dependencies": { "tslib": "^2.3.0" }, @@ -567,14 +608,14 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/core": "13.3.5", + "@angular/core": "13.3.10", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-13.3.5.tgz", - "integrity": "sha512-iSQlYevMk5glwZSXTXf2GytykqZWdK3Rr8heIvEPqd8n88MSB3w1KnDc1fnHLF950q/nUR9K+3r4wWPwc8J2IQ==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-13.3.10.tgz", + "integrity": "sha512-DEtdso89Q9lAGkSVpSf2GrMtGVTnCnenCwLhubYaeSaj4iA/CAnUfNlaYBf9E92ltuPd85Mg9bIJKaxYCRH8RQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -583,9 +624,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.3.5.tgz", - "integrity": "sha512-H7A+MZcbB4g5fa6O4giYgrCG1h5whJfIxr4txDtDfolygzwRzqH1PSMfjW/jYyIpaH6XqXMSDHvbXRFGKstboA==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.3.10.tgz", + "integrity": "sha512-cGFQyUOxOLVnehczdP4L7KXbKQTe/aQgbXmacQYgqcP/AnpJs7QfZbw1/k1wJtXrhzbGBh3JSWnpme74bnF3dQ==", "dependencies": { "@babel/core": "^7.17.2", "chokidar": "^3.0.0", @@ -607,22 +648,10 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/compiler": "13.3.5", + "@angular/compiler": "13.3.10", "typescript": ">=4.4.2 <4.7" } }, - "node_modules/@angular/compiler-cli/node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@angular/compiler-cli/node_modules/@babel/core": { "version": "7.17.9", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz", @@ -782,9 +811,9 @@ } }, "node_modules/@angular/core": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-13.3.5.tgz", - "integrity": "sha512-lf+Be8dDRvz8J+QFR2RxS3BBfgGM4eWq4bI1+k/aqDnM6OW4pQXdq8Lzae8SxN48u1NxB1M/1bbc9LcrChrj2Q==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-13.3.10.tgz", + "integrity": "sha512-7jH1a5wZdE6Ki2Dow7s6v1/5SfUcXsjAu3n523QSDlM078QG0p95npcqPseO9mNftG9MfRqBE7sl1Nb+ZK7eBg==", "dependencies": { "tslib": "^2.3.0" }, @@ -797,9 +826,9 @@ } }, "node_modules/@angular/forms": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.3.5.tgz", - "integrity": "sha512-jCxxAwf4HkDmKE76/yQmTsbqW3jsxiKyPy32Nh6Bt4r/ww8VDv+sv5YdYNuvvZcuuQ70K+/EPnKFpQgYttvS8A==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.3.10.tgz", + "integrity": "sha512-2cREi8nvCdspYHk6KJ5xjIgq8Dgh/kfwPIVjpLQBZFNC03Q6GvOLVoVm8ye6ToOpQFjvjpjndqU93JXSLMANgA==", "dependencies": { "tslib": "^2.3.0" }, @@ -807,25 +836,25 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "13.3.5", - "@angular/core": "13.3.5", - "@angular/platform-browser": "13.3.5", + "@angular/common": "13.3.10", + "@angular/core": "13.3.10", + "@angular/platform-browser": "13.3.10", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-13.3.5.tgz", - "integrity": "sha512-IJawCyu4Zwk6GNPDkbSkY6sFYaBHtaHMwhaiRojrqiKA0n2bDNULLcHfYGSyA7UvkX8m9Nt0M5GaF66BIwuZSw==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-13.3.10.tgz", + "integrity": "sha512-TQwVIEFTWOlX9Jy2PhOT52Eo3ApNWSkjQavAuIU4uNQRCyoKMTywJ6MlQiQlMoWPH77Yn5EZyCwRoWFVWg3q0w==", "dev": true, "engines": { "node": "^12.20.0 || ^14.15.0 || >=16.10.0" } }, "node_modules/@angular/localize": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-13.3.5.tgz", - "integrity": "sha512-0MmGvQSBZeKwsOBATWp7Y5rxyGW6OaszLtecKu32VVJSjoDN4M6uMHBMjVVe4IxbyJnbhlSPRSYL9hRFbOve0A==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-13.3.10.tgz", + "integrity": "sha512-DNSOLJd8SkYHWKWyBm/piYnjurYRsgXTmWoVXTrfEuALEHxz3cwnVUPvoiWwJVMKklFr76D61pDY4mz5muPxog==", "dependencies": { "@babel/core": "7.17.2", "glob": "7.2.0", @@ -840,20 +869,8 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/compiler": "13.3.5", - "@angular/compiler-cli": "13.3.5" - } - }, - "node_modules/@angular/localize/node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "@angular/compiler": "13.3.10", + "@angular/compiler-cli": "13.3.10" } }, "node_modules/@angular/localize/node_modules/@babel/core": { @@ -1004,9 +1021,9 @@ } }, "node_modules/@angular/platform-browser": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.3.5.tgz", - "integrity": "sha512-DPV1J3h1ua4GI9PuXDr8IlzJoC/TR0A/onPTaE6IFOzs1r28vB+vWRRbZURXrTYeWzWVB/2R9tPOqFNoi3zlzA==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.3.10.tgz", + "integrity": "sha512-zi0FrA8zZRiHLBfKlfIxikG06wMF2KcSp6oqrIblrc1VrHgPRVRABz8vryH84lasDssjYdIS9AvbQnCCdgCzJA==", "dependencies": { "tslib": "^2.3.0" }, @@ -1014,9 +1031,9 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/animations": "13.3.5", - "@angular/common": "13.3.5", - "@angular/core": "13.3.5" + "@angular/animations": "13.3.10", + "@angular/common": "13.3.10", + "@angular/core": "13.3.10" }, "peerDependenciesMeta": { "@angular/animations": { @@ -1025,9 +1042,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-13.3.5.tgz", - "integrity": "sha512-Mko8/mRlcVsZJQ5zHfc/p7so/ZN16UMynTnksrD7cEgGxDuJosE8m+exqgoT03VCYaOjJtCFXSwdOb/8FOUDZQ==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-13.3.10.tgz", + "integrity": "sha512-hygsEjTaS+VDUrBZZiRJFo5J7AHCS/EcAc1IWvb69EnVqA9RwqM4hWbuy3y/cmLEeHLLmRldIlS6xRPt8fTNQg==", "dependencies": { "tslib": "^2.3.0" }, @@ -1035,16 +1052,16 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "13.3.5", - "@angular/compiler": "13.3.5", - "@angular/core": "13.3.5", - "@angular/platform-browser": "13.3.5" + "@angular/common": "13.3.10", + "@angular/compiler": "13.3.10", + "@angular/core": "13.3.10", + "@angular/platform-browser": "13.3.10" } }, "node_modules/@angular/platform-server": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-13.3.5.tgz", - "integrity": "sha512-ja9/mgBIAriQO3H9Q1MTXxJV1s59a2NJeeOffGHr86TLDcaxxzumozfoaKr5p6m0Ty3yqZcFc3wUQJl5I/1H0A==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-13.3.10.tgz", + "integrity": "sha512-KekOeqzdL9tL9h7bDuYoz1utnd6p6y6qAvAvCGm2Qa1fX3f/NQQ12pngtVqhvIgB5PpROCNypla/ejcBmNsQ2g==", "dependencies": { "domino": "^2.1.2", "tslib": "^2.3.0", @@ -1054,18 +1071,18 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/animations": "13.3.5", - "@angular/common": "13.3.5", - "@angular/compiler": "13.3.5", - "@angular/core": "13.3.5", - "@angular/platform-browser": "13.3.5", - "@angular/platform-browser-dynamic": "13.3.5" + "@angular/animations": "13.3.10", + "@angular/common": "13.3.10", + "@angular/compiler": "13.3.10", + "@angular/core": "13.3.10", + "@angular/platform-browser": "13.3.10", + "@angular/platform-browser-dynamic": "13.3.10" } }, "node_modules/@angular/router": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-13.3.5.tgz", - "integrity": "sha512-3yUNyBpUi0KUKP91a3dVQsr9Jfjs4wGxpiFYb3apc7lKT5R1LJqt2O0EchjOgvFyJ6TDOemdCAzKgvSRkDxpMw==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-13.3.10.tgz", + "integrity": "sha512-neGaeiHravXlCbNbyGJecwQyu/91Pj/E9/ohVFzBBE4V9BrNx9v7Ntc4ugqgpnrV2wtonPP7TQDqXxrPk4QVfg==", "dependencies": { "tslib": "^2.3.0" }, @@ -1073,9 +1090,9 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "13.3.5", - "@angular/core": "13.3.5", - "@angular/platform-browser": "13.3.5", + "@angular/common": "13.3.10", + "@angular/core": "13.3.10", + "@angular/platform-browser": "13.3.10", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -2863,44 +2880,44 @@ } }, "node_modules/@fortawesome/angular-fontawesome": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.10.1.tgz", - "integrity": "sha512-4nVRm+NcLcdaNrNFhThb/7/tb5CDm0vQwJFyljR3XMCQyEr94hMX5SiUYbvYm9YJVImMYfdpR0lO0B8sh12Vbw==", + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.10.2.tgz", + "integrity": "sha512-VxsCAo2lK74KwD236AKAhGpiethfz9yqCViIG2iRAZqgNmuZ6ihwumjbLW32n6hV4fFvCqLcHmpngoEl3TNiOg==", "dependencies": { "tslib": "^2.3.1" }, "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1.2.27 || ~1.3.0-beta2" + "@fortawesome/fontawesome-svg-core": "~1.2.27 || ~1.3.0-beta2 || ^6.1.0" } }, "node_modules/@fortawesome/fontawesome-common-types": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.3.0.tgz", - "integrity": "sha512-CA3MAZBTxVsF6SkfkHXDerkhcQs0QPofy43eFdbWJJkZiq3SfiaH1msOkac59rQaqto5EqWnASboY1dBuKen5w==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.1.tgz", + "integrity": "sha512-wVn5WJPirFTnzN6tR95abCx+ocH+3IFLXAgyavnf9hUmN0CfWoDjPT/BAWsUVwSlYYVBeCLJxaqi7ZGe4uSjBA==", "hasInstallScript": true, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.3.0.tgz", - "integrity": "sha512-UIL6crBWhjTNQcONt96ExjUnKt1D68foe3xjEensLDclqQ6YagwCRYVQdrp/hW0ALRp/5Fv/VKw+MqTUWYYvPg==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.1.1.tgz", + "integrity": "sha512-NCg0w2YIp81f4V6cMGD9iomfsIj7GWrqmsa0ZsPh59G7PKiGN1KymZNxmF00ssuAlo/VZmpK6xazsGOwzKYUMg==", "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "^0.3.0" + "@fortawesome/fontawesome-common-types": "6.1.1" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.0.0.tgz", - "integrity": "sha512-o4FZ1XbndcgeWNb8Wh0y+Hgf73CjmyOQowUSaqQCtgIIdS+XliSBSOwCl330wER+I6CGYE96hT27bHBPmzX2Gg==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.1.1.tgz", + "integrity": "sha512-0/5exxavOhI/D4Ovm2r3vxNojGZioPwmFrKg0ZUH69Q68uFhFPs6+dhAToh6VEQBntxPRYPuT5Cg1tpNa9JUPg==", "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "^0.3.0" + "@fortawesome/fontawesome-common-types": "6.1.1" }, "engines": { "node": ">=6" @@ -3095,30 +3112,15 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@ngtools/webpack": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.3.4.tgz", - "integrity": "sha512-dNDNeAOwtpX5A7TTEsgDbkg4jTmAJHD96qLqcpJqfBg8nZ4mqn6E0HinX9HZKaCST1/75T6GsFo1Muc4MsHYgA==", - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "@angular/compiler-cli": "^13.0.0", - "typescript": ">=4.4.3 <4.7", - "webpack": "^5.30.0" - } - }, "node_modules/@nguniversal/builders": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@nguniversal/builders/-/builders-13.1.0.tgz", - "integrity": "sha512-tG/o04rODUIlMnkW/g9TWt50S6CkX6sCvR+fCS7n7YsLrknnlhj7qMIIEKCiuE+b/7lbfQUe2HepnFEfxLypmw==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@nguniversal/builders/-/builders-13.1.1.tgz", + "integrity": "sha512-R73GKHr7KeTIBE/kSudhsN0V1gx+TrnM28RHdzw3eHCz2Q3msGpgdt/79+2EjLcvWjoVHOsD+aFIJ9+sx422yQ==", "dev": true, "dependencies": { "@angular-devkit/architect": "^0.1303.0", - "@angular-devkit/core": "^13.3.0", - "@nguniversal/common": "13.1.0", + "@angular-devkit/core": "^13.3.4", + "@nguniversal/common": "13.1.1", "browser-sync": "^2.26.7", "express": "^4.17.1", "guess-parser": "^0.4.12", @@ -3132,7 +3134,7 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular-devkit/build-angular": "^13.3.0" + "@angular-devkit/build-angular": "^13.3.4" } }, "node_modules/@nguniversal/builders/node_modules/http-proxy-middleware": { @@ -3173,11 +3175,28 @@ "nice-napi": "^1.0.2" } }, - "node_modules/@nguniversal/common": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@nguniversal/common/-/common-13.1.0.tgz", - "integrity": "sha512-bmRwJoEM5LfA4kDRkuVvoOv6NFAOgn2WpolwQlnLNfGTKL/fyNw/JyIvdEphQdjhTTRadehY8MYbr+X97+hyLw==", + "node_modules/@nguniversal/builders/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@nguniversal/builders/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 + }, + "node_modules/@nguniversal/common": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@nguniversal/common/-/common-13.1.1.tgz", + "integrity": "sha512-DoAPA7+kUz+qMgCTUtRPFcMGY0zz8OSkOTZnxqO5sUYntD6mCEQImHU0WF4ud88j71o0Hv+AISJD1evAAANCdw==", "dependencies": { "critters": "0.0.16", "jsdom": "19.0.0", @@ -3187,15 +3206,14 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "^13.3.0", - "@angular/core": "^13.3.0" + "@angular/common": "^13.3.4", + "@angular/core": "^13.3.4" } }, "node_modules/@nguniversal/common/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==", - "dev": true, "engines": { "node": ">= 10" } @@ -3204,7 +3222,6 @@ "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -3212,80 +3229,15 @@ "node": ">=0.4.0" } }, - "node_modules/@nguniversal/common/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@nguniversal/common/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "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/@nguniversal/common/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==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@nguniversal/common/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==", - "dev": true - }, - "node_modules/@nguniversal/common/node_modules/critters": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", - "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "css-select": "^4.2.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "postcss": "^8.3.7", - "pretty-bytes": "^5.3.0" - } - }, "node_modules/@nguniversal/common/node_modules/cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", - "dev": true + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" }, "node_modules/@nguniversal/common/node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", - "dev": true, "dependencies": { "abab": "^2.0.6", "whatwg-mimetype": "^3.0.0", @@ -3299,7 +3251,6 @@ "version": "11.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dev": true, "dependencies": { "tr46": "^3.0.0", "webidl-conversions": "^7.0.0" @@ -3312,7 +3263,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "dev": true, "dependencies": { "webidl-conversions": "^7.0.0" }, @@ -3324,7 +3274,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -3346,7 +3295,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -3355,7 +3303,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -3365,20 +3312,10 @@ "node": ">= 6" } }, - "node_modules/@nguniversal/common/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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/@nguniversal/common/node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, "dependencies": { "whatwg-encoding": "^2.0.0" }, @@ -3390,7 +3327,6 @@ "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==", - "dev": true, "dependencies": { "@tootallnate/once": "2", "agent-base": "6", @@ -3404,7 +3340,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -3416,7 +3351,6 @@ "version": "19.0.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==", - "dev": true, "dependencies": { "abab": "^2.0.5", "acorn": "^8.5.0", @@ -3462,29 +3396,15 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "optional": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/@nguniversal/common/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@nguniversal/common/node_modules/tr46": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "dev": true, "dependencies": { "punycode": "^2.1.1" }, @@ -3496,7 +3416,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", - "dev": true, "dependencies": { "xml-name-validator": "^4.0.0" }, @@ -3508,7 +3427,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, "engines": { "node": ">=12" } @@ -3517,7 +3435,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, "dependencies": { "iconv-lite": "0.6.3" }, @@ -3529,7 +3446,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true, "engines": { "node": ">=12" } @@ -3538,7 +3454,6 @@ "version": "10.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", - "dev": true, "dependencies": { "tr46": "^3.0.0", "webidl-conversions": "^7.0.0" @@ -3548,10 +3463,9 @@ } }, "node_modules/@nguniversal/common/node_modules/ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", - "dev": true, + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz", + "integrity": "sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==", "engines": { "node": ">=10.0.0" }, @@ -3572,46 +3486,28 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true, "engines": { "node": ">=12" } }, "node_modules/@nguniversal/express-engine": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/@nguniversal/express-engine/-/express-engine-12.1.3.tgz", - "integrity": "sha512-iu/7Al3NjwdwJw3ALyW9n7MzAQ75/Ca/cd2xWi7rzgvev2pgakZfvn2/EbB8oFI6xOSvA2irA29BeobkR4crlw==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@nguniversal/express-engine/-/express-engine-13.1.1.tgz", + "integrity": "sha512-NdiBP0IRbPrNYEMLy3a6os2mNgRNE84tsMn+mV2uF4wv1JNs3YyoXcucWvhgHdODbDtc6z4CGn8t/6KagRqmvA==", "dependencies": { - "@nguniversal/common": "12.1.3", + "@nguniversal/common": "13.1.1", "tslib": "^2.3.0" }, "engines": { - "node": ">=12.13.0" + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "^12.2.12", - "@angular/core": "^12.2.12", - "@angular/platform-server": "^12.2.12", + "@angular/common": "^13.3.4", + "@angular/core": "^13.3.4", + "@angular/platform-server": "^13.3.4", "express": "^4.15.2" } }, - "node_modules/@nguniversal/express-engine/node_modules/@nguniversal/common": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/@nguniversal/common/-/common-12.1.3.tgz", - "integrity": "sha512-0fP8ThHx3vePJxTdic5cz9hvWnqB2Uvx2uf1JQ68zto8ZXrasOeB8kT+ujhuUIyH8AR98BYMudMXCVvq+UbkeA==", - "dependencies": { - "critters": "0.0.12", - "jsdom": "16.6.0", - "tslib": "^2.3.0" - }, - "engines": { - "node": ">=12.13.0" - }, - "peerDependencies": { - "@angular/common": "^12.2.12", - "@angular/core": "^12.2.12" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3834,6 +3730,24 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "optional": true }, + "node_modules/@schematics/angular/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "optional": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@schematics/angular/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, "node_modules/@sideway/address": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz", @@ -3900,9 +3814,9 @@ } }, "node_modules/@socket.io/component-emitter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", - "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==", + "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==", "dev": true }, "node_modules/@tootallnate/once": { @@ -4018,15 +3932,15 @@ } }, "node_modules/@types/jasmine": { - "version": "3.6.9", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.9.tgz", - "integrity": "sha512-B53NIwMj/AO0O+xfSWLYmKB0Mo6TYxfv2Mk8/c1T2w/e38t55iaPR6p7pHXTTtqfTmevPK3i8T1YweYFTZlxDw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.0.3.tgz", + "integrity": "sha512-Opp1LvvEuZdk8fSSvchK2mZwhVrsNT0JgJE9Di6MjnaIpmEXM8TLCPPrVtNTYh8+5MPdY8j9bAHMu2SSfwpZJg==", "dev": true }, "node_modules/@types/jasminewd2": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz", - "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.10.tgz", + "integrity": "sha512-J7mDz7ovjwjc+Y9rR9rY53hFWKATcIkrr9DwQWmOas4/pnIPJTXawnzjwpHm3RSxz/e3ZVUvQ7cRbd5UQLo10g==", "dev": true, "dependencies": { "@types/jasmine": "*" @@ -4755,15 +4669,17 @@ } }, "node_modules/async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } }, "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": "sha1-dhfBkXQB/Yykooqtzj266Yr+tDI=", + "integrity": "sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==", "dev": true, "engines": { "node": ">=0.8.0" @@ -4865,12 +4781,12 @@ } }, "node_modules/babel-loader": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", - "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", "dependencies": { "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", + "loader-utils": "^2.0.0", "make-dir": "^3.1.0", "schema-utils": "^2.6.5" }, @@ -4882,30 +4798,6 @@ "webpack": ">=2" } }, - "node_modules/babel-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/babel-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -4996,12 +4888,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -5149,7 +5035,7 @@ "node_modules/bonjour": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", "dependencies": { "array-flatten": "^2.1.0", "deep-equal": "^1.0.1", @@ -5254,13 +5140,13 @@ } }, "node_modules/browser-sync": { - "version": "2.27.9", - "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.9.tgz", - "integrity": "sha512-3zBtggcaZIeU9so4ja9yxk7/CZu9B3DOL6zkxFpzHCHsQmkGBPVXg61jItbeoa+WXgNLnr1sYES/2yQwyEZ2+w==", + "version": "2.27.10", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.10.tgz", + "integrity": "sha512-xKm+6KJmJu6RuMWWbFkKwOCSqQOxYe3nOrFkKI5Tr/ZzjPxyU3pFShKK3tWnazBo/3lYQzN7fzjixG8fwJh1Xw==", "dev": true, "dependencies": { - "browser-sync-client": "^2.27.9", - "browser-sync-ui": "^2.27.9", + "browser-sync-client": "^2.27.10", + "browser-sync-ui": "^2.27.10", "bs-recipes": "1.3.4", "bs-snippet-injector": "^2.0.1", "chokidar": "^3.5.1", @@ -5277,7 +5163,7 @@ "localtunnel": "^2.0.1", "micromatch": "^4.0.2", "opn": "5.3.0", - "portscanner": "2.1.1", + "portscanner": "2.2.0", "qs": "6.2.3", "raw-body": "^2.3.2", "resp-modifier": "6.0.2", @@ -5298,15 +5184,16 @@ } }, "node_modules/browser-sync-client": { - "version": "2.27.9", - "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.9.tgz", - "integrity": "sha512-FHW8kydp7FXo6jnX3gXJCpHAHtWNLK0nx839nnK+boMfMI1n4KZd0+DmTxHBsHsF3OHud4V4jwoN8U5HExMIdQ==", + "version": "2.27.10", + "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.10.tgz", + "integrity": "sha512-KCFKA1YDj6cNul0VsA28apohtBsdk5Wv8T82ClOZPZMZWxPj4Ny5AUbrj9UlAb/k6pdxE5HABrWDhP9+cjt4HQ==", "dev": true, "dependencies": { "etag": "1.8.1", "fresh": "0.5.2", "mitt": "^1.1.3", - "rxjs": "^5.5.6" + "rxjs": "^5.5.6", + "typescript": "^4.6.2" }, "engines": { "node": ">=8.0.0" @@ -5334,9 +5221,9 @@ } }, "node_modules/browser-sync-ui": { - "version": "2.27.9", - "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.9.tgz", - "integrity": "sha512-rsduR2bRIwFvM8CX6iY/Nu5aWub0WB9zfSYg9Le/RV5N5DEyxJYey0VxdfWCnzDOoelassTDzYQo+r0iJno3qw==", + "version": "2.27.10", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.10.tgz", + "integrity": "sha512-elbJILq4Uo6OQv6gsvS3Y9vRAJlWu+h8j0JDkF0X/ua+3S6SVbbiWnZc8sNOFlG7yvVGIwBED3eaYQ0iBo1Dtw==", "dev": true, "dependencies": { "async-each-series": "0.1.1", @@ -6106,9 +5993,9 @@ } }, "node_modules/clipboard": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.10.tgz", - "integrity": "sha512-cz3m2YVwFz95qSEbCDi2fzLN/epEN9zXBvfgAoGkvGOJZATMl9gtTDVOtBYkx2ODUJl2kvmud7n32sV2BpYR4g==", + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", + "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==", "dependencies": { "good-listener": "^1.2.2", "select": "^1.1.2", @@ -6147,9 +6034,9 @@ } }, "node_modules/codelyzer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.1.tgz", - "integrity": "sha512-cOyGQgMdhnRYtW2xrJUNrNYDjEgwQ+BrE2y93Bwz3h4DJ6vJRLfupemU5N3pbYsUlBHJf0u1j1UGk+NLW4d97g==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.2.tgz", + "integrity": "sha512-v3+E0Ucu2xWJMOJ2fA/q9pDT/hlxHftHGPUay1/1cTgyPV5JTHFdO9hqo837Sx2s9vKBMTt5gO+lhF95PO6J+g==", "dev": true, "dependencies": { "@angular/compiler": "9.0.0", @@ -6166,6 +6053,11 @@ "sprintf-js": "^1.1.2", "tslib": "^1.10.0", "zone.js": "~0.10.3" + }, + "peerDependencies": { + "@angular/compiler": ">=2.3.1 <13.0.0 || ^12.0.0-next || ^12.1.0-next || ^12.2.0-next", + "@angular/core": ">=2.3.1 <13.0.0 || ^12.0.0-next || ^12.1.0-next || ^12.2.0-next", + "tslint": "^5.0.0 || ^6.0.0" } }, "node_modules/codelyzer/node_modules/@angular/compiler": { @@ -6178,6 +6070,18 @@ "integrity": "sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==", "dev": true }, + "node_modules/codelyzer/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, "node_modules/codelyzer/node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -6304,7 +6208,7 @@ "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, "node_modules/component-emitter": { "version": "1.3.0", @@ -6343,7 +6247,7 @@ "node_modules/compression/node_modules/bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "engines": { "node": ">= 0.8" } @@ -6692,12 +6596,12 @@ } }, "node_modules/critters": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.12.tgz", - "integrity": "sha512-ujxKtKc/mWpjrOKeaACTaQ1aP0O31M0ZPWhfl85jZF1smPU4Ivb9va5Ox2poif4zVJQQo0LCFlzGtEZAsCAPcw==", + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", "dependencies": { "chalk": "^4.1.0", - "css-select": "^4.1.3", + "css-select": "^4.2.0", "parse5": "^6.0.1", "parse5-htmlparser2-tree-adapter": "^6.0.1", "postcss": "^8.3.7", @@ -6971,7 +6875,8 @@ "node_modules/cssom": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true }, "node_modules/cssstyle": { "version": "2.3.0", @@ -6996,9 +6901,9 @@ "devOptional": true }, "node_modules/cypress": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.5.2.tgz", - "integrity": "sha512-gYiQYvJozMzDOriUV1rCt6CeRM/pRK4nhwGJj3nJQyX2BoUdTCVwp30xDMKc771HiNVhBtgj5o5/iBdVDVXQUg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.6.1.tgz", + "integrity": "sha512-ECzmV7pJSkk+NuAhEw6C3D+RIRATkSb2VAHXDY6qGZbca/F9mv5pPsj2LO6Ty6oIFVBTrwCyL9agl28MtJMe2g==", "hasInstallScript": true, "optional": true, "dependencies": { @@ -7034,7 +6939,7 @@ "listr2": "^3.8.3", "lodash": "^4.17.21", "log-symbols": "^4.0.0", - "minimist": "^1.2.5", + "minimist": "^1.2.6", "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", "proxy-from-env": "1.0.0", @@ -7264,6 +7169,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, "dependencies": { "abab": "^2.0.3", "whatwg-mimetype": "^2.3.0", @@ -7404,9 +7310,9 @@ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" }, "node_modules/del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", "dependencies": { "globby": "^11.0.1", "graceful-fs": "^4.2.4", @@ -7677,6 +7583,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, "dependencies": { "webidl-conversions": "^5.0.0" }, @@ -7688,6 +7595,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, "engines": { "node": ">=8" } @@ -7891,20 +7799,16 @@ } }, "node_modules/engine.io-client": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz", - "integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.2.tgz", + "integrity": "sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==", "dev": true, "dependencies": { - "@socket.io/component-emitter": "~3.0.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", - "engine.io-parser": "~5.0.0", - "has-cors": "1.1.0", - "parseqs": "0.0.6", - "parseuri": "0.0.6", + "engine.io-parser": "~5.0.3", "ws": "~8.2.3", - "xmlhttprequest-ssl": "~2.0.0", - "yeast": "0.1.2" + "xmlhttprequest-ssl": "~2.0.0" } }, "node_modules/engine.io-client/node_modules/ws": { @@ -9571,12 +9475,6 @@ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" }, - "node_modules/has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -9689,6 +9587,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, "dependencies": { "whatwg-encoding": "^1.0.5" }, @@ -10044,14 +9943,6 @@ "node": ">=8" } }, - "node_modules/inquirer/node_modules/rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", - "dependencies": { - "tslib": "~2.1.0" - } - }, "node_modules/inquirer/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -10063,11 +9954,6 @@ "node": ">=8" } }, - "node_modules/inquirer/node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - }, "node_modules/insert-module-globals": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", @@ -10543,15 +10429,15 @@ } }, "node_modules/jasmine-core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz", - "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.1.1.tgz", + "integrity": "sha512-lmUfT5XcK9KKvt3lLYzn93hc4MGzlUBowExFVgzbSW0ZCrdeyS574dfsyfRhxbg81Wj4gk+RxUiTnj7KBfDA1g==", "dev": true }, "node_modules/jasmine-spec-reporter": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz", - "integrity": "sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-7.0.0.tgz", + "integrity": "sha512-OtC7JRasiTcjsaCBPtMO0Tl8glCejM4J4/dNuOJdA8lBjz4PmWjYQ6pzb0uzpBNAWJMDudYuj9OdXJWqM2QTJg==", "dev": true, "dependencies": { "colors": "1.4.0" @@ -10632,6 +10518,7 @@ "version": "16.6.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz", "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==", + "dev": true, "dependencies": { "abab": "^2.0.5", "acorn": "^8.2.4", @@ -10677,6 +10564,7 @@ "version": "8.7.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -10688,6 +10576,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -10709,6 +10598,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "engines": { "node": ">=4.0" } @@ -10717,6 +10607,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -10730,6 +10621,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "optional": true, "engines": { "node": ">=0.10.0" @@ -10947,12 +10839,6 @@ "integrity": "sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q==", "dev": true }, - "node_modules/karma-jasmine/node_modules/jasmine-core": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.1.0.tgz", - "integrity": "sha512-8E8BiffCL8sBwK1zU9cbavLe8xpJAgOduSJ6N8PJVv8VosQ/nxVTuXj2kUeHxTlZBVvh24G19ga7xdiaxlceKg==", - "dev": true - }, "node_modules/karma-source-map-support": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", @@ -11274,6 +11160,24 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "optional": true }, + "node_modules/listr2/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "optional": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/listr2/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, "node_modules/listr2/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -11720,9 +11624,9 @@ } }, "node_modules/memfs": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", - "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.3.tgz", + "integrity": "sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==", "dependencies": { "fs-monkey": "1.0.3" }, @@ -13181,18 +13085,6 @@ "parse5": "^6.0.1" } }, - "node_modules/parseqs": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", - "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==", - "dev": true - }, - "node_modules/parseuri": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", - "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==", - "dev": true - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -13366,14 +13258,6 @@ "node": ">= 0.12.0" } }, - "node_modules/portfinder/node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dependencies": { - "lodash": "^4.17.14" - } - }, "node_modules/portfinder/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -13383,12 +13267,12 @@ } }, "node_modules/portscanner": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.1.1.tgz", - "integrity": "sha1-6rtAnk3iSVD1oqUW01rnaTQ/u5Y=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", + "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", "dev": true, "dependencies": { - "async": "1.5.2", + "async": "^2.6.0", "is-number-like": "^1.0.3" }, "engines": { @@ -14535,21 +14419,13 @@ "dev": true }, "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" + "tslib": "^2.1.0" } }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -15098,29 +14974,27 @@ "devOptional": true }, "node_modules/socket.io-client": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz", - "integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.1.tgz", + "integrity": "sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA==", "dev": true, "dependencies": { - "@socket.io/component-emitter": "~3.0.0", - "backo2": "~1.0.2", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", - "engine.io-client": "~6.1.1", - "parseuri": "0.0.6", - "socket.io-parser": "~4.1.1" + "engine.io-client": "~6.2.1", + "socket.io-parser": "~4.2.0" }, "engines": { "node": ">=10.0.0" } }, "node_modules/socket.io-client/node_modules/socket.io-parser": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz", - "integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.0.tgz", + "integrity": "sha512-tLfmEwcEwnlQTxFB7jibL/q2+q8dlVQzj4JdRLJ/W/G1+Fu9VSxCx1Lo+n1HvXxKnM//dUuD0xgiA7tQf57Vng==", "dev": true, "dependencies": { - "@socket.io/component-emitter": "~3.0.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" }, "engines": { @@ -15909,6 +15783,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, "dependencies": { "punycode": "^2.1.1" }, @@ -15990,9 +15865,9 @@ } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/tslint": { "version": "6.1.3", @@ -16125,9 +16000,9 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "node_modules/typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16454,6 +16329,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, "dependencies": { "xml-name-validator": "^3.0.0" }, @@ -16480,6 +16356,24 @@ "node": ">=8.9.0" } }, + "node_modules/wait-on/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "optional": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/wait-on/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, "node_modules/watchpack": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", @@ -16512,6 +16406,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, "engines": { "node": ">=10.4" } @@ -16831,9 +16726,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz", + "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==", "engines": { "node": ">=10.0.0" }, @@ -16951,6 +16846,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, "dependencies": { "iconv-lite": "0.4.24" } @@ -16958,12 +16854,14 @@ "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==" + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true }, "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==", + "dev": true, "dependencies": { "lodash": "^4.7.0", "tr46": "^2.1.0", @@ -17094,6 +16992,7 @@ "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true, "engines": { "node": ">=8.3.0" } @@ -17109,7 +17008,8 @@ "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==" + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true }, "node_modules/xmlchars": { "version": "2.2.0", @@ -17264,12 +17164,6 @@ "fd-slicer": "~1.1.0" } }, - "node_modules/yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -17291,11 +17185,11 @@ } }, "node_modules/zone.js": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", - "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.5.tgz", + "integrity": "sha512-D1/7VxEuQ7xk6z/kAROe4SUbd9CzxY4zOwVGnGHerd/SgLIVU5f4esDzQUsOCeArn933BZfWMKydH7l7dPEp0g==", "dependencies": { - "tslib": "^2.0.0" + "tslib": "^2.3.0" } }, "node_modules/zrender": { @@ -17314,32 +17208,47 @@ }, "dependencies": { "@ampproject/remapping": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.1.1.tgz", - "integrity": "sha512-YVAcA4DKLOj296CF5SrQ8cYiMRiUGc2sqFpLxsDGWE34suHqhGP/5yMsDHKsrh8hs8I5TiRVXNwKPWQpX3iGjw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "sourcemap-codec": "1.4.8" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, "@angular-devkit/architect": { - "version": "0.1303.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1303.4.tgz", - "integrity": "sha512-d6YmIWdYvwk6WaknHRcJgiXeJvX9K5i8uPMAaL2P2/LU8n3moIQ59C7SP0uULcHuuiREEmFWOyyrWnGxZCI9bg==", + "version": "0.1303.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1303.7.tgz", + "integrity": "sha512-xr35v7AuJygRdiaFhgoBSLN2ZMUri8x8Qx9jkmCkD3WLKz33TSFyAyqwdNNmOO9riK8ePXMH/QcSv0wY12pFBw==", "requires": { - "@angular-devkit/core": "13.3.4", + "@angular-devkit/core": "13.3.7", "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } } }, "@angular-devkit/build-angular": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.3.4.tgz", - "integrity": "sha512-z74cmDi2V+5XpvyZKFlUXxvQ446shxyZk5aGdToG6n+0/IJWkDXSiryQkCo8nblGMze7HKf75i3DsGWYQZLDnQ==", + "version": "13.3.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.3.7.tgz", + "integrity": "sha512-XUmiq/3zpuna+r0UOqNSvA9kEcPwsLblEmNLUYyZXL9v/aGWUHOSH0nhGVrNRrSud4ryklEnxfkxkxlZlT4mjQ==", "requires": { - "@ampproject/remapping": "1.1.1", - "@angular-devkit/architect": "0.1303.4", - "@angular-devkit/build-webpack": "0.1303.4", - "@angular-devkit/core": "13.3.4", + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1303.7", + "@angular-devkit/build-webpack": "0.1303.7", + "@angular-devkit/core": "13.3.7", "@babel/core": "7.16.12", "@babel/generator": "7.16.8", "@babel/helper-annotate-as-pure": "7.16.7", @@ -17350,9 +17259,9 @@ "@babel/runtime": "7.16.7", "@babel/template": "7.16.7", "@discoveryjs/json-ext": "0.5.6", - "@ngtools/webpack": "13.3.4", + "@ngtools/webpack": "13.3.7", "ansi-colors": "4.1.1", - "babel-loader": "8.2.3", + "babel-loader": "8.2.5", "babel-plugin-istanbul": "6.1.1", "browserslist": "^4.9.1", "cacache": "15.3.0", @@ -17403,82 +17312,67 @@ "webpack-subresource-integrity": "5.1.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==" - }, - "critters": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", - "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", - "requires": { - "chalk": "^4.1.0", - "css-select": "^4.2.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "postcss": "^8.3.7", - "pretty-bytes": "^5.3.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==" + "@ngtools/webpack": { + "version": "13.3.7", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.3.7.tgz", + "integrity": "sha512-KtNMHOGZIU2oaNTzk97ZNwTnJLbvnSpwyG3/+VW9xN92b2yw8gG9tHPKW2fsFrfzF9Mz8kqJeF31ftvkYuKtuA==", + "requires": {} }, "loader-utils": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==" }, - "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==", + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "requires": { - "has-flag": "^4.0.0" + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "@angular-devkit/build-webpack": { - "version": "0.1303.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1303.4.tgz", - "integrity": "sha512-3F10P9XshRXkI/PEmJUcgP4yK4sobaoInQfifzPNOemrS5nXs8y3uEiQuxzyswYx/dymZLV+19sV/eh1WfXnBA==", + "version": "0.1303.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1303.7.tgz", + "integrity": "sha512-5vF399cPdwuCbzbxS4yNGgChdAzEM0/By21P0uiqBcIe/Zxuz3IUPapjvcyhkAo5OTu+d7smY9eusLHqoq1WFQ==", "requires": { - "@angular-devkit/architect": "0.1303.4", + "@angular-devkit/architect": "0.1303.7", "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } } }, "@angular-devkit/core": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.4.tgz", - "integrity": "sha512-gj6i8ksPaT2bvYwI7wKJxLX53pHfTmZc1RaNbAGfZB1/zFNnb3MPj8utTcJSk4qMsGXuDDhiB7hpTKBw8ROaGA==", + "version": "13.3.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.7.tgz", + "integrity": "sha512-Ucy4bJmlgCoBenuVeGMdtW9dE8+cD+guWCgqexsFIG21KJ/l0ShZEZ/dGC1XibzaIs1HbKiTr/T1MOjInCV1rA==", "requires": { "ajv": "8.9.0", "ajv-formats": "2.1.1", @@ -17511,6 +17405,19 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" } } }, @@ -17565,26 +17472,41 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "optional": true + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "optional": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true } } }, "@angular/animations": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.3.5.tgz", - "integrity": "sha512-BYXX80N1hxkATWqv2IZddfKvqxomktxH5fREv+KjwIYFkyK9KBnRBILqynyIJaXXut5KE6QNOf1zWEZxdOnc3A==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.3.10.tgz", + "integrity": "sha512-V/0h3xepWPBRjWroFXYrNIE3iZPREjv0hiB3gskF/2KLlx5jvpUWlaBx0rEYRa8XXIPJyAaKBGwWSBnT/Z88TQ==", "requires": { "tslib": "^2.3.0" } }, "@angular/cli": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.3.4.tgz", - "integrity": "sha512-4S5FNjkZgq98zcBVgwkYtMgMRMSVsprCgq7dM8yTxIQh+Np3fYgj5eRJ1+mfFG/kankH2z/TFyuoYiILh2D9Uw==", + "version": "13.3.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.3.7.tgz", + "integrity": "sha512-XIp0w0YOwhHp4Je3npHAs0W4rjHvFnG2w/lDO2M/UNp5634S4PRMFmVVMt6DQBj1cbffYVKFqffqesyCqNuvAQ==", "requires": { - "@angular-devkit/architect": "0.1303.4", - "@angular-devkit/core": "13.3.4", - "@angular-devkit/schematics": "13.3.4", - "@schematics/angular": "13.3.4", + "@angular-devkit/architect": "0.1303.7", + "@angular-devkit/core": "13.3.7", + "@angular-devkit/schematics": "13.3.7", + "@schematics/angular": "13.3.7", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.1", "debug": "4.3.3", @@ -17603,11 +17525,11 @@ }, "dependencies": { "@angular-devkit/schematics": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.3.4.tgz", - "integrity": "sha512-gKNpMMoZJjLKdXxjuVembic4GWa4AYV7kU1ou3ZuZoDKtKcig9URISr1wjS+nrhKYz+miFy0zIqSGMMattDlDQ==", + "version": "13.3.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.3.7.tgz", + "integrity": "sha512-6TKpFMwiiXmPhiVdbkSJrkBXj8n7SVVhsHl2GodDLVTb8OT3fxYIB9EU8Il07AMfDcjpydOcJduCFPOsQYd7BA==", "requires": { - "@angular-devkit/core": "13.3.4", + "@angular-devkit/core": "13.3.7", "jsonc-parser": "3.0.0", "magic-string": "0.25.7", "ora": "5.4.1", @@ -17615,12 +17537,12 @@ } }, "@schematics/angular": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.3.4.tgz", - "integrity": "sha512-Cta11k965Igz2kWj60KQ/9z6RFAg9FjZ8i1TH4nyROJs9nWemWPQNA+OJFuXrEy6Ldpk7yJ5cWgJsyryGB25PA==", + "version": "13.3.7", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.3.7.tgz", + "integrity": "sha512-OAny1e/yliku52xG7vfWs1hNYSgCNTPpMv9fS8zz9eF5/GrKv28WFSy20mUXqLZ91VsbGSs6X0mI6pdNnpVtJA==", "requires": { - "@angular-devkit/core": "13.3.4", - "@angular-devkit/schematics": "13.3.4", + "@angular-devkit/core": "13.3.7", + "@angular-devkit/schematics": "13.3.7", "jsonc-parser": "3.0.0" } }, @@ -17636,29 +17558,42 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" } } }, "@angular/common": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-13.3.5.tgz", - "integrity": "sha512-teG+itdlw2sOMwYeXkeFe8h32SsNqN0qHHz/v6I9qKHgCLkC/or8A7NtsoCwYSTymIEJJ7DZ1w9VWhM7DSYd1w==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-13.3.10.tgz", + "integrity": "sha512-KWw91QzmCDZ6uq1Z58v7vQQ57Ux7A2UkPdIBOyvpOgtQPTvlvKsePkUVCC+dum+W9mOy4kq2falO5T7Gi7SJgw==", "requires": { "tslib": "^2.3.0" } }, "@angular/compiler": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-13.3.5.tgz", - "integrity": "sha512-iSQlYevMk5glwZSXTXf2GytykqZWdK3Rr8heIvEPqd8n88MSB3w1KnDc1fnHLF950q/nUR9K+3r4wWPwc8J2IQ==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-13.3.10.tgz", + "integrity": "sha512-DEtdso89Q9lAGkSVpSf2GrMtGVTnCnenCwLhubYaeSaj4iA/CAnUfNlaYBf9E92ltuPd85Mg9bIJKaxYCRH8RQ==", "requires": { "tslib": "^2.3.0" } }, "@angular/compiler-cli": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.3.5.tgz", - "integrity": "sha512-H7A+MZcbB4g5fa6O4giYgrCG1h5whJfIxr4txDtDfolygzwRzqH1PSMfjW/jYyIpaH6XqXMSDHvbXRFGKstboA==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.3.10.tgz", + "integrity": "sha512-cGFQyUOxOLVnehczdP4L7KXbKQTe/aQgbXmacQYgqcP/AnpJs7QfZbw1/k1wJtXrhzbGBh3JSWnpme74bnF3dQ==", "requires": { "@babel/core": "^7.17.2", "chokidar": "^3.0.0", @@ -17672,15 +17607,6 @@ "yargs": "^17.2.1" }, "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, "@babel/core": { "version": "7.17.9", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz", @@ -17801,46 +17727,37 @@ } }, "@angular/core": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-13.3.5.tgz", - "integrity": "sha512-lf+Be8dDRvz8J+QFR2RxS3BBfgGM4eWq4bI1+k/aqDnM6OW4pQXdq8Lzae8SxN48u1NxB1M/1bbc9LcrChrj2Q==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-13.3.10.tgz", + "integrity": "sha512-7jH1a5wZdE6Ki2Dow7s6v1/5SfUcXsjAu3n523QSDlM078QG0p95npcqPseO9mNftG9MfRqBE7sl1Nb+ZK7eBg==", "requires": { "tslib": "^2.3.0" } }, "@angular/forms": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.3.5.tgz", - "integrity": "sha512-jCxxAwf4HkDmKE76/yQmTsbqW3jsxiKyPy32Nh6Bt4r/ww8VDv+sv5YdYNuvvZcuuQ70K+/EPnKFpQgYttvS8A==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.3.10.tgz", + "integrity": "sha512-2cREi8nvCdspYHk6KJ5xjIgq8Dgh/kfwPIVjpLQBZFNC03Q6GvOLVoVm8ye6ToOpQFjvjpjndqU93JXSLMANgA==", "requires": { "tslib": "^2.3.0" } }, "@angular/language-service": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-13.3.5.tgz", - "integrity": "sha512-IJawCyu4Zwk6GNPDkbSkY6sFYaBHtaHMwhaiRojrqiKA0n2bDNULLcHfYGSyA7UvkX8m9Nt0M5GaF66BIwuZSw==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-13.3.10.tgz", + "integrity": "sha512-TQwVIEFTWOlX9Jy2PhOT52Eo3ApNWSkjQavAuIU4uNQRCyoKMTywJ6MlQiQlMoWPH77Yn5EZyCwRoWFVWg3q0w==", "dev": true }, "@angular/localize": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-13.3.5.tgz", - "integrity": "sha512-0MmGvQSBZeKwsOBATWp7Y5rxyGW6OaszLtecKu32VVJSjoDN4M6uMHBMjVVe4IxbyJnbhlSPRSYL9hRFbOve0A==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-13.3.10.tgz", + "integrity": "sha512-DNSOLJd8SkYHWKWyBm/piYnjurYRsgXTmWoVXTrfEuALEHxz3cwnVUPvoiWwJVMKklFr76D61pDY4mz5muPxog==", "requires": { "@babel/core": "7.17.2", "glob": "7.2.0", "yargs": "^17.2.1" }, "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, "@babel/core": { "version": "7.17.2", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.2.tgz", @@ -17951,25 +17868,25 @@ } }, "@angular/platform-browser": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.3.5.tgz", - "integrity": "sha512-DPV1J3h1ua4GI9PuXDr8IlzJoC/TR0A/onPTaE6IFOzs1r28vB+vWRRbZURXrTYeWzWVB/2R9tPOqFNoi3zlzA==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.3.10.tgz", + "integrity": "sha512-zi0FrA8zZRiHLBfKlfIxikG06wMF2KcSp6oqrIblrc1VrHgPRVRABz8vryH84lasDssjYdIS9AvbQnCCdgCzJA==", "requires": { "tslib": "^2.3.0" } }, "@angular/platform-browser-dynamic": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-13.3.5.tgz", - "integrity": "sha512-Mko8/mRlcVsZJQ5zHfc/p7so/ZN16UMynTnksrD7cEgGxDuJosE8m+exqgoT03VCYaOjJtCFXSwdOb/8FOUDZQ==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-13.3.10.tgz", + "integrity": "sha512-hygsEjTaS+VDUrBZZiRJFo5J7AHCS/EcAc1IWvb69EnVqA9RwqM4hWbuy3y/cmLEeHLLmRldIlS6xRPt8fTNQg==", "requires": { "tslib": "^2.3.0" } }, "@angular/platform-server": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-13.3.5.tgz", - "integrity": "sha512-ja9/mgBIAriQO3H9Q1MTXxJV1s59a2NJeeOffGHr86TLDcaxxzumozfoaKr5p6m0Ty3yqZcFc3wUQJl5I/1H0A==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-13.3.10.tgz", + "integrity": "sha512-KekOeqzdL9tL9h7bDuYoz1utnd6p6y6qAvAvCGm2Qa1fX3f/NQQ12pngtVqhvIgB5PpROCNypla/ejcBmNsQ2g==", "requires": { "domino": "^2.1.2", "tslib": "^2.3.0", @@ -17977,9 +17894,9 @@ } }, "@angular/router": { - "version": "13.3.5", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-13.3.5.tgz", - "integrity": "sha512-3yUNyBpUi0KUKP91a3dVQsr9Jfjs4wGxpiFYb3apc7lKT5R1LJqt2O0EchjOgvFyJ6TDOemdCAzKgvSRkDxpMw==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-13.3.10.tgz", + "integrity": "sha512-neGaeiHravXlCbNbyGJecwQyu/91Pj/E9/ohVFzBBE4V9BrNx9v7Ntc4ugqgpnrV2wtonPP7TQDqXxrPk4QVfg==", "requires": { "tslib": "^2.3.0" } @@ -19241,32 +19158,32 @@ "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==" }, "@fortawesome/angular-fontawesome": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.10.1.tgz", - "integrity": "sha512-4nVRm+NcLcdaNrNFhThb/7/tb5CDm0vQwJFyljR3XMCQyEr94hMX5SiUYbvYm9YJVImMYfdpR0lO0B8sh12Vbw==", + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.10.2.tgz", + "integrity": "sha512-VxsCAo2lK74KwD236AKAhGpiethfz9yqCViIG2iRAZqgNmuZ6ihwumjbLW32n6hV4fFvCqLcHmpngoEl3TNiOg==", "requires": { "tslib": "^2.3.1" } }, "@fortawesome/fontawesome-common-types": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.3.0.tgz", - "integrity": "sha512-CA3MAZBTxVsF6SkfkHXDerkhcQs0QPofy43eFdbWJJkZiq3SfiaH1msOkac59rQaqto5EqWnASboY1dBuKen5w==" + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.1.tgz", + "integrity": "sha512-wVn5WJPirFTnzN6tR95abCx+ocH+3IFLXAgyavnf9hUmN0CfWoDjPT/BAWsUVwSlYYVBeCLJxaqi7ZGe4uSjBA==" }, "@fortawesome/fontawesome-svg-core": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.3.0.tgz", - "integrity": "sha512-UIL6crBWhjTNQcONt96ExjUnKt1D68foe3xjEensLDclqQ6YagwCRYVQdrp/hW0ALRp/5Fv/VKw+MqTUWYYvPg==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.1.1.tgz", + "integrity": "sha512-NCg0w2YIp81f4V6cMGD9iomfsIj7GWrqmsa0ZsPh59G7PKiGN1KymZNxmF00ssuAlo/VZmpK6xazsGOwzKYUMg==", "requires": { - "@fortawesome/fontawesome-common-types": "^0.3.0" + "@fortawesome/fontawesome-common-types": "6.1.1" } }, "@fortawesome/free-solid-svg-icons": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.0.0.tgz", - "integrity": "sha512-o4FZ1XbndcgeWNb8Wh0y+Hgf73CjmyOQowUSaqQCtgIIdS+XliSBSOwCl330wER+I6CGYE96hT27bHBPmzX2Gg==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.1.1.tgz", + "integrity": "sha512-0/5exxavOhI/D4Ovm2r3vxNojGZioPwmFrKg0ZUH69Q68uFhFPs6+dhAToh6VEQBntxPRYPuT5Cg1tpNa9JUPg==", "requires": { - "@fortawesome/fontawesome-common-types": "^0.3.0" + "@fortawesome/fontawesome-common-types": "6.1.1" } }, "@gar/promisify": { @@ -19425,21 +19342,15 @@ "tslib": "^2.3.0" } }, - "@ngtools/webpack": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.3.4.tgz", - "integrity": "sha512-dNDNeAOwtpX5A7TTEsgDbkg4jTmAJHD96qLqcpJqfBg8nZ4mqn6E0HinX9HZKaCST1/75T6GsFo1Muc4MsHYgA==", - "requires": {} - }, "@nguniversal/builders": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@nguniversal/builders/-/builders-13.1.0.tgz", - "integrity": "sha512-tG/o04rODUIlMnkW/g9TWt50S6CkX6sCvR+fCS7n7YsLrknnlhj7qMIIEKCiuE+b/7lbfQUe2HepnFEfxLypmw==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@nguniversal/builders/-/builders-13.1.1.tgz", + "integrity": "sha512-R73GKHr7KeTIBE/kSudhsN0V1gx+TrnM28RHdzw3eHCz2Q3msGpgdt/79+2EjLcvWjoVHOsD+aFIJ9+sx422yQ==", "dev": true, "requires": { "@angular-devkit/architect": "^0.1303.0", - "@angular-devkit/core": "^13.3.0", - "@nguniversal/common": "13.1.0", + "@angular-devkit/core": "^13.3.4", + "@nguniversal/common": "13.1.1", "browser-sync": "^2.26.7", "express": "^4.17.1", "guess-parser": "^0.4.12", @@ -19474,14 +19385,28 @@ "hdr-histogram-percentiles-obj": "^3.0.0", "nice-napi": "^1.0.2" } + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true } } }, "@nguniversal/common": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@nguniversal/common/-/common-13.1.0.tgz", - "integrity": "sha512-bmRwJoEM5LfA4kDRkuVvoOv6NFAOgn2WpolwQlnLNfGTKL/fyNw/JyIvdEphQdjhTTRadehY8MYbr+X97+hyLw==", - "dev": true, + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@nguniversal/common/-/common-13.1.1.tgz", + "integrity": "sha512-DoAPA7+kUz+qMgCTUtRPFcMGY0zz8OSkOTZnxqO5sUYntD6mCEQImHU0WF4ud88j71o0Hv+AISJD1evAAANCdw==", "requires": { "critters": "0.0.16", "jsdom": "19.0.0", @@ -19491,74 +19416,22 @@ "@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==", - "dev": true + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==" }, "acorn": { "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "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==", - "dev": 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==", - "dev": 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==", - "dev": true - }, - "critters": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", - "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "css-select": "^4.2.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "postcss": "^8.3.7", - "pretty-bytes": "^5.3.0" - } + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==" }, "cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", - "dev": true + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" }, "data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", - "dev": true, "requires": { "abab": "^2.0.6", "whatwg-mimetype": "^3.0.0", @@ -19569,7 +19442,6 @@ "version": "11.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dev": true, "requires": { "tr46": "^3.0.0", "webidl-conversions": "^7.0.0" @@ -19581,7 +19453,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "dev": true, "requires": { "webidl-conversions": "^7.0.0" } @@ -19590,7 +19461,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, "requires": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -19602,31 +19472,22 @@ "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" }, "form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, "html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, "requires": { "whatwg-encoding": "^2.0.0" } @@ -19635,7 +19496,6 @@ "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==", - "dev": true, "requires": { "@tootallnate/once": "2", "agent-base": "6", @@ -19646,7 +19506,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" } @@ -19655,7 +19514,6 @@ "version": "19.0.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==", - "dev": true, "requires": { "abab": "^2.0.5", "acorn": "^8.5.0", @@ -19690,23 +19548,12 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "optional": 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==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, "tr46": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "dev": true, "requires": { "punycode": "^2.1.1" } @@ -19715,7 +19562,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", - "dev": true, "requires": { "xml-name-validator": "^4.0.0" } @@ -19723,14 +19569,12 @@ "webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" }, "whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, "requires": { "iconv-lite": "0.6.3" } @@ -19738,53 +19582,37 @@ "whatwg-mimetype": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==" }, "whatwg-url": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", - "dev": true, "requires": { "tr46": "^3.0.0", "webidl-conversions": "^7.0.0" } }, "ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", - "dev": true, + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz", + "integrity": "sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==", "requires": {} }, "xml-name-validator": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==" } } }, "@nguniversal/express-engine": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/@nguniversal/express-engine/-/express-engine-12.1.3.tgz", - "integrity": "sha512-iu/7Al3NjwdwJw3ALyW9n7MzAQ75/Ca/cd2xWi7rzgvev2pgakZfvn2/EbB8oFI6xOSvA2irA29BeobkR4crlw==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@nguniversal/express-engine/-/express-engine-13.1.1.tgz", + "integrity": "sha512-NdiBP0IRbPrNYEMLy3a6os2mNgRNE84tsMn+mV2uF4wv1JNs3YyoXcucWvhgHdODbDtc6z4CGn8t/6KagRqmvA==", "requires": { - "@nguniversal/common": "12.1.3", + "@nguniversal/common": "13.1.1", "tslib": "^2.3.0" - }, - "dependencies": { - "@nguniversal/common": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/@nguniversal/common/-/common-12.1.3.tgz", - "integrity": "sha512-0fP8ThHx3vePJxTdic5cz9hvWnqB2Uvx2uf1JQ68zto8ZXrasOeB8kT+ujhuUIyH8AR98BYMudMXCVvq+UbkeA==", - "requires": { - "critters": "0.0.12", - "jsdom": "16.6.0", - "tslib": "^2.3.0" - } - } } }, "@nodelib/fs.scandir": { @@ -19954,6 +19782,21 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "optional": true + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "optional": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true } } }, @@ -20020,9 +19863,9 @@ "devOptional": true }, "@socket.io/component-emitter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", - "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==", + "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==", "dev": true }, "@tootallnate/once": { @@ -20135,15 +19978,15 @@ } }, "@types/jasmine": { - "version": "3.6.9", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.9.tgz", - "integrity": "sha512-B53NIwMj/AO0O+xfSWLYmKB0Mo6TYxfv2Mk8/c1T2w/e38t55iaPR6p7pHXTTtqfTmevPK3i8T1YweYFTZlxDw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.0.3.tgz", + "integrity": "sha512-Opp1LvvEuZdk8fSSvchK2mZwhVrsNT0JgJE9Di6MjnaIpmEXM8TLCPPrVtNTYh8+5MPdY8j9bAHMu2SSfwpZJg==", "dev": true }, "@types/jasminewd2": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz", - "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.10.tgz", + "integrity": "sha512-J7mDz7ovjwjc+Y9rR9rY53hFWKATcIkrr9DwQWmOas4/pnIPJTXawnzjwpHm3RSxz/e3ZVUvQ7cRbd5UQLo10g==", "dev": true, "requires": { "@types/jasmine": "*" @@ -20794,15 +20637,17 @@ "optional": true }, "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "requires": { + "lodash": "^4.17.14" + } }, "async-each-series": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", - "integrity": "sha1-dhfBkXQB/Yykooqtzj266Yr+tDI=", + "integrity": "sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==", "dev": true }, "asynckit": { @@ -20873,34 +20718,14 @@ } }, "babel-loader": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", - "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", "requires": { "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", + "loader-utils": "^2.0.0", "make-dir": "^3.1.0", "schema-utils": "^2.6.5" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } } }, "babel-plugin-dynamic-import-node": { @@ -20976,12 +20801,6 @@ "@babel/helper-define-polyfill-provider": "^0.3.1" } }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -21112,7 +20931,7 @@ "bonjour": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", "requires": { "array-flatten": "^2.1.0", "deep-equal": "^1.0.1", @@ -21210,13 +21029,13 @@ } }, "browser-sync": { - "version": "2.27.9", - "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.9.tgz", - "integrity": "sha512-3zBtggcaZIeU9so4ja9yxk7/CZu9B3DOL6zkxFpzHCHsQmkGBPVXg61jItbeoa+WXgNLnr1sYES/2yQwyEZ2+w==", + "version": "2.27.10", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.10.tgz", + "integrity": "sha512-xKm+6KJmJu6RuMWWbFkKwOCSqQOxYe3nOrFkKI5Tr/ZzjPxyU3pFShKK3tWnazBo/3lYQzN7fzjixG8fwJh1Xw==", "dev": true, "requires": { - "browser-sync-client": "^2.27.9", - "browser-sync-ui": "^2.27.9", + "browser-sync-client": "^2.27.10", + "browser-sync-ui": "^2.27.10", "bs-recipes": "1.3.4", "bs-snippet-injector": "^2.0.1", "chokidar": "^3.5.1", @@ -21233,7 +21052,7 @@ "localtunnel": "^2.0.1", "micromatch": "^4.0.2", "opn": "5.3.0", - "portscanner": "2.1.1", + "portscanner": "2.2.0", "qs": "6.2.3", "raw-body": "^2.3.2", "resp-modifier": "6.0.2", @@ -21343,15 +21162,16 @@ } }, "browser-sync-client": { - "version": "2.27.9", - "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.9.tgz", - "integrity": "sha512-FHW8kydp7FXo6jnX3gXJCpHAHtWNLK0nx839nnK+boMfMI1n4KZd0+DmTxHBsHsF3OHud4V4jwoN8U5HExMIdQ==", + "version": "2.27.10", + "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.10.tgz", + "integrity": "sha512-KCFKA1YDj6cNul0VsA28apohtBsdk5Wv8T82ClOZPZMZWxPj4Ny5AUbrj9UlAb/k6pdxE5HABrWDhP9+cjt4HQ==", "dev": true, "requires": { "etag": "1.8.1", "fresh": "0.5.2", "mitt": "^1.1.3", - "rxjs": "^5.5.6" + "rxjs": "^5.5.6", + "typescript": "^4.6.2" }, "dependencies": { "rxjs": { @@ -21372,9 +21192,9 @@ } }, "browser-sync-ui": { - "version": "2.27.9", - "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.9.tgz", - "integrity": "sha512-rsduR2bRIwFvM8CX6iY/Nu5aWub0WB9zfSYg9Le/RV5N5DEyxJYey0VxdfWCnzDOoelassTDzYQo+r0iJno3qw==", + "version": "2.27.10", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.10.tgz", + "integrity": "sha512-elbJILq4Uo6OQv6gsvS3Y9vRAJlWu+h8j0JDkF0X/ua+3S6SVbbiWnZc8sNOFlG7yvVGIwBED3eaYQ0iBo1Dtw==", "dev": true, "requires": { "async-each-series": "0.1.1", @@ -21926,9 +21746,9 @@ "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" }, "clipboard": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.10.tgz", - "integrity": "sha512-cz3m2YVwFz95qSEbCDi2fzLN/epEN9zXBvfgAoGkvGOJZATMl9gtTDVOtBYkx2ODUJl2kvmud7n32sV2BpYR4g==", + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", + "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==", "requires": { "good-listener": "^1.2.2", "select": "^1.1.2", @@ -21961,9 +21781,9 @@ } }, "codelyzer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.1.tgz", - "integrity": "sha512-cOyGQgMdhnRYtW2xrJUNrNYDjEgwQ+BrE2y93Bwz3h4DJ6vJRLfupemU5N3pbYsUlBHJf0u1j1UGk+NLW4d97g==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.2.tgz", + "integrity": "sha512-v3+E0Ucu2xWJMOJ2fA/q9pDT/hlxHftHGPUay1/1cTgyPV5JTHFdO9hqo837Sx2s9vKBMTt5gO+lhF95PO6J+g==", "dev": true, "requires": { "@angular/compiler": "9.0.0", @@ -21992,6 +21812,15 @@ "integrity": "sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==", "dev": true }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -22104,7 +21933,7 @@ "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, "component-emitter": { "version": "1.3.0", @@ -22137,7 +21966,7 @@ "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" }, "debug": { "version": "2.6.9", @@ -22419,12 +22248,12 @@ } }, "critters": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.12.tgz", - "integrity": "sha512-ujxKtKc/mWpjrOKeaACTaQ1aP0O31M0ZPWhfl85jZF1smPU4Ivb9va5Ox2poif4zVJQQo0LCFlzGtEZAsCAPcw==", + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", "requires": { "chalk": "^4.1.0", - "css-select": "^4.1.3", + "css-select": "^4.2.0", "parse5": "^6.0.1", "parse5-htmlparser2-tree-adapter": "^6.0.1", "postcss": "^8.3.7", @@ -22617,7 +22446,8 @@ "cssom": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true }, "cssstyle": { "version": "2.3.0", @@ -22641,9 +22471,9 @@ "devOptional": true }, "cypress": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.5.2.tgz", - "integrity": "sha512-gYiQYvJozMzDOriUV1rCt6CeRM/pRK4nhwGJj3nJQyX2BoUdTCVwp30xDMKc771HiNVhBtgj5o5/iBdVDVXQUg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.6.1.tgz", + "integrity": "sha512-ECzmV7pJSkk+NuAhEw6C3D+RIRATkSb2VAHXDY6qGZbca/F9mv5pPsj2LO6Ty6oIFVBTrwCyL9agl28MtJMe2g==", "optional": true, "requires": { "@cypress/request": "^2.88.10", @@ -22678,7 +22508,7 @@ "listr2": "^3.8.3", "lodash": "^4.17.21", "log-symbols": "^4.0.0", - "minimist": "^1.2.5", + "minimist": "^1.2.6", "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", "proxy-from-env": "1.0.0", @@ -22867,6 +22697,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, "requires": { "abab": "^2.0.3", "whatwg-mimetype": "^2.3.0", @@ -22975,9 +22806,9 @@ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" }, "del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", "requires": { "globby": "^11.0.1", "graceful-fs": "^4.2.4", @@ -23191,6 +23022,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, "requires": { "webidl-conversions": "^5.0.0" }, @@ -23198,7 +23030,8 @@ "webidl-conversions": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true } } }, @@ -23395,20 +23228,16 @@ } }, "engine.io-client": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz", - "integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.2.tgz", + "integrity": "sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==", "dev": true, "requires": { - "@socket.io/component-emitter": "~3.0.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", - "engine.io-parser": "~5.0.0", - "has-cors": "1.1.0", - "parseqs": "0.0.6", - "parseuri": "0.0.6", + "engine.io-parser": "~5.0.3", "ws": "~8.2.3", - "xmlhttprequest-ssl": "~2.0.0", - "yeast": "0.1.2" + "xmlhttprequest-ssl": "~2.0.0" }, "dependencies": { "ws": { @@ -24622,12 +24451,6 @@ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -24727,6 +24550,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, "requires": { "whatwg-encoding": "^1.0.5" } @@ -25002,14 +24826,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, - "rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", - "requires": { - "tslib": "~2.1.0" - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -25017,11 +24833,6 @@ "requires": { "has-flag": "^4.0.0" } - }, - "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" } } }, @@ -25372,15 +25183,15 @@ } }, "jasmine-core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz", - "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.1.1.tgz", + "integrity": "sha512-lmUfT5XcK9KKvt3lLYzn93hc4MGzlUBowExFVgzbSW0ZCrdeyS574dfsyfRhxbg81Wj4gk+RxUiTnj7KBfDA1g==", "dev": true }, "jasmine-spec-reporter": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz", - "integrity": "sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-7.0.0.tgz", + "integrity": "sha512-OtC7JRasiTcjsaCBPtMO0Tl8glCejM4J4/dNuOJdA8lBjz4PmWjYQ6pzb0uzpBNAWJMDudYuj9OdXJWqM2QTJg==", "dev": true, "requires": { "colors": "1.4.0" @@ -25448,6 +25259,7 @@ "version": "16.6.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz", "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==", + "dev": true, "requires": { "abab": "^2.0.5", "acorn": "^8.2.4", @@ -25481,12 +25293,14 @@ "acorn": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==" + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true }, "escodegen": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, "requires": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -25498,12 +25312,14 @@ "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true }, "form-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -25514,6 +25330,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "optional": true } } @@ -25757,14 +25574,6 @@ "dev": true, "requires": { "jasmine-core": "^4.1.0" - }, - "dependencies": { - "jasmine-core": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.1.0.tgz", - "integrity": "sha512-8E8BiffCL8sBwK1zU9cbavLe8xpJAgOduSJ6N8PJVv8VosQ/nxVTuXj2kUeHxTlZBVvh24G19ga7xdiaxlceKg==", - "dev": true - } } }, "karma-jasmine-html-reporter": { @@ -25936,6 +25745,21 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "optional": true }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "optional": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -26296,9 +26120,9 @@ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "memfs": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", - "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.3.tgz", + "integrity": "sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==", "requires": { "fs-monkey": "1.0.3" } @@ -27435,18 +27259,6 @@ "parse5": "^6.0.1" } }, - "parseqs": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", - "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==", - "dev": true - }, - "parseuri": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", - "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==", - "dev": true - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -27576,14 +27388,6 @@ "mkdirp": "^0.5.5" }, "dependencies": { - "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "requires": { - "lodash": "^4.17.14" - } - }, "debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -27595,12 +27399,12 @@ } }, "portscanner": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.1.1.tgz", - "integrity": "sha1-6rtAnk3iSVD1oqUW01rnaTQ/u5Y=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", + "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", "dev": true, "requires": { - "async": "1.5.2", + "async": "^2.6.0", "is-number-like": "^1.0.3" } }, @@ -28425,18 +28229,11 @@ "dev": true }, "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } + "tslib": "^2.1.0" } }, "safe-buffer": { @@ -28879,26 +28676,24 @@ "devOptional": true }, "socket.io-client": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz", - "integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.1.tgz", + "integrity": "sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA==", "dev": true, "requires": { - "@socket.io/component-emitter": "~3.0.0", - "backo2": "~1.0.2", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", - "engine.io-client": "~6.1.1", - "parseuri": "0.0.6", - "socket.io-parser": "~4.1.1" + "engine.io-client": "~6.2.1", + "socket.io-parser": "~4.2.0" }, "dependencies": { "socket.io-parser": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz", - "integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.0.tgz", + "integrity": "sha512-tLfmEwcEwnlQTxFB7jibL/q2+q8dlVQzj4JdRLJ/W/G1+Fu9VSxCx1Lo+n1HvXxKnM//dUuD0xgiA7tQf57Vng==", "dev": true, "requires": { - "@socket.io/component-emitter": "~3.0.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" } } @@ -29519,6 +29314,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, "requires": { "punycode": "^2.1.1" } @@ -29584,9 +29380,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "tslint": { "version": "6.1.3", @@ -29699,9 +29495,9 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==" + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==" }, "ua-parser-js": { "version": "1.0.2", @@ -29957,6 +29753,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, "requires": { "xml-name-validator": "^3.0.0" } @@ -29972,6 +29769,23 @@ "lodash": "^4.17.21", "minimist": "^1.2.5", "rxjs": "^6.6.3" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "optional": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + } } }, "watchpack": { @@ -30002,7 +29816,8 @@ "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true }, "webpack": { "version": "5.70.0", @@ -30235,9 +30050,9 @@ } }, "ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz", + "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==", "requires": {} } } @@ -30283,6 +30098,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, "requires": { "iconv-lite": "0.4.24" } @@ -30290,12 +30106,14 @@ "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==" + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true }, "whatwg-url": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, "requires": { "lodash": "^4.7.0", "tr46": "^2.1.0", @@ -30406,7 +30224,8 @@ "ws": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true }, "xhr2": { "version": "0.2.0", @@ -30416,7 +30235,8 @@ "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==" + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true }, "xmlchars": { "version": "2.2.0", @@ -30543,12 +30363,6 @@ "fd-slicer": "~1.1.0" } }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -30561,11 +30375,11 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" }, "zone.js": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", - "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.5.tgz", + "integrity": "sha512-D1/7VxEuQ7xk6z/kAROe4SUbd9CzxY4zOwVGnGHerd/SgLIVU5f4esDzQUsOCeArn933BZfWMKydH7l7dPEp0g==", "requires": { - "tslib": "^2.0.0" + "tslib": "^2.3.0" } }, "zrender": { diff --git a/frontend/package.json b/frontend/package.json index 9a6445b46..f20f498df 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -61,26 +61,26 @@ "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" }, "dependencies": { - "@angular-devkit/build-angular": "^13.3.4", - "@angular/animations": "~13.3.5", - "@angular/cli": "~13.3.4", - "@angular/common": "~13.3.5", - "@angular/compiler": "~13.3.5", - "@angular/core": "~13.3.5", - "@angular/forms": "~13.3.5", - "@angular/localize": "^13.3.5", - "@angular/platform-browser": "~13.3.5", - "@angular/platform-browser-dynamic": "~13.3.5", - "@angular/platform-server": "~13.3.5", - "@angular/router": "~13.3.5", - "@fortawesome/angular-fontawesome": "0.10.1", - "@fortawesome/fontawesome-common-types": "0.3.0", - "@fortawesome/fontawesome-svg-core": "1.3.0", - "@fortawesome/free-solid-svg-icons": "6.0.0", + "@angular-devkit/build-angular": "~13.3.7", + "@angular/animations": "~13.3.10", + "@angular/cli": "~13.3.7", + "@angular/common": "~13.3.10", + "@angular/compiler": "~13.3.10", + "@angular/core": "~13.3.10", + "@angular/forms": "~13.3.10", + "@angular/localize": "~13.3.10", + "@angular/platform-browser": "~13.3.10", + "@angular/platform-browser-dynamic": "~13.3.10", + "@angular/platform-server": "~13.3.10", + "@angular/router": "~13.3.10", + "@fortawesome/angular-fontawesome": "~0.10.2", + "@fortawesome/fontawesome-common-types": "~6.1.1", + "@fortawesome/fontawesome-svg-core": "~6.1.1", + "@fortawesome/free-solid-svg-icons": "~6.1.1", "@juggle/resize-observer": "^3.3.1", "@mempool/mempool.js": "2.3.0", "@ng-bootstrap/ng-bootstrap": "^11.0.0", - "@nguniversal/express-engine": "12.1.3", + "@nguniversal/express-engine": "~13.1.1", "@types/qrcode": "~1.4.2", "bootstrap": "~4.5.0", "browserify": "^17.0.0", @@ -93,24 +93,24 @@ "ngx-echarts": "8.0.1", "ngx-infinite-scroll": "^10.0.1", "qrcode": "1.5.0", - "rxjs": "^6.6.7", + "rxjs": "~7.5.5", "tinyify": "^3.0.0", "tlite": "^0.1.9", - "tslib": "^2.2.0", - "zone.js": "~0.11.4" + "tslib": "~2.4.0", + "zone.js": "~0.11.5" }, "devDependencies": { - "@angular/compiler-cli": "~13.3.5", - "@angular/language-service": "~13.3.5", - "@nguniversal/builders": "~13.1.0", + "@angular/compiler-cli": "~13.3.10", + "@angular/language-service": "~13.3.10", + "@nguniversal/builders": "~13.1.1", "@types/express": "^4.17.0", - "@types/jasmine": "~3.6.0", - "@types/jasminewd2": "~2.0.3", + "@types/jasmine": "~4.0.3", + "@types/jasminewd2": "~2.0.10", "@types/node": "^12.11.1", - "codelyzer": "^6.0.1", + "codelyzer": "~6.0.2", "http-proxy-middleware": "^1.0.5", - "jasmine-core": "~3.6.0", - "jasmine-spec-reporter": "~5.0.0", + "jasmine-core": "~4.1.0", + "jasmine-spec-reporter": "~7.0.0", "karma": "~6.3.19", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.0.3", @@ -118,11 +118,11 @@ "karma-jasmine-html-reporter": "^1.5.0", "ts-node": "~8.3.0", "tslint": "~6.1.0", - "typescript": "~4.4.4" + "typescript": "~4.6.4" }, "optionalDependencies": { "@cypress/schematic": "^1.3.0", - "cypress": "^9.5.2", + "cypress": "^9.6.1", "cypress-fail-on-console-error": "^2.1.3", "cypress-wait-until": "^1.7.1", "mock-socket": "^9.0.3", diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 2eaf7e277..6951accb2 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -1,154 +1,232 @@ import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; +import { Routes, RouterModule, PreloadAllModules } from '@angular/router'; import { StartComponent } from './components/start/start.component'; import { TransactionComponent } from './components/transaction/transaction.component'; import { BlockComponent } from './components/block/block.component'; import { AddressComponent } from './components/address/address.component'; import { MasterPageComponent } from './components/master-page/master-page.component'; import { AboutComponent } from './components/about/about.component'; -import { TelevisionComponent } from './components/television/television.component'; -import { StatisticsComponent } from './components/statistics/statistics.component'; -import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component'; -import { AssetComponent } from './components/asset/asset.component'; -import { AssetsNavComponent } from './components/assets/assets-nav/assets-nav.component'; import { StatusViewComponent } from './components/status-view/status-view.component'; -import { DashboardComponent } from './dashboard/dashboard.component'; -import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component'; import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component'; import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component'; import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component'; import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component'; import { SponsorComponent } from './components/sponsor/sponsor.component'; -import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component'; import { PushTransactionComponent } from './components/push-transaction/push-transaction.component'; -import { PoolRankingComponent } from './components/pool-ranking/pool-ranking.component'; +import { BlocksList } from './components/blocks-list/blocks-list.component'; +import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component'; import { AssetGroupComponent } from './components/assets/asset-group/asset-group.component'; import { AssetsFeaturedComponent } from './components/assets/assets-featured/assets-featured.component'; import { AssetsComponent } from './components/assets/assets.component'; -import { PoolComponent } from './components/pool/pool.component'; -import { MiningDashboardComponent } from './components/mining-dashboard/mining-dashboard.component'; -import { HashrateChartComponent } from './components/hashrate-chart/hashrate-chart.component'; -import { HashrateChartPoolsComponent } from './components/hashrates-chart-pools/hashrate-chart-pools.component'; -import { MiningStartComponent } from './components/mining-start/mining-start.component'; -import { GraphsComponent } from './components/graphs/graphs.component'; -import { BlocksList } from './components/blocks-list/blocks-list.component'; -import { BlockFeesGraphComponent } from './components/block-fees-graph/block-fees-graph.component'; -import { BlockRewardsGraphComponent } from './components/block-rewards-graph/block-rewards-graph.component'; -import { BlockFeeRatesGraphComponent } from './components/block-fee-rates-graph/block-fee-rates-graph.component'; -import { BlockSizesWeightsGraphComponent } from './components/block-sizes-weights-graph/block-sizes-weights-graph.component'; +import { AssetComponent } from './components/asset/asset.component'; +import { AssetsNavComponent } from './components/assets/assets-nav/assets-nav.component'; let routes: Routes = [ - { - path: '', - component: MasterPageComponent, + { + path: 'testnet', children: [ { - path: 'tx/push', - component: PushTransactionComponent, + path: '', + pathMatch: 'full', + loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) }, { path: '', - component: StartComponent, + component: MasterPageComponent, children: [ { - path: '', - component: DashboardComponent, + path: 'mining/blocks', + redirectTo: 'blocks', + pathMatch: 'full' }, { - path: 'tx/:id', - component: TransactionComponent + path: 'tx/push', + component: PushTransactionComponent, }, { - path: 'block/:id', - component: BlockComponent + path: 'about', + component: AboutComponent, }, - { - path: 'mempool-block/:id', - component: MempoolBlockComponent - }, - { - path: 'mining', - component: MiningDashboardComponent, - }, - ], - }, - { - path: 'blocks', - component: LatestBlocksComponent, - }, - { - path: 'mining', - component: MiningStartComponent, - children: [ { path: 'blocks', component: BlocksList, }, { - path: 'pool', + path: 'terms-of-service', + component: TermsOfServiceComponent + }, + { + path: 'privacy-policy', + component: PrivacyPolicyComponent + }, + { + path: 'trademark-policy', + component: TrademarkPolicyComponent + }, + { + path: 'address/:id', + children: [], + component: AddressComponent + }, + { + path: 'tx', + component: StartComponent, children: [ { - path: ':slug', - component: PoolComponent, + path: ':id', + component: TransactionComponent }, - ] - }, - ] - }, - { - path: 'graphs', - component: GraphsComponent, - children: [ - { - path: '', - pathMatch: 'full', - redirectTo: 'mempool', + ], }, { - path: 'mempool', - component: StatisticsComponent, + path: 'block', + component: StartComponent, + children: [ + { + path: ':id', + component: BlockComponent + }, + ], }, { - path: 'mining/hashrate-difficulty', - component: HashrateChartComponent, + path: 'docs', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) }, { - path: 'mining/pools-dominance', - component: HashrateChartPoolsComponent, - }, - { - path: 'mining/pools', - component: PoolRankingComponent, - }, - { - path: 'mining/block-fees', - component: BlockFeesGraphComponent, - }, - { - path: 'mining/block-rewards', - component: BlockRewardsGraphComponent, - }, - { - path: 'mining/block-fee-rates', - component: BlockFeeRatesGraphComponent, - }, - { - path: 'mining/block-sizes-weights', - component: BlockSizesWeightsGraphComponent, + path: 'api', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) }, ], }, + { + path: 'status', + component: StatusViewComponent + }, + { + path: '', + loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + }, + { + path: '**', + redirectTo: '/testnet' + }, + ] + }, + { + path: 'signet', + children: [ + { + path: 'mining/blocks', + redirectTo: 'blocks', + pathMatch: 'full' + }, + { + path: '', + pathMatch: 'full', + loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + }, + { + path: '', + component: MasterPageComponent, + children: [ + { + path: 'tx/push', + component: PushTransactionComponent, + }, + { + path: 'about', + component: AboutComponent, + }, + { + path: 'blocks', + component: BlocksList, + }, + { + path: 'terms-of-service', + component: TermsOfServiceComponent + }, + { + path: 'privacy-policy', + component: PrivacyPolicyComponent + }, + { + path: 'trademark-policy', + component: TrademarkPolicyComponent + }, + { + path: 'address/:id', + children: [], + component: AddressComponent + }, + { + path: 'tx', + component: StartComponent, + children: [ + { + path: ':id', + component: TransactionComponent + }, + ], + }, + { + path: 'block', + component: StartComponent, + children: [ + { + path: ':id', + component: BlockComponent + }, + ], + }, + { + path: 'docs', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) + }, + { + path: 'api', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) + }, + ], + }, + { + path: 'status', + component: StatusViewComponent + }, + { + path: '', + loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + }, + { + path: '**', + redirectTo: '/signet' + }, + ] + }, + { + path: '', + pathMatch: 'full', + loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + }, + { + path: '', + component: MasterPageComponent, + children: [ + { + path: 'mining/blocks', + redirectTo: 'blocks', + pathMatch: 'full' + }, + { + path: 'tx/push', + component: PushTransactionComponent, + }, { path: 'about', component: AboutComponent, }, { - path: 'docs', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'api', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) + path: 'blocks', + component: BlocksList, }, { path: 'terms-of-service', @@ -167,276 +245,36 @@ let routes: Routes = [ children: [], component: AddressComponent }, + { + path: 'tx', + component: StartComponent, + children: [ + { + path: ':id', + component: TransactionComponent + }, + ], + }, + { + path: 'block', + component: StartComponent, + children: [ + { + path: ':id', + component: BlockComponent + }, + ], + }, + { + path: 'docs', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) + }, + { + path: 'api', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) + }, ], }, - { - path: 'testnet', - children: [ - { - path: '', - component: MasterPageComponent, - children: [ - { - path: 'tx/push', - component: PushTransactionComponent, - }, - { - path: '', - component: StartComponent, - children: [ - { - path: '', - component: DashboardComponent - }, - { - path: 'tx/:id', - component: TransactionComponent - }, - { - path: 'block/:id', - component: BlockComponent - }, - { - path: 'mempool-block/:id', - component: MempoolBlockComponent - }, - { - path: 'mining', - component: MiningDashboardComponent, - }, - ], - }, - { - path: 'blocks', - component: LatestBlocksComponent, - }, - { - path: 'mining', - component: MiningStartComponent, - children: [ - { - path: 'blocks', - component: BlocksList, - }, - { - path: 'pool', - children: [ - { - path: ':slug', - component: PoolComponent, - }, - ] - }, - ] - }, - { - path: 'graphs', - component: GraphsComponent, - children: [ - { - path: '', - pathMatch: 'full', - redirectTo: 'mempool', - }, - { - path: 'mempool', - component: StatisticsComponent, - }, - { - path: 'mining/hashrate-difficulty', - component: HashrateChartComponent, - }, - { - path: 'mining/pools-dominance', - component: HashrateChartPoolsComponent, - }, - { - path: 'mining/pools', - component: PoolRankingComponent, - }, - { - path: 'mining/block-fees', - component: BlockFeesGraphComponent, - }, - { - path: 'mining/block-rewards', - component: BlockRewardsGraphComponent, - }, - { - path: 'mining/block-fee-rates', - component: BlockFeeRatesGraphComponent, - }, - { - path: 'mining/block-sizes-weights', - component: BlockSizesWeightsGraphComponent, - }, - ] - }, - { - path: 'address/:id', - children: [], - component: AddressComponent - }, - { - path: 'docs', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'api', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - ], - }, - { - path: 'tv', - component: TelevisionComponent - }, - { - path: 'status', - component: StatusViewComponent - }, - { - path: '**', - redirectTo: '/testnet' - }, - ] - }, - { - path: 'signet', - children: [ - { - path: '', - component: MasterPageComponent, - children: [ - { - path: 'tx/push', - component: PushTransactionComponent, - }, - { - path: '', - component: StartComponent, - children: [ - { - path: '', - component: DashboardComponent - }, - { - path: 'tx/:id', - component: TransactionComponent - }, - { - path: 'block/:id', - component: BlockComponent - }, - { - path: 'mempool-block/:id', - component: MempoolBlockComponent - }, - { - path: 'mining', - component: MiningDashboardComponent, - }, - ], - }, - { - path: 'blocks', - component: LatestBlocksComponent, - }, - { - path: 'mining', - component: MiningStartComponent, - children: [ - { - path: 'blocks', - component: BlocksList, - }, - { - path: 'pool', - children: [ - { - path: ':slug', - component: PoolComponent, - }, - ] - }, - ] - }, - { - path: 'graphs', - component: GraphsComponent, - children: [ - { - path: '', - pathMatch: 'full', - redirectTo: 'mempool', - }, - { - path: 'mempool', - component: StatisticsComponent, - }, - { - path: 'mining/hashrate-difficulty', - component: HashrateChartComponent, - }, - { - path: 'mining/pools-dominance', - component: HashrateChartPoolsComponent, - }, - { - path: 'mining/pools', - component: PoolRankingComponent, - }, - { - path: 'mining/block-fees', - component: BlockFeesGraphComponent, - }, - { - path: 'mining/block-rewards', - component: BlockRewardsGraphComponent, - }, - { - path: 'mining/block-fee-rates', - component: BlockFeeRatesGraphComponent, - }, - { - path: 'mining/block-sizes-weights', - component: BlockSizesWeightsGraphComponent, - }, - ] - }, - { - path: 'address/:id', - children: [], - component: AddressComponent - }, - { - path: 'docs', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'api', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - ], - }, - { - path: 'tv', - component: TelevisionComponent - }, - { - path: 'status', - component: StatusViewComponent - }, - { - path: '**', - redirectTo: '/signet' - }, - ] - }, - { - path: 'tv', - component: TelevisionComponent, - }, { path: 'status', component: StatusViewComponent @@ -445,6 +283,10 @@ let routes: Routes = [ path: 'sponsor', component: SponsorComponent, }, + { + path: '', + loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + }, { path: '**', redirectTo: '' @@ -464,244 +306,233 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'bisq') { } if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { - routes = [{ - path: '', - component: LiquidMasterPageComponent, - children: [ - { - path: '', - component: StartComponent, - children: [ - { - path: '', - component: DashboardComponent - }, - { - path: 'tx/push', - component: PushTransactionComponent, - }, - { - path: 'tx/:id', - component: TransactionComponent - }, - { - path: 'block/:id', - component: BlockComponent - }, - { - path: 'mempool-block/:id', - component: MempoolBlockComponent - }, - ], - }, - { - path: 'blocks', - component: LatestBlocksComponent, - }, - { - path: 'graphs', - component: GraphsComponent, - children: [ - { - path: '', - pathMatch: 'full', - redirectTo: 'mempool', - }, - { - path: 'mempool', - component: StatisticsComponent, - } - ] - }, - { - path: 'address/:id', - component: AddressComponent - }, - { - path: 'assets', - component: AssetsNavComponent, - children: [ - { - path: 'featured', - component: AssetsFeaturedComponent, - }, - { - path: 'all', - component: AssetsComponent, - }, - { - path: 'asset/:id', - component: AssetComponent - }, - { - path: 'group/:id', - component: AssetGroupComponent - }, - { - path: '**', - redirectTo: 'featured' - } - ] - }, - { - path: 'docs', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'api', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'about', - component: AboutComponent, - }, - { - path: 'terms-of-service', - component: TermsOfServiceComponent - }, - { - path: 'privacy-policy', - component: PrivacyPolicyComponent - }, - { - path: 'trademark-policy', - component: TrademarkPolicyComponent - }, - ], - }, - { - path: 'testnet', - children: [ - { - path: '', - component: LiquidMasterPageComponent, - children: [ - { - path: '', - component: StartComponent, + routes = [ + { + path: 'testnet', + children: [ + { + path: '', + pathMatch: 'full', + loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + }, + { + path: '', + component: LiquidMasterPageComponent, + children: [ + { + path: 'tx/push', + component: PushTransactionComponent, + }, + { + path: 'about', + component: AboutComponent, + }, + { + path: 'blocks', + component: BlocksList, + }, + { + path: 'terms-of-service', + component: TermsOfServiceComponent + }, + { + path: 'privacy-policy', + component: PrivacyPolicyComponent + }, + { + path: 'trademark-policy', + component: TrademarkPolicyComponent + }, + { + path: 'address/:id', + children: [], + component: AddressComponent + }, + { + path: 'tx', + component: StartComponent, + children: [ + { + path: ':id', + component: TransactionComponent + }, + ], + }, + { + path: 'block', + component: StartComponent, + children: [ + { + path: ':id', + component: BlockComponent + }, + ], + }, + { + path: 'assets', + component: AssetsNavComponent, + children: [ + { + path: 'all', + component: AssetsComponent, + }, + { + path: 'asset/:id', + component: AssetComponent + }, + { + path: 'group/:id', + component: AssetGroupComponent + }, + { + path: '**', + redirectTo: 'all' + } + ] + }, + { + path: 'docs', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) + }, + { + path: 'api', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) + }, + ], + }, + { + path: 'status', + component: StatusViewComponent + }, + { + path: '', + loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + }, + { + path: '**', + redirectTo: '/signet' + }, + ] + }, + { + path: '', + pathMatch: 'full', + loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + }, + { + path: '', + component: LiquidMasterPageComponent, + children: [ + { + path: 'tx/push', + component: PushTransactionComponent, + }, + { + path: 'about', + component: AboutComponent, + }, + { + path: 'blocks', + component: BlocksList, + }, + { + path: 'terms-of-service', + component: TermsOfServiceComponent + }, + { + path: 'privacy-policy', + component: PrivacyPolicyComponent + }, + { + path: 'trademark-policy', + component: TrademarkPolicyComponent + }, + { + path: 'address/:id', + children: [], + component: AddressComponent + }, + { + path: 'tx', + component: StartComponent, + children: [ + { + path: ':id', + component: TransactionComponent + }, + ], + }, + { + path: 'block', + component: StartComponent, children: [ - { - path: '', - component: DashboardComponent - }, - { - path: 'tx/push', - component: PushTransactionComponent, - }, - { - path: 'tx/:id', - component: TransactionComponent - }, - { - path: 'block/:id', - component: BlockComponent - }, - { - path: 'mempool-block/:id', - component: MempoolBlockComponent - }, - ], - }, - { - path: 'blocks', - component: LatestBlocksComponent, - }, - { - path: 'graphs', - component: GraphsComponent, - children: [ - { - path: '', - pathMatch: 'full', - redirectTo: 'mempool', - }, - { - path: 'mempool', - component: StatisticsComponent, - } - ] - }, - { - path: 'address/:id', - component: AddressComponent - }, - { - path: 'assets', - component: AssetsNavComponent, - children: [ - { - path: 'all', - component: AssetsComponent, - }, - { - path: 'asset/:id', - component: AssetComponent - }, - { - path: 'group/:id', - component: AssetGroupComponent - }, - { - path: '**', - redirectTo: 'all' - } - ] - }, - { - path: 'docs', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'api', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'about', - component: AboutComponent, - }, - { - path: 'terms-of-service', - component: TermsOfServiceComponent - }, - { - path: 'privacy-policy', - component: PrivacyPolicyComponent - }, - { - path: 'trademark-policy', - component: TrademarkPolicyComponent - }, - ], - }, - { - path: 'tv', - component: TelevisionComponent - }, - { - path: 'status', - component: StatusViewComponent - }, - ] - }, - { - path: 'tv', - component: TelevisionComponent - }, - { - path: 'status', - component: StatusViewComponent - }, - { - path: '**', - redirectTo: '/testnet' - }]; + { + path: ':id', + component: BlockComponent + }, + ], + }, + { + path: 'assets', + component: AssetsNavComponent, + children: [ + { + path: 'featured', + component: AssetsFeaturedComponent, + }, + { + path: 'all', + component: AssetsComponent, + }, + { + path: 'asset/:id', + component: AssetComponent + }, + { + path: 'group/:id', + component: AssetGroupComponent + }, + { + path: '**', + redirectTo: 'featured' + } + ] + }, + { + path: 'docs', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) + }, + { + path: 'api', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) + }, + ], + }, + { + path: 'status', + component: StatusViewComponent + }, + { + path: 'sponsor', + component: SponsorComponent, + }, + { + path: '', + loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + }, + { + path: '**', + redirectTo: '' + }, + ]; } @NgModule({ imports: [RouterModule.forRoot(routes, { initialNavigation: 'enabled', scrollPositionRestoration: 'enabled', - anchorScrolling: 'enabled' + anchorScrolling: 'enabled', + preloadingStrategy: PreloadAllModules })], }) export class AppRoutingModule { } diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 336cfead2..8845f4255 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -2,142 +2,23 @@ import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-bro import { NgModule } from '@angular/core'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { InfiniteScrollModule } from 'ngx-infinite-scroll'; -import { NgxEchartsModule } from 'ngx-echarts'; - import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './components/app/app.component'; - -import { StartComponent } from './components/start/start.component'; import { ElectrsApiService } from './services/electrs-api.service'; -import { TransactionComponent } from './components/transaction/transaction.component'; -import { TransactionsListComponent } from './components/transactions-list/transactions-list.component'; import { StateService } from './services/state.service'; -import { BlockComponent } from './components/block/block.component'; -import { AddressComponent } from './components/address/address.component'; -import { SearchFormComponent } from './components/search-form/search-form.component'; -import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component'; import { WebsocketService } from './services/websocket.service'; -import { AddressLabelsComponent } from './components/address-labels/address-labels.component'; -import { MasterPageComponent } from './components/master-page/master-page.component'; -import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component'; -import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component'; -import { AboutComponent } from './components/about/about.component'; -import { TelevisionComponent } from './components/television/television.component'; -import { StatisticsComponent } from './components/statistics/statistics.component'; -import { FooterComponent } from './components/footer/footer.component'; import { AudioService } from './services/audio.service'; -import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component'; -import { FeeDistributionGraphComponent } from './components/fee-distribution-graph/fee-distribution-graph.component'; -import { IncomingTransactionsGraphComponent } from './components/incoming-transactions-graph/incoming-transactions-graph.component'; -import { TimeSpanComponent } from './components/time-span/time-span.component'; import { SeoService } from './services/seo.service'; -import { MempoolGraphComponent } from './components/mempool-graph/mempool-graph.component'; -import { PoolRankingComponent } from './components/pool-ranking/pool-ranking.component'; -import { PoolComponent } from './components/pool/pool.component'; -import { LbtcPegsGraphComponent } from './components/lbtc-pegs-graph/lbtc-pegs-graph.component'; -import { AssetComponent } from './components/asset/asset.component'; -import { AssetsComponent } from './components/assets/assets.component'; -import { AssetsNavComponent } from './components/assets/assets-nav/assets-nav.component'; -import { StatusViewComponent } from './components/status-view/status-view.component'; -import { MinerComponent } from './components/miner/miner.component'; import { SharedModule } from './shared/shared.module'; -import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap'; -import { FeesBoxComponent } from './components/fees-box/fees-box.component'; -import { DashboardComponent } from './dashboard/dashboard.component'; -import { DifficultyComponent } from './components/difficulty/difficulty.component'; -import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; -import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle, - faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, - faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload } from '@fortawesome/free-solid-svg-icons'; -import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component'; -import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component'; -import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component'; import { StorageService } from './services/storage.service'; import { HttpCacheInterceptor } from './services/http-cache.interceptor'; import { LanguageService } from './services/language.service'; -import { SponsorComponent } from './components/sponsor/sponsor.component'; -import { PushTransactionComponent } from './components/push-transaction/push-transaction.component'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; -import { AssetsFeaturedComponent } from './components/assets/assets-featured/assets-featured.component'; -import { AssetGroupComponent } from './components/assets/asset-group/asset-group.component'; -import { AssetCirculationComponent } from './components/asset-circulation/asset-circulation.component'; -import { MiningDashboardComponent } from './components/mining-dashboard/mining-dashboard.component'; -import { HashrateChartComponent } from './components/hashrate-chart/hashrate-chart.component'; -import { HashrateChartPoolsComponent } from './components/hashrates-chart-pools/hashrate-chart-pools.component'; -import { MiningStartComponent } from './components/mining-start/mining-start.component'; -import { AmountShortenerPipe } from './shared/pipes/amount-shortener.pipe'; import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-string.pipe'; -import { GraphsComponent } from './components/graphs/graphs.component'; -import { DifficultyAdjustmentsTable } from './components/difficulty-adjustments-table/difficulty-adjustments-table.components'; -import { BlocksList } from './components/blocks-list/blocks-list.component'; -import { RewardStatsComponent } from './components/reward-stats/reward-stats.component'; -import { DataCyDirective } from './data-cy.directive'; -import { BlockFeesGraphComponent } from './components/block-fees-graph/block-fees-graph.component'; -import { BlockRewardsGraphComponent } from './components/block-rewards-graph/block-rewards-graph.component'; -import { BlockFeeRatesGraphComponent } from './components/block-fee-rates-graph/block-fee-rates-graph.component'; -import { LoadingIndicatorComponent } from './components/loading-indicator/loading-indicator.component'; -import { IndexingProgressComponent } from './components/indexing-progress/indexing-progress.component'; -import { BlockSizesWeightsGraphComponent } from './components/block-sizes-weights-graph/block-sizes-weights-graph.component'; +import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe'; @NgModule({ declarations: [ AppComponent, - AboutComponent, - MasterPageComponent, - BisqMasterPageComponent, - LiquidMasterPageComponent, - TelevisionComponent, - StartComponent, - StatisticsComponent, - TransactionComponent, - BlockComponent, - TransactionsListComponent, - AddressComponent, - LatestBlocksComponent, - SearchFormComponent, - TimeSpanComponent, - AddressLabelsComponent, - FooterComponent, - MempoolBlockComponent, - FeeDistributionGraphComponent, - IncomingTransactionsGraphComponent, - MempoolGraphComponent, - PoolRankingComponent, - PoolComponent, - LbtcPegsGraphComponent, - AssetComponent, - AssetsComponent, - MinerComponent, - StatusViewComponent, - FeesBoxComponent, - DashboardComponent, - DifficultyComponent, - TermsOfServiceComponent, - PrivacyPolicyComponent, - TrademarkPolicyComponent, - SponsorComponent, - PushTransactionComponent, - AssetsNavComponent, - AssetsFeaturedComponent, - AssetGroupComponent, - AssetCirculationComponent, - MiningDashboardComponent, - HashrateChartComponent, - HashrateChartPoolsComponent, - MiningStartComponent, - AmountShortenerPipe, - GraphsComponent, - DifficultyAdjustmentsTable, - BlocksList, - DataCyDirective, - RewardStatsComponent, - BlockFeesGraphComponent, - BlockRewardsGraphComponent, - BlockFeeRatesGraphComponent, - LoadingIndicatorComponent, - IndexingProgressComponent, - BlockSizesWeightsGraphComponent ], imports: [ BrowserModule.withServerTransition({ appId: 'serverApp' }), @@ -145,14 +26,7 @@ import { BlockSizesWeightsGraphComponent } from './components/block-sizes-weight AppRoutingModule, HttpClientModule, BrowserAnimationsModule, - InfiniteScrollModule, - NgbTypeaheadModule, - NgbModule, - FontAwesomeModule, SharedModule, - NgxEchartsModule.forRoot({ - echarts: () => import('echarts') - }) ], providers: [ ElectrsApiService, @@ -163,45 +37,9 @@ import { BlockSizesWeightsGraphComponent } from './components/block-sizes-weight StorageService, LanguageService, ShortenStringPipe, + CapAddressPipe, { provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true } ], bootstrap: [AppComponent] }) -export class AppModule { - constructor(library: FaIconLibrary) { - library.addIcons(faInfoCircle); - library.addIcons(faChartArea); - library.addIcons(faTv); - library.addIcons(faTachometerAlt); - library.addIcons(faCubes); - library.addIcons(faHammer); - library.addIcons(faCogs); - library.addIcons(faThList); - library.addIcons(faList); - library.addIcons(faTachometerAlt); - library.addIcons(faDatabase); - library.addIcons(faSearch); - library.addIcons(faLink); - library.addIcons(faBolt); - library.addIcons(faTint); - library.addIcons(faFilter); - library.addIcons(faAngleDown); - library.addIcons(faAngleUp); - library.addIcons(faExchangeAlt); - library.addIcons(faAngleDoubleUp); - library.addIcons(faAngleDoubleDown); - library.addIcons(faChevronDown); - library.addIcons(faFileAlt); - library.addIcons(faRedoAlt); - library.addIcons(faArrowAltCircleRight); - library.addIcons(faExternalLinkAlt); - library.addIcons(faSortUp); - library.addIcons(faCaretUp); - library.addIcons(faCaretDown); - library.addIcons(faAngleRight); - library.addIcons(faAngleLeft); - library.addIcons(faBook); - library.addIcons(faListUl); - library.addIcons(faDownload); - } -} +export class AppModule { } diff --git a/frontend/src/app/bisq/bisq-block/bisq-block.component.html b/frontend/src/app/bisq/bisq-block/bisq-block.component.html index 36281157d..9cc2ad699 100644 --- a/frontend/src/app/bisq/bisq-block/bisq-block.component.html +++ b/frontend/src/app/bisq/bisq-block/bisq-block.component.html @@ -20,7 +20,7 @@ {{ block.hash | shortenString : 13 }} - Timestamp + Timestamp ‎{{ block.time | date:'yyyy-MM-dd HH:mm' }}
@@ -83,7 +83,7 @@ - Timestamp + Timestamp 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 index 83b93dc78..2d7df05e1 100644 --- 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 @@ -89,7 +89,7 @@
- + @@ -98,7 +98,7 @@
Latest Trades
- +
diff --git a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html index 2b3964caa..257281c7a 100644 --- a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html +++ b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html @@ -31,7 +31,7 @@ - + - + diff --git a/frontend/src/app/bisq/bisq.module.ts b/frontend/src/app/bisq/bisq.module.ts index 77b0a26e8..ebb9c3ee6 100644 --- a/frontend/src/app/bisq/bisq.module.ts +++ b/frontend/src/app/bisq/bisq.module.ts @@ -7,7 +7,6 @@ import { LightweightChartsComponent } from './lightweight-charts/lightweight-cha 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 { NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap'; import { BisqTransactionComponent } from './bisq-transaction/bisq-transaction.component'; import { BisqBlockComponent } from './bisq-block/bisq-block.component'; import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.component'; @@ -24,6 +23,7 @@ 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'; @NgModule({ declarations: [ @@ -46,9 +46,9 @@ import { BisqTradesComponent } from './bisq-trades/bisq-trades.component'; BisqMainDashboardComponent, ], imports: [ + CommonModule, BisqRoutingModule, SharedModule, - NgbPaginationModule, FontAwesomeModule, NgxBootstrapMultiselectModule, ], diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html index 27892626c..88e296c2d 100644 --- a/frontend/src/app/components/about/about.component.html +++ b/frontend/src/app/components/about/about.component.html @@ -20,9 +20,6 @@ - - - @@ -250,6 +247,10 @@ Marina + + + Schildbach + 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 f4eff6d78..34c82c851 100644 --- a/frontend/src/app/components/address-labels/address-labels.component.ts +++ b/frontend/src/app/components/address-labels/address-labels.component.ts @@ -118,7 +118,7 @@ export class AddressLabelsComponent implements OnInit { } const m = parseInt(opM.match(/[0-9]+/)[0], 10); - this.label = `multisig ${m} of ${n}`; + this.label = $localize`:@@address-label.multisig:Multisig ${m}:multisigM: of ${n}:multisigN:` } handleVout() { diff --git a/frontend/src/app/components/address/address.component.html b/frontend/src/app/components/address/address.component.html index fa9e14651..6111075e4 100644 --- a/frontend/src/app/components/address/address.component.html +++ b/frontend/src/app/components/address/address.component.html @@ -66,9 +66,14 @@
-
- -
+ + +
+
+
+
+
+
@@ -81,13 +86,6 @@
- -
-
-
-
-
-
@@ -155,3 +153,9 @@
+ + +
+ +
+
diff --git a/frontend/src/app/components/assets/asset-group/asset-group.component.html b/frontend/src/app/components/assets/asset-group/asset-group.component.html index df3f90abd..a4c9e9da0 100644 --- a/frontend/src/app/components/assets/asset-group/asset-group.component.html +++ b/frontend/src/app/components/assets/asset-group/asset-group.component.html @@ -1,11 +1,13 @@ -
+
-
-

{{ group.group.name }}

- -
Group of {{ group.group.assets.length | number }} assets
-
+ + +
+

{{ group.name }}

+
Group of {{ group.assets.length | number }} assets
+
+
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 index cacd38f39..b1735adee 100644 --- 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 @@ -19,7 +19,7 @@ Signet Testnet - + Bisq Liquid Liquid Testnet
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 f15356c2f..016c80936 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 @@ -2,41 +2,40 @@
- Block fee rates + Block Fee Rates -
-
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 62b155fb5..3a4d2b89d 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 @@ -62,7 +62,7 @@ export class BlockFeeRatesGraphComponent implements OnInit { } ngOnInit(): void { - this.seoService.setTitle($localize`:@@mining.block-fee-rates:Block Fee Rates`); + this.seoService.setTitle($localize`:@@ed8e33059967f554ff06b4f5b6049c465b92d9b3:Block Fee Rates`); this.miningWindowPreference = this.miningService.getDefaultTimespan('24h'); this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); @@ -76,7 +76,7 @@ export class BlockFeeRatesGraphComponent implements OnInit { this.isLoading = true; return this.apiService.getHistoricalBlockFeeRates$(timespan) .pipe( - tap((data: any) => { + tap((response) => { // Group by percentile const seriesData = { 'Min': [], @@ -87,15 +87,15 @@ export class BlockFeeRatesGraphComponent implements OnInit { '90th': [], 'Max': [] }; - for (const rate of data.blockFeeRates) { + for (const rate of response.body) { const timestamp = rate.timestamp * 1000; - seriesData['Min'].push([timestamp, rate.avg_fee_0, rate.avg_height]); - seriesData['10th'].push([timestamp, rate.avg_fee_10, rate.avg_height]); - seriesData['25th'].push([timestamp, rate.avg_fee_25, rate.avg_height]); - seriesData['Median'].push([timestamp, rate.avg_fee_50, rate.avg_height]); - seriesData['75th'].push([timestamp, rate.avg_fee_75, rate.avg_height]); - seriesData['90th'].push([timestamp, rate.avg_fee_90, rate.avg_height]); - seriesData['Max'].push([timestamp, rate.avg_fee_100, rate.avg_height]); + seriesData['Min'].push([timestamp, rate.avgFee_0, rate.avgHeight]); + seriesData['10th'].push([timestamp, rate.avgFee_10, rate.avgHeight]); + seriesData['25th'].push([timestamp, rate.avgFee_25, rate.avgHeight]); + seriesData['Median'].push([timestamp, rate.avgFee_50, rate.avgHeight]); + seriesData['75th'].push([timestamp, rate.avgFee_75, rate.avgHeight]); + seriesData['90th'].push([timestamp, rate.avgFee_90, rate.avgHeight]); + seriesData['Max'].push([timestamp, rate.avgFee_100, rate.avgHeight]); } // Prepare chart @@ -130,13 +130,9 @@ export class BlockFeeRatesGraphComponent implements OnInit { }); this.isLoading = false; }), - map((data: any) => { - const availableTimespanDay = ( - (new Date().getTime() / 1000) - (data.oldestIndexedBlockTimestamp) - ) / 3600 / 24; - + map((response) => { return { - availableTimespanDay: availableTimespanDay, + blockCount: parseInt(response.headers.get('x-total-count'), 10), }; }), ); 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 c78311d71..171aa1d44 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 @@ -2,11 +2,10 @@
- Block fees - -
- + - + @@ -103,9 +114,20 @@ - + - + +
TimestampTimestamp ‎{{ bisqTx.time | date:'yyyy-MM-dd HH:mm' }}
diff --git a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html b/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html index d1064972e..7a2056b46 100644 --- a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html +++ b/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html @@ -18,7 +18,7 @@
TXID Type AmountConfirmedConfirmed Height
Confidential
Total fees + + + + +   +   +
Subsidy + fees: - + + + +
Miner + + {{ block.extras.pool.name }} + + + + {{ block.extras.pool.name }} + +
@@ -173,14 +195,29 @@
- + - + +
+
+ Error loading data. +

+ {{ transactionsError.status }}: {{ transactionsError.error }} +
+
+
+
+ +
-
- -
+ +
+
+
+
+
+
@@ -193,14 +230,6 @@
- - -
-
-
-
-
-
@@ -253,9 +282,15 @@
- Error loading block data. + Error loading data.

- {{ error.error }} + {{ error.code }}: {{ error.error }} +
+
+ + +
+
diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 512b6b411..bd70e8628 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -10,6 +10,7 @@ import { SeoService } from 'src/app/services/seo.service'; import { WebsocketService } from 'src/app/services/websocket.service'; import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe'; import { BlockExtended } from 'src/app/interfaces/node-api.interface'; +import { ApiService } from 'src/app/services/api.service'; @Component({ selector: 'app-block', @@ -31,13 +32,13 @@ export class BlockComponent implements OnInit, OnDestroy { blockSubsidy: number; fees: number; paginationMaxSize: number; - coinbaseTx: Transaction; page = 1; itemsPerPage: number; txsLoadingStatus$: Observable; showDetails = false; showPreviousBlocklink = true; showNextBlocklink = true; + transactionsError: any = null; subscription: Subscription; keyNavigationSubscription: Subscription; @@ -50,10 +51,11 @@ export class BlockComponent implements OnInit, OnDestroy { private location: Location, private router: Router, private electrsApiService: ElectrsApiService, - private stateService: StateService, + public stateService: StateService, private seoService: SeoService, private websocketService: WebsocketService, private relativeUrlPipe: RelativeUrlPipe, + private apiService: ApiService ) { } ngOnInit() { @@ -88,7 +90,6 @@ export class BlockComponent implements OnInit, OnDestroy { const blockHash: string = params.get('id') || ''; this.block = undefined; this.page = 1; - this.coinbaseTx = undefined; this.error = undefined; this.fees = undefined; this.stateService.markBlock$.next({}); @@ -124,7 +125,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.location.replaceState( this.router.createUrlTree([(this.network ? '/' + this.network : '') + '/block/', hash]).toString() ); - return this.electrsApiService.getBlock$(hash); + return this.apiService.getBlock$(hash); }) ); } @@ -134,7 +135,7 @@ export class BlockComponent implements OnInit, OnDestroy { return of(blockInCache); } - return this.electrsApiService.getBlock$(blockHash); + return this.apiService.getBlock$(blockHash); } }), tap((block: BlockExtended) => { @@ -145,7 +146,6 @@ export class BlockComponent implements OnInit, OnDestroy { this.seoService.setTitle($localize`:@@block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.id}:BLOCK_ID:`); this.isLoadingBlock = false; - this.coinbaseTx = block?.extras?.coinbaseTx; this.setBlockSubsidy(); if (block?.extras?.reward !== undefined) { this.fees = block.extras.reward / 100000000 - this.blockSubsidy; @@ -153,12 +153,13 @@ export class BlockComponent implements OnInit, OnDestroy { this.stateService.markBlock$.next({ blockHeight: this.blockHeight }); this.isLoadingTransactions = true; this.transactions = null; + this.transactionsError = null; }), debounceTime(300), switchMap((block) => this.electrsApiService.getBlockTransactions$(block.id) .pipe( catchError((err) => { - console.log(err); + this.transactionsError = err; return of([]); })) ), @@ -167,9 +168,6 @@ export class BlockComponent implements OnInit, OnDestroy { if (this.fees === undefined && transactions[0]) { this.fees = transactions[0].vout.reduce((acc: number, curr: Vout) => acc + curr.value, 0) / 100000000 - this.blockSubsidy; } - if (!this.coinbaseTx && transactions[0]) { - this.coinbaseTx = transactions[0]; - } this.transactions = transactions; this.isLoadingTransactions = false; }, @@ -212,22 +210,26 @@ export class BlockComponent implements OnInit, OnDestroy { this.queryParamsSubscription.unsubscribe(); } + // TODO - Refactor this.fees/this.reward for liquid because it is not + // used anymore on Bitcoin networks (we use block.extras directly) setBlockSubsidy() { - if (this.network === 'liquid' || this.network === 'liquidtestnet') { - this.blockSubsidy = 0; - return; - } - const halvings = Math.floor(this.block.height / 210000); - this.blockSubsidy = 50 * 2 ** -halvings; + this.blockSubsidy = 0; } pageChange(page: number, target: HTMLElement) { const start = (page - 1) * this.itemsPerPage; this.isLoadingTransactions = true; 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; 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 e31214443..4a6bcf14a 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -1,35 +1,34 @@ -
-

Blocks

+
+

Blocks

+
- - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.scss b/frontend/src/app/components/blocks-list/blocks-list.component.scss index c8035dc44..8388ecac2 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.scss +++ b/frontend/src/app/components/blocks-list/blocks-list.component.scss @@ -1,3 +1,9 @@ +.spinner-border { + height: 25px; + width: 25px; + margin-top: 13px; +} + .container-xl { max-width: 1400px; padding-bottom: 100px; @@ -6,18 +12,18 @@ padding-left: 0px; padding-bottom: 0px; } +.container-xl.legacy { + max-width: 1140px; +} .container { max-width: 100%; } -td { - padding-top: 0.7rem !important; +tr, td, th { + border: 0px; + padding-top: 0.65rem !important; padding-bottom: 0.7rem !important; - @media (max-width: 376px) { - padding-top: 0.73rem !important; - padding-bottom: 0.73rem !important; - } } .clear-link { @@ -41,7 +47,7 @@ td { } .pool.widget { width: 40%; - padding-left: 30px; + padding-left: 24px; @media (max-width: 376px) { width: 60%; } @@ -56,11 +62,14 @@ td { width: 10%; } .height.widget { - width: 20%; + width: 15%; @media (max-width: 576px) { width: 10%; } } +.height.legacy { + width: 15%; +} .timestamp { width: 18%; @@ -68,6 +77,9 @@ td { display: none; } } +.timestamp.legacy { + width: 20%; +} .mined { width: 13%; @@ -75,6 +87,12 @@ td { display: none; } } +.mined.legacy { + width: 15%; + @media (max-width: 576px) { + display: table-cell; + } +} .txs { padding-right: 40px; @@ -91,6 +109,10 @@ td { display: none; } } +.txs.legacy { + padding-right: 80px; + width: 10%; +} .fees { width: 10%; @@ -129,6 +151,12 @@ td { display: none; } } +.size.legacy { + width: 20%; + @media (max-width: 576px) { + display: table-cell; + } +} /* Tooltip text */ .tooltip-custom { 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 9da92f158..7cca2af62 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -17,6 +17,7 @@ export class BlocksList implements OnInit { blocks$: Observable = undefined; + indexingAvailable = false; isLoading = true; fromBlockHeight = undefined; paginationMaxSize: number; @@ -35,11 +36,14 @@ export class BlocksList implements OnInit { } ngOnInit(): void { + this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && + this.stateService.env.MINING_DASHBOARD === true); + if (!this.widget) { this.websocketService.want(['blocks']); } - this.skeletonLines = this.widget === true ? [...Array(5).keys()] : [...Array(15).keys()]; + this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; this.blocks$ = combineLatest([ @@ -55,17 +59,19 @@ export class BlocksList implements OnInit { this.isLoading = false; }), map(blocks => { - for (const block of blocks) { - // @ts-ignore: Need to add an extra field for the template - block.extras.pool.logo = `./resources/mining-pools/` + - block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; + if (this.indexingAvailable) { + for (const block of blocks) { + // @ts-ignore: Need to add an extra field for the template + block.extras.pool.logo = `./resources/mining-pools/` + + block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; + } } if (this.widget) { - return blocks.slice(0, 5); + return blocks.slice(0, 6); } return blocks; }), - retryWhen(errors => errors.pipe(delayWhen(() => timer(1000)))) + retryWhen(errors => errors.pipe(delayWhen(() => timer(10000)))) ) }) ), @@ -81,11 +87,13 @@ export class BlocksList implements OnInit { return blocks[0]; } this.blocksCount = Math.max(this.blocksCount, blocks[1][0].height) + 1; - // @ts-ignore: Need to add an extra field for the template - blocks[1][0].extras.pool.logo = `./resources/mining-pools/` + - blocks[1][0].extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; + if (this.stateService.env.MINING_DASHBOARD) { + // @ts-ignore: Need to add an extra field for the template + blocks[1][0].extras.pool.logo = `./resources/mining-pools/` + + blocks[1][0].extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; + } acc.unshift(blocks[1][0]); - acc = acc.slice(0, this.widget ? 5 : 15); + acc = acc.slice(0, this.widget ? 6 : 15); return acc; }, []) ); diff --git a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.html b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.html index 787058d91..5f11c2608 100644 --- a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.html +++ b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.html @@ -1,4 +1,4 @@ -
+
Height - PoolTimestampMined - RewardFeesTxsSizeHeightPoolTimestampMinedRewardFeesTXsTransactionsSize
- {{ block.height - }} + + {{ block.height }} + ‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }} + - + + - + + + {{ block.tx_count | number }} +
@@ -62,29 +61,29 @@
+ + + + + + + +
@@ -22,7 +22,7 @@ - + diff --git a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.scss b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.scss index a0d8e115e..f379effe2 100644 --- a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.scss +++ b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.scss @@ -2,10 +2,15 @@ width: 100%; text-align: left; table-layout:fixed; - tr, td, th { + tr, th { border: 0px; + padding-top: 0.65rem !important; + padding-bottom: 0.7rem !important; } td { + border: 0px; + padding-top: 0.71rem !important; + padding-bottom: 0.75rem !important; width: 25%; @media (max-width: 376px) { padding: 0.85rem; diff --git a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.components.ts b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.components.ts index 1026bc145..fb7d5c8f7 100644 --- a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.components.ts +++ b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.components.ts @@ -34,10 +34,6 @@ export class DifficultyAdjustmentsTable implements OnInit { .pipe( map((response) => { const data = response.body; - const availableTimespanDay = ( - (new Date().getTime() / 1000) - (data.oldestIndexedBlockTimestamp) - ) / 3600 / 24; - const tableData = []; for (let i = data.difficulty.length - 1; i > 0; --i) { const selectedPowerOfTen: any = selectPowerOfTen(data.difficulty[i].difficulty); @@ -53,8 +49,7 @@ export class DifficultyAdjustmentsTable implements OnInit { this.isLoading = false; return { - availableTimespanDay: availableTimespanDay, - difficulty: tableData.slice(0, 5), + difficulty: tableData.slice(0, 6), }; }), ); diff --git a/frontend/src/app/components/graphs/graphs.component.html b/frontend/src/app/components/graphs/graphs.component.html index a32829d6b..b02adae5e 100644 --- a/frontend/src/app/components/graphs/graphs.component.html +++ b/frontend/src/app/components/graphs/graphs.component.html @@ -1,37 +1,23 @@ -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HeightTimestampMinedTransactionsSize
{{ block.height }}‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}{{ block.tx_count | number }} -
-
-
-
-
-
-
-
-
- - -
- Error loading blocks -
- {{ error.error }} -
-
- -
diff --git a/frontend/src/app/components/latest-blocks/latest-blocks.component.scss b/frontend/src/app/components/latest-blocks/latest-blocks.component.scss deleted file mode 100644 index 0f2246c99..000000000 --- a/frontend/src/app/components/latest-blocks/latest-blocks.component.scss +++ /dev/null @@ -1,14 +0,0 @@ -.progress { - background-color: #2d3348; -} - -@media (min-width: 768px) { - .d-md-block { - display: table-cell !important; - } -} -@media (min-width: 992px) { - .d-lg-block { - display: table-cell !important; - } -} diff --git a/frontend/src/app/components/latest-blocks/latest-blocks.component.ts b/frontend/src/app/components/latest-blocks/latest-blocks.component.ts deleted file mode 100644 index 5bdb55ece..000000000 --- a/frontend/src/app/components/latest-blocks/latest-blocks.component.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; -import { ElectrsApiService } from '../../services/electrs-api.service'; -import { StateService } from '../../services/state.service'; -import { Block } from '../../interfaces/electrs.interface'; -import { Subscription, Observable, merge, of } from 'rxjs'; -import { SeoService } from '../../services/seo.service'; -import { WebsocketService } from 'src/app/services/websocket.service'; -import { map } from 'rxjs/operators'; - -@Component({ - selector: 'app-latest-blocks', - templateUrl: './latest-blocks.component.html', - styleUrls: ['./latest-blocks.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class LatestBlocksComponent implements OnInit, OnDestroy { - network$: Observable; - error: any; - blocks: any[] = []; - blockSubscription: Subscription; - isLoading = true; - interval: any; - blocksLoadingStatus$: Observable; - - latestBlockHeight: number; - - heightOfPageUntilBlocks = 150; - heightOfBlocksTableChunk = 470; - - constructor( - private electrsApiService: ElectrsApiService, - public stateService: StateService, - private seoService: SeoService, - private websocketService: WebsocketService, - private cd: ChangeDetectorRef, - ) { } - - ngOnInit() { - this.seoService.setTitle($localize`:@@f4cba7faeb126346f09cc6af30124f9a343f7a28:Blocks`); - this.websocketService.want(['blocks']); - - this.network$ = merge(of(''), this.stateService.networkChanged$); - - this.blocksLoadingStatus$ = this.stateService.loadingIndicators$ - .pipe( - map((indicators) => indicators['blocks'] !== undefined ? indicators['blocks'] : 0) - ); - - this.blockSubscription = this.stateService.blocks$ - .subscribe(([block]) => { - if (block === null || !this.blocks.length) { - return; - } - - this.latestBlockHeight = block.height; - - if (block.height === this.blocks[0].height) { - return; - } - - // If we are out of sync, reload the blocks instead - if (block.height > this.blocks[0].height + 1) { - this.loadInitialBlocks(); - return; - } - - if (block.height <= this.blocks[0].height) { - return; - } - - this.blocks.pop(); - this.blocks.unshift(block); - this.cd.markForCheck(); - }); - - this.loadInitialBlocks(); - } - - ngOnDestroy() { - clearInterval(this.interval); - this.blockSubscription.unsubscribe(); - } - - loadInitialBlocks() { - this.electrsApiService.listBlocks$() - .subscribe((blocks) => { - this.blocks = blocks; - this.isLoading = false; - this.error = undefined; - - this.latestBlockHeight = blocks[0].height; - - const spaceForBlocks = window.innerHeight - this.heightOfPageUntilBlocks; - const chunks = Math.ceil(spaceForBlocks / this.heightOfBlocksTableChunk) - 1; - if (chunks > 0) { - this.loadMore(chunks); - } - this.cd.markForCheck(); - }, - (error) => { - console.log(error); - this.error = error; - this.isLoading = false; - this.cd.markForCheck(); - }); - } - - loadMore(chunks = 0) { - if (this.isLoading) { - return; - } - const height = this.blocks[this.blocks.length - 1].height - 1; - if (height < 0) { - return; - } - this.isLoading = true; - this.electrsApiService.listBlocks$(height) - .subscribe((blocks) => { - this.blocks = this.blocks.concat(blocks); - this.isLoading = false; - this.error = undefined; - - const chunksLeft = chunks - 1; - if (chunksLeft > 0) { - this.loadMore(chunksLeft); - } - this.cd.markForCheck(); - }, - (error) => { - console.log(error); - this.error = error; - this.isLoading = false; - this.cd.markForCheck(); - }); - } - - trackByBlock(index: number, block: Block) { - return block.height; - } -} 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 a8d2987a1..a729f3708 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 @@ -21,8 +21,8 @@ Testnet Bisq - - + Liquid + Liquid Testnet
diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html index 98e490f37..ac141ecd5 100644 --- a/frontend/src/app/components/master-page/master-page.component.html +++ b/frontend/src/app/components/master-page/master-page.component.html @@ -3,7 +3,7 @@