From 3e61e9b1f34857a134a91698305c4189383fe051 Mon Sep 17 00:00:00 2001 From: Pianochicken Date: Wed, 8 Mar 2023 14:50:15 +0800 Subject: [PATCH] Update: Quorum network add node (#70) * Fix quorum network setting and path while creating * Modify gas setting & Add error handler * Fix: superuser privileges not allow * Fix: update blockscout and quorum to lastest * Feat: update genesis.json config * Fix: uid and gid enviroment variable * Fix: blockscout url is nil * Feat: ora spinner in command * Feat: default of explorer creating * Feat: default of network creating * Fix: quorum explorer can not be deleted * Fix: modify Validator/Member expose port * Fix: eslint & package audit update * Feat: add local validator * Fix: modify add validator * Feat: add local member * Fix: add trytime for adding node in quorum network * Feat: add downOneService in quorum network * Feat: add check command * Feat: add check chainId * Fix: modify ports of node connections * Feat: add generate & addValidatorRemote & joinValidator * Fix: modify json file & send empty transaction * Feat: add get command to get Quorum network information * Fix: modify addValidatorRemote parameter requirement & get validator enode information * Feat: add addMemberRemote & modify join/generate * Doc: new example of adding remote node & Modify document introduction * Doc: unified quorum Example.md format * fix: backup dockerCompose file according to node * Doc: upgrade 2.0.2 README.md and CHANGELOG.md --------- Co-authored-by: kidneyweak --- CHANGELOG.md | 23 + README.md | 5 +- docs/quorum/COMMANDS.md | 68 +- docs/quorum/EXAMPLE.md | 89 +- package-lock.json | 1584 ++++------------- package.json | 3 +- src/bdk.ts | 6 +- src/quorum/command/backup/export.ts | 14 +- src/quorum/command/backup/import.ts | 8 +- src/quorum/command/explorer/create.ts | 12 +- src/quorum/command/explorer/delete.ts | 13 +- src/quorum/command/network/add.ts | 127 ++ src/quorum/command/network/check.ts | 63 + src/quorum/command/network/create.ts | 37 +- src/quorum/command/network/delete.ts | 15 +- src/quorum/command/network/down.ts | 6 +- src/quorum/command/network/generate.ts | 81 + src/quorum/command/network/get.ts | 99 ++ src/quorum/command/network/join.ts | 76 + src/quorum/command/network/up.ts | 14 +- src/quorum/instance/bdkFile.ts | 69 + .../instance/infra/InfraRunner.interface.ts | 1 + src/quorum/instance/infra/docker/runner.ts | 58 +- src/quorum/instance/member.ts | 5 + src/quorum/instance/validator.ts | 5 + src/quorum/model/defaultNetworkConfig.ts | 25 + src/quorum/model/testNetworkConfig.ts | 52 - src/quorum/model/type/docker.type.ts | 4 + src/quorum/model/type/network.type.ts | 34 +- .../explorerDockerComposeYaml.ts | 18 +- .../docker-compose/memberDockerCompose.ts | 16 +- .../validatorDockerComposeYaml.ts | 13 +- src/quorum/service/backup.ts | 9 +- src/quorum/service/explorer.ts | 17 + src/quorum/service/network.ts | 425 ++++- src/util/error.ts | 11 + src/util/utils.ts | 15 +- test/fabric/service/caService.test.ts | 2 +- tsconfig.json | 2 +- 39 files changed, 1659 insertions(+), 1465 deletions(-) create mode 100644 src/quorum/command/network/add.ts create mode 100644 src/quorum/command/network/check.ts create mode 100644 src/quorum/command/network/generate.ts create mode 100644 src/quorum/command/network/get.ts create mode 100644 src/quorum/command/network/join.ts create mode 100644 src/quorum/model/defaultNetworkConfig.ts delete mode 100644 src/quorum/model/testNetworkConfig.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ddd7f202..9ff7444e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,29 @@ All notable changes to BDK project will be documented here. [2.0.1]: https://github.com/cathayddt/bdk/releases/tag/v2.0.1 +[2.0.2]: https://github.com/cathayddt/bdk/releases/tag/v2.0.2 + +## [2.0.2][2.0.2] - 2023-03- + +### new +1. New Package: `ora` +2. New Command: `bdk quorum network add` +3. New Command: `bdk quorum network generate` +4. New Command: `bdk quorum network join` +5. New Command: `bdk quorum network check` +6. New Command: `bdk quorum network get` +7. New Example: add remote node + +### changed +1. Fix: path not found, provide `PathError` notification +2. Fix: superuser privilege, not provide su execute +3. Fix: `bdk quorum network delete` cannot delete image +4. Upgrade: quorum (22.7.4) and blockscout (v4.1.5-beta) +5. Modify: `gasLimit` from `0xFFFFFF` to `0xE0000000` +6. Modify: `emptyBlockPeriodSeconds` from 60 to 3600 +7. Modify: `requestTimeoutSeconds` from 4 to 60 +8. Modify: `bdk quorum network create` member node default value 0 + ## [2.0.1][2.0.1] - 2022-12- ### new diff --git a/README.md b/README.md index 04ce6729..a9bac472 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,9 @@ bdk fabric network create -i | Latest | Stable | | ---------------- | ---------------- | -| [v2.0.1][v2.0.1] | [v1.0.4][v1.0.4] | +| [v2.0.2][v2.0.2] | [v2.0.2][v2.0.2] | -[v1.0.4]: https://github.com/cathayddt/bdk/releases/tag/v1.0.4 -[v2.0.1]: https://github.com/cathayddt/bdk/releases/tag/v2.0.1 +[v2.0.2]: https://github.com/cathayddt/bdk/releases/tag/v2.0.2 [更新內容 (Changelog)](CHANGELOG.md) diff --git a/docs/quorum/COMMANDS.md b/docs/quorum/COMMANDS.md index 1b1d8f7b..ea77323d 100644 --- a/docs/quorum/COMMANDS.md +++ b/docs/quorum/COMMANDS.md @@ -10,9 +10,29 @@ ## Network +### `bdk quorum network add` + +Description: 新增 Quorum Node + +| Options | Type | Description | Required | Default | +| --------------------- | :-----: | ------------------------------ | :------: | ------- | +| --help | boolean | Show help | | | +| --version | boolean | Show version number | | | +| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | | + +### `bdk quorum network check` + +Description: 確認 Quorum Node 資訊 + +| Options | Type | Description | Required | Default | +| --------------------- | :-----: | ------------------------------ | :------: | ------- | +| --help | boolean | Show help | | | +| --version | boolean | Show version number | | | +| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | | + ### `bdk quorum network create` -Description: 產生 Quorum network 所需的相關設定檔案 +Description: 產生 Quorum Network 所需的相關設定檔案並建立網路 | Options | Type | Description | Required | Default | | --------------------- | :-----: | ------------------------------ | :------: | ------- | @@ -22,32 +42,62 @@ Description: 產生 Quorum network 所需的相關設定檔案 ### `bdk quorum network delete` -Description: 刪除現有的 Quorum Network. +Description: 刪除現有的 Quorum Network | Options | Type | Description | Required | Default | | --------------------- | :-----: | ------------------------------ | :------: | ------- | | --help | boolean | Show help | | | | --version | boolean | Show version number | | | -### `bdk quorum network up` +### `bdk quorum network down` + +Description: 停止現有的 Quorum Network + +| Options | Type | Description | Required | Default | +| --------------------- | :-----: | ------------------------------ | :------: | ------- | +| --help | boolean | Show help | | | +| --version | boolean | Show version number | | | + +### `bdk quorum network generate` -Description: 啟動現有的 Quorum Network. +Description: 產生 Quorum Network 所需的相關設定檔案 | Options | Type | Description | Required | Default | | --------------------- | :-----: | ------------------------------ | :------: | ------- | | --help | boolean | Show help | | | | --version | boolean | Show version number | | | | -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | | -| -a, --all | boolean | 是否啟動所有節點 | | | -### `bdk quorum network down` +### `bdk quorum network get` + +Description: 取得 Quorum 檔案資訊 + +| Options | Type | Description | Required | Default | +| --------------------- | :-----: | ------------------------------ | :------: | ------- | +| --help | boolean | Show help | | | +| --version | boolean | Show version number | | | +| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | | + +### `bdk quorum network join` -Description: 停止現有的 Quorum Network. +Description: 選擇現有節點加入 Quorum Network | Options | Type | Description | Required | Default | | --------------------- | :-----: | ------------------------------ | :------: | ------- | | --help | boolean | Show help | | | | --version | boolean | Show version number | | | +| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | | + +### `bdk quorum network up` + +Description: 啟動現有的 Quorum Network + +| Options | Type | Description | Required | Default | +| --------------------- | :-----: | ------------------------------ | :------: | ------- | +| --help | boolean | Show help | | | +| --version | boolean | Show version number | | | +| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | | +| -a, --all | boolean | 是否啟動所有節點 | | | ## Explorer @@ -74,7 +124,7 @@ Description: 刪除現有的 Quorum Explorer. ### `bdk quorum backup export` -Description: 匯出現有的 Quorum Network. +Description: 匯出現有的 Quorum Network | Options | Type | Description | Required | Default | | --------------------- | :-----: | ------------------------------ | :------: | ------- | @@ -85,7 +135,7 @@ Description: 匯出現有的 Quorum Network. ### `bdk quorum backup import` -Description: 匯入現有的 Quorum Network. +Description: 匯入現有的 Quorum Network | Options | Type | Description | Required | Default | | --------------------- | :-----: | ------------------------------ | :------: | ------- | diff --git a/docs/quorum/EXAMPLE.md b/docs/quorum/EXAMPLE.md index 8f29def2..a01f7ea2 100644 --- a/docs/quorum/EXAMPLE.md +++ b/docs/quorum/EXAMPLE.md @@ -2,65 +2,118 @@ (English version)(Work In Progress) ## 目錄 -- [建立 Blockchain network](#建立-blockchain-network) +- [確認 BDK 安裝狀態](#確認-bdk-安裝狀態) +- [建立 Quorum Network](#建立-quorum-network) +- [建立 Blockscout Explorer](#建立-blockscout-explorer) +- [加入 Remote 節點](#加入-remote-節點) - [備份還原 Node](#備份還原-node) -## 建立 Blockchain network - -Quorum 無須做過多的前置設定,只需確認 BDK 套件是否安裝完成即可 +## 確認 BDK 安裝狀態 ```bash +# 確認 BDK 套件是否安裝完成 bdk hello ``` 如果指令已順利安裝,你會看到 `You have installed bdk successfully!!!` -### 建立 Network +## 建立 Quorum Network + +```bash +# 輸入指令,啟動 Quorum 網路互動式介面 +bdk quorum network create -i +``` + +依序輸入 `chain id`(預設為 81712)、`validator` 以及 `member` 的數量,並填入自己的錢包,如無錢包則選擇 `false`,會提供一組公私鑰來作為使用 Quorum 網路的帳號,該組帳號將在創始區塊擁有代幣 + +## 建立 Blockscout Explorer + +```bash +# 輸入指令,啟動 Blockscout 區塊鏈瀏覽器 +bdk quorum explorer create -i +``` + +輸入 `port`,並稍待片刻,即可使用區塊鏈瀏覽器 + +## 加入 Remote 節點 + +範例情境:建立擁有三個 Validator 節點的 Quorum 網路,且三個節點分別建置在不同機器。 + +> `需確認機器的 IP Address 以及 Port 30303 (Validator) 或 Port 30403 (Member) 有互相開放`。 + +Validator 以及 Member 節點的加入流程相同,此處以 Validator 為例。 -輸入指令,啟動 Quorum 網路互動式介面 +### Step 0: 建立 Network ```bash +# 於第一台機器上建立 Validator 數量為 1,Member 數量為 0 的 Quorum 網路。 bdk quorum network create -i ``` -依序輸入 chain id (預設為1337)、validator 以及 member 的數量,以及填入自己的錢包,如無填入(選擇 false ),則會提供一組公私鑰來作為使用 Quorum 網路的帳號,該組帳號會在創始區塊擁有代幣 +### Step 1:產生 Node 設定檔,取得 enodeInfo -### 建立 Blockscout Explorer +```bash +# 分別於第二台及第三台機器產生 Node 設定檔, Validator 數量填入 1,Member 數量填入 0。 +bdk quorum network generate -i + +# 選擇 node,選擇剛產生的節點,選擇 enodeInfo 保留下來。 +bdk quorum network get -i +``` -輸入指令,啟動 Blockscout 區塊鏈瀏覽器 +### Step 2:新增節點 ```bash -bdk quorum explorer create -i +# 從第一台機器輸入指令,選擇 remote、validator,填入要加入節點的 enodeInfo、IP Address。 +bdk quorum network add -i + +# 選擇 network,取得 genesis.json、static-nodes.json 保留下來。 +bdk quorum network get -i +``` + +### Step 3:加入網路 + +```bash +# 從要加入的機器輸入指令,選擇要加入的節點,並依序輸入以下資訊,填入要加入網路的 IP Address、genesis.json、static-nodes.json。 +bdk quorum network join -i ``` -輸入 port ,並稍待片刻,即可使用區塊鏈瀏覽器 +> 欲加入第三台機器的節點,則需在第一台及第二台機器完成前述 `Step 2:新增節點` 的動作,再於第三台機器完成 `Step 3:加入網路`,第三個節點便可順利加入網路。 + +### Step 4:確認節點狀態 + +```bash +# 輸入指令,可確認以下節點狀態 +bdk quorum network check -i +``` +- `isValidator`: 確認節點是否為 Validator,此處三個節點都應回傳 `true`。 +- `getValidator`: 獲取 Validator 清單,此處應回傳包含三個 Validator 的地址。 +- `peerCount`: 確認節點連接數,此處每個節點應回傳 `2`。 +- `chainId`:鏈 ID,此處預設應回傳 `81712`。 ## 備份還原 Node Quorum 網路各節點啟動後,即可做備份的動作,BDK 備份工具可根據使用者的需求備份個別或全部節點,並將備份壓縮檔存入 `~/.bdk/quorum/backup` 資料夾中,達成多台機器還原環境的功能 -### 備份現有的 Node (擇一備份) +### 備份 Node ```bash +# 備份現有的 Node (擇一備份) bdk quorum backup export --interactive ``` -### 備份所有的 Node - ```bash +# 備份所有的 Node bdk quorum backup export --all ``` ### 還原 Node -透過 `bdk backup` 指令還原節點,可在清單中選擇需要還原的備份檔 - ```bash +# 透過指令還原節點,可在清單中選擇需要還原的備份檔 bdk quorum backup import -i ``` -還原後需透過以下指令,來啟動該備份的節點 - ```bash +# 還原後需透過以下指令,來啟動該備份的節點 bdk quorum network up --all ``` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c5a64c09..374b9128 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cathayddt/bdk", - "version": "2.0.1", + "version": "2.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@cathayddt/bdk", - "version": "2.0.1", + "version": "2.0.2", "license": "Apache-2.0", "dependencies": { "deep-object-diff": "^1.1.0", @@ -16,6 +16,7 @@ "ethers": "^5.6.5", "fs-extra": "^9.1.0", "js-yaml": "^4.1.0", + "ora": "^5.4.1", "prompts": "^2.4.0", "rlp": "^3.0.0", "string-format": "^2.0.0", @@ -1420,15 +1421,6 @@ "node": ">= 8" } }, - "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -1464,18 +1456,6 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, - "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "dependencies": { - "defer-to-connect": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@tsconfig/node10": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", @@ -1891,15 +1871,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "dependencies": { - "string-width": "^4.1.0" - } - }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -2110,40 +2081,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, - "node_modules/boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "dev": true, - "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2229,48 +2166,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -2331,7 +2226,6 @@ "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" @@ -2344,10 +2238,16 @@ } }, "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -2381,12 +2281,6 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -2396,11 +2290,21 @@ "node": ">=6" } }, - "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", "engines": { "node": ">=6" }, @@ -2418,13 +2322,12 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "engines": { + "node": ">=0.8" } }, "node_modules/color": { @@ -2503,23 +2406,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "node_modules/configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "dependencies": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/convert-source-map": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", @@ -2567,15 +2453,6 @@ "node": ">= 8" } }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -2601,27 +2478,6 @@ "node": ">=0.10.0" } }, - "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2645,11 +2501,16 @@ "node": ">=8" } }, - "node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/define-properties": { "version": "1.1.3", @@ -2722,18 +2583,6 @@ "node": ">=6.0.0" } }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/dotenv": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", @@ -2742,12 +2591,6 @@ "node": ">=10" } }, - "node_modules/duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, "node_modules/electron-to-chromium": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.11.tgz", @@ -2898,15 +2741,6 @@ "node": ">=6" } }, - "node_modules/escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3654,9 +3488,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "node_modules/follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true, "funding": [ { @@ -3808,18 +3642,6 @@ "node": ">=8.0.0" } }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -3837,9 +3659,9 @@ } }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -3868,21 +3690,6 @@ "node": ">=10.13.0" } }, - "node_modules/global-dirs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", - "dev": true, - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globals": { "version": "13.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", @@ -3918,28 +3725,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", @@ -4000,7 +3785,6 @@ "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" } @@ -4032,15 +3816,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -4100,12 +3875,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -4156,15 +3925,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -4198,15 +3958,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -4278,18 +4029,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-core-module": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", @@ -4346,20 +4085,12 @@ "node": ">=0.10.0" } }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "dev": true, - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/is-negative-zero": { @@ -4374,18 +4105,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-npm": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", - "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4410,24 +4129,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -4513,7 +4214,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, "engines": { "node": ">=10" }, @@ -4542,12 +4242,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true - }, "node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -4698,12 +4392,6 @@ "node": ">=4" } }, - "node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -4717,13 +4405,10 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, "bin": { "json5": "lib/cli.js" }, @@ -4754,15 +4439,6 @@ "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, - "node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.0" - } - }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -4776,18 +4452,6 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, - "node_modules/latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dev": true, - "dependencies": { - "package-json": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4836,7 +4500,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -4860,15 +4523,6 @@ "triple-beam": "^1.3.0" } }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4918,12 +4572,12 @@ "dev": true }, "node_modules/marked": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", - "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", "dev": true, "bin": { - "marked": "bin/marked" + "marked": "bin/marked.js" }, "engines": { "node": ">= 12" @@ -4951,13 +4605,12 @@ "node": ">=8.6" } }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/minimalistic-assert": { @@ -4971,9 +4624,9 @@ "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -4983,10 +4636,13 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/minipass": { "version": "3.3.5", @@ -5029,32 +4685,32 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "node_modules/mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -5081,29 +4737,6 @@ "xml-escape": "1.1.0" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/mocha/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -5135,6 +4768,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5207,9 +4852,9 @@ "optional": true }, "node_modules/nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -5262,22 +4907,21 @@ "dev": true }, "node_modules/nodemon": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz", - "integrity": "sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", + "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", "dev": true, - "hasInstallScript": true, "dependencies": { "chokidar": "^3.5.2", "debug": "^3.2.7", "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "pstree.remy": "^1.1.8", "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", "supports-color": "^5.5.0", "touch": "^3.1.0", - "undefsafe": "^2.0.5", - "update-notifier": "^5.1.0" + "undefsafe": "^2.0.5" }, "bin": { "nodemon": "bin/nodemon.js" @@ -5353,15 +4997,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/nyc": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", @@ -5617,6 +5252,20 @@ "fn.name": "1.x.x" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -5634,13 +5283,26 @@ "node": ">= 0.8.0" } }, - "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-limit": { @@ -5703,30 +5365,6 @@ "node": ">=8" } }, - "node_modules/package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "dependencies": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5829,15 +5467,6 @@ "node": ">= 0.8.0" } }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -5900,18 +5529,6 @@ "node": ">=6" } }, - "node_modules/pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dev": true, - "dependencies": { - "escape-goat": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5941,36 +5558,6 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -6008,30 +5595,6 @@ "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "dev": true, - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dev": true, - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -6080,13 +5643,16 @@ "node": ">=4" } }, - "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dependencies": { - "lowercase-keys": "^1.0.0" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" } }, "node_modules/reusify": { @@ -6180,27 +5746,6 @@ "node": ">=10" } }, - "node_modules/semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dev": true, - "dependencies": { - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -6265,8 +5810,7 @@ "node_modules/signal-exit": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", - "dev": true + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" }, "node_modules/simple-swizzle": { "version": "0.2.2", @@ -6276,6 +5820,27 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dev": true, + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -6496,7 +6061,6 @@ "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" }, @@ -6599,15 +6163,6 @@ "node": ">=4" } }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6700,9 +6255,9 @@ } }, "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -6789,15 +6344,15 @@ } }, "node_modules/typedoc": { - "version": "0.21.9", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.9.tgz", - "integrity": "sha512-VRo7aII4bnYaBBM1lhw4bQFmUcDQV8m8tqgjtc7oXl87jc1Slbhfw2X5MccfcR2YnEClHDWgsiQGgNB8KJXocA==", + "version": "0.21.10", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.10.tgz", + "integrity": "sha512-Y0wYIehkjkPfsp3pv86fp3WPHUcOf8pnQUDLwG1PqSccUSqdsv7Pz1Gd5WrTJvXQB2wO1mKlZ8qW8qMiopKyjA==", "dev": true, "dependencies": { "glob": "^7.1.7", "handlebars": "^4.7.7", "lunr": "^2.3.9", - "marked": "^3.0.2", + "marked": "^4.0.10", "minimatch": "^3.0.0", "progress": "^2.0.3", "shiki": "^0.9.8", @@ -6869,18 +6424,6 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -6889,34 +6432,6 @@ "node": ">= 10.0.0" } }, - "node_modules/update-notifier": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", - "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", - "dev": true, - "dependencies": { - "boxen": "^5.0.0", - "chalk": "^4.1.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.4.0", - "is-npm": "^5.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.1.0", - "pupa": "^2.1.1", - "semver": "^7.3.4", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -6926,18 +6441,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6971,6 +6474,14 @@ "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", "dev": true }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7008,18 +6519,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/winston": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", @@ -7094,9 +6593,9 @@ "dev": true }, "node_modules/workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "node_modules/wrap-ansi": { @@ -7152,15 +6651,6 @@ } } }, - "node_modules/xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/xml-escape": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/xml-escape/-/xml-escape-1.1.0.tgz", @@ -8172,12 +7662,6 @@ "fastq": "^1.6.0" } }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true - }, "@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -8213,15 +7697,6 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "requires": { - "defer-to-connect": "^1.0.1" - } - }, "@tsconfig/node10": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", @@ -8544,15 +8019,6 @@ "uri-js": "^4.2.2" } }, - "ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "requires": { - "string-width": "^4.1.0" - } - }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -8710,30 +8176,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, - "boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "dev": true, - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", - "dev": true - } - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -8792,38 +8234,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - } - } - }, "caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -8868,16 +8278,15 @@ "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" } }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -8906,23 +8315,24 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==" }, "cliui": { "version": "7.0.4", @@ -8934,14 +8344,10 @@ "wrap-ansi": "^7.0.0" } }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" }, "color": { "version": "3.2.1", @@ -9015,20 +8421,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, "convert-source-map": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", @@ -9069,12 +8461,6 @@ "which": "^2.0.1" } }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true - }, "debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -9089,21 +8475,6 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -9124,11 +8495,13 @@ "strip-bom": "^4.0.0" } }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true + "defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "requires": { + "clone": "^1.0.2" + } }, "define-properties": { "version": "1.1.3", @@ -9183,26 +8556,11 @@ "esutils": "^2.0.2" } }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, "dotenv": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, "electron-to-chromium": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.11.tgz", @@ -9328,12 +8686,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true - }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -9899,9 +9251,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true }, "foreground-child": { @@ -9997,15 +9349,6 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, "get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -10017,9 +9360,9 @@ } }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -10039,15 +9382,6 @@ "is-glob": "^4.0.3" } }, - "global-dirs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", - "dev": true, - "requires": { - "ini": "2.0.0" - } - }, "globals": { "version": "13.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", @@ -10071,25 +9405,6 @@ "slash": "^3.0.0" } }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, "graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", @@ -10132,8 +9447,7 @@ "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 + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "has-symbols": { "version": "1.0.2", @@ -10150,12 +9464,6 @@ "has-symbols": "^1.0.2" } }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true - }, "hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -10205,12 +9513,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -10238,12 +9540,6 @@ "resolve-from": "^4.0.0" } }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true - }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -10271,12 +9567,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true - }, "internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -10327,15 +9617,6 @@ "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", "dev": true }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, "is-core-module": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", @@ -10374,15 +9655,10 @@ "is-extglob": "^2.1.1" } }, - "is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "dev": true, - "requires": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - } + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==" }, "is-negative-zero": { "version": "2.0.1", @@ -10390,12 +9666,6 @@ "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", "dev": true }, - "is-npm": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", - "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", - "dev": true - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -10411,18 +9681,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -10477,8 +9735,7 @@ "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" }, "is-weakref": { "version": "1.0.1", @@ -10495,12 +9752,6 @@ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true - }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -10620,12 +9871,6 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -10639,13 +9884,10 @@ "dev": true }, "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true }, "jsonc-parser": { "version": "3.0.0", @@ -10668,15 +9910,6 @@ "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -10687,15 +9920,6 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dev": true, - "requires": { - "package-json": "^6.3.0" - } - }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -10738,7 +9962,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, "requires": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -10756,12 +9979,6 @@ "triple-beam": "^1.3.0" } }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -10801,9 +10018,9 @@ "dev": true }, "marked": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", - "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", "dev": true }, "merge2": { @@ -10822,11 +10039,10 @@ "picomatch": "^2.2.3" } }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "minimalistic-assert": { "version": "1.0.1", @@ -10839,18 +10055,18 @@ "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", "dev": true }, "minipass": { @@ -10885,54 +10101,37 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -10952,6 +10151,15 @@ "p-locate": "^5.0.0" } }, + "minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -11015,9 +10223,9 @@ "optional": true }, "nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "natural-compare": { @@ -11061,21 +10269,21 @@ "dev": true }, "nodemon": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz", - "integrity": "sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", + "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", "dev": true, "requires": { "chokidar": "^3.5.2", "debug": "^3.2.7", "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "pstree.remy": "^1.1.8", "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", "supports-color": "^5.5.0", "touch": "^3.1.0", - "undefsafe": "^2.0.5", - "update-notifier": "^5.1.0" + "undefsafe": "^2.0.5" }, "dependencies": { "debug": { @@ -11125,12 +10333,6 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true - }, "nyc": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", @@ -11331,6 +10533,14 @@ "fn.name": "1.x.x" } }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -11345,11 +10555,21 @@ "word-wrap": "^1.2.3" } }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } }, "p-limit": { "version": "1.3.0", @@ -11396,26 +10616,6 @@ "release-zalgo": "^1.0.0" } }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -11491,12 +10691,6 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -11547,15 +10741,6 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, - "pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dev": true, - "requires": { - "escape-goat": "^2.0.0" - } - }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -11571,32 +10756,6 @@ "safe-buffer": "^5.1.0" } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - } - } - }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -11622,24 +10781,6 @@ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, - "registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -11676,13 +10817,13 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "requires": { - "lowercase-keys": "^1.0.0" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" } }, "reusify": { @@ -11743,23 +10884,6 @@ "lru-cache": "^6.0.0" } }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dev": true, - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -11815,8 +10939,7 @@ "signal-exit": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", - "dev": true + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" }, "simple-swizzle": { "version": "0.2.2", @@ -11826,6 +10949,23 @@ "is-arrayish": "^0.3.1" } }, + "simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dev": true, + "requires": { + "semver": "~7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, "sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -11990,7 +11130,6 @@ "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" } @@ -12071,12 +11210,6 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -12141,9 +11274,9 @@ }, "dependencies": { "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -12208,15 +11341,15 @@ } }, "typedoc": { - "version": "0.21.9", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.9.tgz", - "integrity": "sha512-VRo7aII4bnYaBBM1lhw4bQFmUcDQV8m8tqgjtc7oXl87jc1Slbhfw2X5MccfcR2YnEClHDWgsiQGgNB8KJXocA==", + "version": "0.21.10", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.10.tgz", + "integrity": "sha512-Y0wYIehkjkPfsp3pv86fp3WPHUcOf8pnQUDLwG1PqSccUSqdsv7Pz1Gd5WrTJvXQB2wO1mKlZ8qW8qMiopKyjA==", "dev": true, "requires": { "glob": "^7.1.7", "handlebars": "^4.7.7", "lunr": "^2.3.9", - "marked": "^3.0.2", + "marked": "^4.0.10", "minimatch": "^3.0.0", "progress": "^2.0.3", "shiki": "^0.9.8", @@ -12260,42 +11393,11 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" - } - }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" }, - "update-notifier": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", - "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", - "dev": true, - "requires": { - "boxen": "^5.0.0", - "chalk": "^4.1.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.4.0", - "is-npm": "^5.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.1.0", - "pupa": "^2.1.1", - "semver": "^7.3.4", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - } - }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -12305,15 +11407,6 @@ "punycode": "^2.1.0" } }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -12343,6 +11436,14 @@ "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", "dev": true }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "requires": { + "defaults": "^1.0.3" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -12371,15 +11472,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "requires": { - "string-width": "^4.0.0" - } - }, "winston": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", @@ -12447,9 +11539,9 @@ "dev": true }, "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { @@ -12485,12 +11577,6 @@ "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", "requires": {} }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true - }, "xml-escape": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/xml-escape/-/xml-escape-1.1.0.tgz", diff --git a/package.json b/package.json index 54c6f5c8..ff870d70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cathayddt/bdk", - "version": "2.0.1", + "version": "2.0.2", "description": "Blockchain Deploy Kit", "homepage": "https://github.com/cathayddt/bdk#readme", "bugs": { @@ -50,6 +50,7 @@ "ethers": "^5.6.5", "fs-extra": "^9.1.0", "js-yaml": "^4.1.0", + "ora": "^5.4.1", "prompts": "^2.4.0", "rlp": "^3.0.0", "string-format": "^2.0.0", diff --git a/src/bdk.ts b/src/bdk.ts index 7aed175d..764aaf7f 100755 --- a/src/bdk.ts +++ b/src/bdk.ts @@ -2,7 +2,7 @@ import yargs from 'yargs' import config from './fabric/config' -import { errorHandler } from './util' +import { errorHandler, ProcessError } from './util' (config.isDevMode || config.isTestMode) && require('source-map-support/register') @@ -20,6 +20,10 @@ Blockchain Deploy Kit // eslint-disable-next-line @typescript-eslint/no-unused-vars const argv = yargs + .check(() => { + if (process.getuid() === 0) throw new ProcessError('⚠️ DO NOT RUN AS ROOT ⚠️ ') + return true + }) .commandDir('fabric') .commandDir('quorum') .commandDir('hello') diff --git a/src/quorum/command/backup/export.ts b/src/quorum/command/backup/export.ts index 39a9fe53..934956ca 100644 --- a/src/quorum/command/backup/export.ts +++ b/src/quorum/command/backup/export.ts @@ -1,13 +1,13 @@ import { Argv, Arguments } from 'yargs' import config from '../../config' import Backup from '../../service/backup' -import { logger } from '../../../util' -import { onCancel, ParamsError } from '../../../util/error' +import { onCancel, ParamsError, ProcessError } from '../../../util/error' import prompts from 'prompts' +import ora from 'ora' export const command = 'export' -export const desc = '匯出現有的 Quorum Network.' +export const desc = '匯出現有的 Quorum Network' interface OptType { interactive: boolean @@ -25,8 +25,9 @@ export const handler = async (argv: Arguments) => { const backup = new Backup(config) if (argv.all) { + const spinner = ora('Quorum Network Export All ...').start() await backup.exportAll() - logger.info('Quorum Network export all Successfully!') + spinner.succeed('Quorum Network Export All Successfully!') } else if (argv.interactive) { const node: string = await (async () => { const nodeList = backup.getExportItems() @@ -38,12 +39,13 @@ export const handler = async (argv: Arguments) => { choices: nodeList, }, { onCancel })).node } else { - throw new ParamsError('Invalid params: Required node not exist') + throw new ProcessError('[x] [file-system error]: Node not exist') } })() + const spinner = ora(`Quorum Network Export ${node} ...`).start() await backup.export(node) - logger.info(`Quorum Network export ${node} Successfully!`) + spinner.succeed(`Quorum Network Export ${node} Successfully!`) } else { throw new ParamsError('Invalid params: Required parameter missing') } diff --git a/src/quorum/command/backup/import.ts b/src/quorum/command/backup/import.ts index f3e0ab78..5fbb0be3 100644 --- a/src/quorum/command/backup/import.ts +++ b/src/quorum/command/backup/import.ts @@ -1,13 +1,13 @@ import { Argv, Arguments } from 'yargs' import config from '../../config' import Backup from '../../service/backup' -import { logger } from '../../../util' import { onCancel, ParamsError } from '../../../util/error' import prompts from 'prompts' +import ora from 'ora' export const command = 'import' -export const desc = '匯入現有的 Quorum Network.' +export const desc = '匯入現有的 Quorum Network' interface OptType { interactive: boolean @@ -35,7 +35,7 @@ export const handler = async (argv: Arguments) => { throw new ParamsError('Invalid params: Required parameter missing') } })() - + const spinner = ora('Quorum Network Import ...').start() await backup.import(archive) - logger.info('Quorum Network import Successfully!') + spinner.succeed('Quorum Network Import Successfully!') } diff --git a/src/quorum/command/explorer/create.ts b/src/quorum/command/explorer/create.ts index 562757a7..470719ef 100644 --- a/src/quorum/command/explorer/create.ts +++ b/src/quorum/command/explorer/create.ts @@ -1,9 +1,9 @@ import { Argv, Arguments } from 'yargs' import Explorer from '../../service/explorer' -import { onCancel, ParamsError } from '../../../util/error' +import { onCancel } from '../../../util/error' import config from '../../config' import prompts from 'prompts' -import { logger } from '../../../util' +import ora from 'ora' export const command = 'create' @@ -11,8 +11,6 @@ export const desc = '產生 Quorum Explorer 所需的相關設定檔案' interface OptType { interactive: boolean - genesis: boolean - dockerCompose: boolean } export const builder = (yargs: Argv) => { @@ -36,10 +34,12 @@ export const handler = async (argv: Arguments) => { initial: 26000, }, ], { onCancel })).port + } else { + return 26000 } - throw new ParamsError('Invalid params: Required parameter missing') })() + const spinner = ora('Quorum Explorer Create ...').start() await explorer.create(explorerCreate) - logger.info(`Quorum Explorer Create Successfully! Host at: http://localhost:${explorerCreate}`) + spinner.succeed(`Quorum Explorer Create Successfully! Host at: http://localhost:${explorerCreate}`) } diff --git a/src/quorum/command/explorer/delete.ts b/src/quorum/command/explorer/delete.ts index 553b2b88..a15c4bb6 100644 --- a/src/quorum/command/explorer/delete.ts +++ b/src/quorum/command/explorer/delete.ts @@ -1,8 +1,8 @@ -import { Arguments } from 'yargs' import config from '../../config' import Explorer from '../../service/explorer' -import { logger, onCancel } from '../../../util' +import { onCancel } from '../../../util' import prompts from 'prompts' +import ora from 'ora' export const command = 'delete' @@ -10,9 +10,7 @@ export const desc = '刪除現有的 Quorum Explorer.' export const builder = {} -export const handler = async (argv: Arguments) => { - logger.debug('exec explorer delete', argv.$0) - +export const handler = async () => { const explorer = new Explorer(config) let confirmDelete = true @@ -20,14 +18,15 @@ export const handler = async (argv: Arguments) => { const response = await prompts({ type: 'confirm', name: 'value', - message: 'Confirm to delete Quorum Explorer?', + message: '⚠️ The following processes will remove all explorer files. Confirm to delete Quorum Explorer?', initial: false, }, { onCancel }) confirmDelete = response.value if (confirmDelete) { + const spinner = ora('Quorum Explorer Delete ...').start() await explorer.delete() - logger.info('Quorum Explorer delete Successfully!') + spinner.succeed('Quorum Explorer Delete Successfully!') } } diff --git a/src/quorum/command/network/add.ts b/src/quorum/command/network/add.ts new file mode 100644 index 00000000..96841837 --- /dev/null +++ b/src/quorum/command/network/add.ts @@ -0,0 +1,127 @@ +import { Argv, Arguments } from 'yargs' +import config from '../../config' +import prompts from 'prompts' +import Network from '../../service/network' +import { onCancel, ParamsError } from '../../../util/error' +import ora from 'ora' +import { AddValidatorRemoteType, AddMemberRemoteType } from '../../model/type/network.type' +import { ethers } from 'ethers' + +export const command = 'add' + +export const desc = '新增 Quorum Node' + +interface OptType { + interactive: boolean +} + +export const builder = (yargs: Argv) => { + return yargs + .example('bdk quorum network add --interactive', 'Cathay BDK 互動式問答') + .option('interactive', { type: 'boolean', description: '是否使用 Cathay BDK 互動式問答', alias: 'i' }) +} + +export const handler = async (argv: Arguments) => { + const network = new Network(config) + + if (argv.interactive) { + const connectOptionList = [ + { title: 'local', value: 'local' }, + { title: 'remote', value: 'remote' }, + ] + const { connectOption } = await prompts([ + { + type: 'select', + name: 'connectOption', + message: 'Which type of connection do you want?', + choices: connectOptionList, + }, + ], { onCancel }) + + const nodeOptionList = [ + { title: 'validator', value: 'validator' }, + { title: 'member', value: 'member' }, + ] + const { nodeOption } = await prompts([ + { + type: 'select', + name: 'nodeOption', + message: 'Which type of node do you want to add?', + choices: nodeOptionList, + }, + ], { onCancel }) + + if (connectOption === 'local') { + if (nodeOption === 'validator') { + const spinner = ora('Quorum Network Add ...').start() + const validatorNum = await network.addValidatorLocal() + spinner.succeed(`Quorum Network Add Validator${validatorNum} Successfully!`) + } else { + const spinner = ora('Quorum Network Add ...').start() + const memberNum = await network.addMemberLocal() + spinner.succeed(`Quorum Network Add Member${memberNum} Successfully!`) + } + } else if (connectOption === 'remote') { + if (nodeOption === 'validator') { + const { enodeInfo, ipAddress } = await prompts([ + { + type: 'text', + name: 'enodeInfo', + message: 'Paste the enodeInfo of the validator node you want to add', + }, + { + type: 'text', + name: 'ipAddress', + message: 'Provide the ip address of the validator node you want to add', + }, + ], { onCancel }) + + const validatorPublicKey = `0x04${(enodeInfo.match(/enode:\/\/(.*?)@/i)[1]).replace(/^0x/, '').toLowerCase()}` + const validatorDiscoveryPort = enodeInfo.slice(enodeInfo.lastIndexOf(':') + 1) + const validatorAddress = ethers.utils.computeAddress(validatorPublicKey).toLowerCase() + + const addValidatorRemoteConfig: AddValidatorRemoteType = { + validatorAddress: validatorAddress, + validatorPublicKey: validatorPublicKey.replace(/^0x04/, ''), + discoveryPort: validatorDiscoveryPort, + ipAddress: ipAddress, + } + + const spinner = ora('Quorum Network Add ...').start() + await network.addValidatorRemote(addValidatorRemoteConfig) + spinner.succeed(`Quorum Network Add Validator ${validatorAddress} Successfully!`) + } else { + const { enodeInfo, ipAddress } = await prompts([ + { + type: 'text', + name: 'enodeInfo', + message: 'Paste the enodeInfo of the member node you want to add', + }, + { + type: 'text', + name: 'ipAddress', + message: 'Provide the ip address of the member node you want to add', + }, + ], { onCancel }) + + const memberPublicKey = `0x04${(enodeInfo.match(/enode:\/\/(.*?)@/i)[1]).replace(/^0x/, '').toLowerCase()}` + const memberDiscoveryPort = enodeInfo.slice(enodeInfo.lastIndexOf(':') + 1) + const memberAddress = ethers.utils.computeAddress(memberPublicKey).toLowerCase() + + const addMemberRemoteConfig: AddMemberRemoteType = { + memberAddress: memberAddress, + memberPublicKey: memberPublicKey.replace(/^0x04/, ''), + discoveryPort: memberDiscoveryPort, + ipAddress: ipAddress, + } + + const spinner = ora('Quorum Network Add ...').start() + await network.addMemberRemote(addMemberRemoteConfig) + spinner.succeed(`Quorum Network Add Member ${memberAddress} Successfully!`) + } + // TODO: addMemberRemote + } + } else { + throw new ParamsError('Invalid params: Required parameter missing') + } +} diff --git a/src/quorum/command/network/check.ts b/src/quorum/command/network/check.ts new file mode 100644 index 00000000..54a38eff --- /dev/null +++ b/src/quorum/command/network/check.ts @@ -0,0 +1,63 @@ +import { Argv, Arguments } from 'yargs' +import config from '../../config' +import prompts from 'prompts' +import Network from '../../service/network' +import { onCancel, ParamsError, ProcessError } from '../../../util/error' +import ora from 'ora' + +export const command = 'check' + +export const desc = '確認 Quorum Node 資訊' + +interface OptType { + interactive: boolean +} + +export const builder = (yargs: Argv) => { + return yargs + .example('bdk quorum network check --interactive', 'Cathay BDK 互動式問答') + .option('interactive', { type: 'boolean', description: '是否使用 Cathay BDK 互動式問答', alias: 'i' }) +} + +export const handler = async (argv: Arguments) => { + const network = new Network(config) + + if (argv.interactive) { + const node: string = await (async () => { + const nodeList = network.getUpExportItems() + + if (nodeList.length !== 0) { + return (await prompts({ + type: 'select', + name: 'node', + message: 'Which node do you want to check?', + choices: nodeList, + }, { onCancel })).node + } else { + throw new ProcessError('[x] [file-system error]: Node not exist') + } + })() + const checkOption = [ + { title: 'isValidator', value: 'istanbul.isValidator()' }, + { title: 'getValidator', value: 'istanbul.getValidators()' }, + { title: 'peerCount', value: 'net.peerCount' }, + { title: 'chainId', value: 'admin.nodeInfo.protocols.eth.network' }, + ] + + const { method } = await prompts([ + { + type: 'select', + name: 'method', + message: 'What do you want to check?', + choices: checkOption, + }, + ], { onCancel }) + + const spinner = ora('Quorum Network Check ...').start() + const result = await network.checkNode(node, method) + spinner.succeed(`Quorum Network Check Result: ${result}`) + spinner.succeed('Quorum Network Check Successfully!') + } else { + throw new ParamsError('Invalid params: Required parameter missing') + } +} diff --git a/src/quorum/command/network/create.ts b/src/quorum/command/network/create.ts index d7a86073..6603f57b 100644 --- a/src/quorum/command/network/create.ts +++ b/src/quorum/command/network/create.ts @@ -2,19 +2,18 @@ import { ethers } from 'ethers' import prompts from 'prompts' import { Argv, Arguments } from 'yargs' import Network from '../../service/network' -import { onCancel, ParamsError } from '../../../util/error' +import { onCancel } from '../../../util/error' import { NetworkCreateType } from '../../model/type/network.type' import config from '../../config' -import { logger } from '../../../util/logger' +import { defaultNetworkConfig } from '../../model/defaultNetworkConfig' +import ora from 'ora' export const command = 'create' -export const desc = '產生 Quorum network 所需的相關設定檔案' +export const desc = '產生 Quorum Network 所需的相關設定檔案並建立網路' interface OptType { interactive: boolean - genesis: boolean - dockerCompose: boolean } export const builder = (yargs: Argv) => { @@ -27,6 +26,7 @@ export const handler = async (argv: Arguments) => { const network = new Network(config) // check bdkPath files exist or not (include useless file e.g. .DS_Store) const confirm: boolean = await (async () => { + network.createBdkFolder() const fileList = network.getBdkFiles() if (fileList.length !== 0) { const confirmDelete = (await prompts({ @@ -36,8 +36,9 @@ export const handler = async (argv: Arguments) => { initial: false, }, { onCancel })).value if (confirmDelete) { + const spinner = ora('Quorum Network Create ...').start() network.removeBdkFiles(fileList) - logger.info('✔ Remove all existing files!') + spinner.succeed('Remove all existing files!') } return confirmDelete } else { @@ -54,7 +55,7 @@ export const handler = async (argv: Arguments) => { name: 'chainId', message: 'What is your chain id?', min: 0, - initial: 1337, + initial: 81712, }, { type: 'number', @@ -67,8 +68,8 @@ export const handler = async (argv: Arguments) => { type: 'number', name: 'memberNumber', message: 'How many member do you want?', - min: 1, - initial: 1, + min: 0, + initial: 0, }, ], { onCancel }) @@ -103,8 +104,14 @@ export const handler = async (argv: Arguments) => { } else { const { address, privateKey } = await network.createWalletAddress() walletAddress = address - logger.info(`Your wallet address: 0x${walletAddress}`) - logger.info(`Wallet private key: ${privateKey}`) + ora().stopAndPersist({ + text: `Your wallet address: 0x${walletAddress}`, + symbol: '🔑', + }) + ora().stopAndPersist({ + text: `Wallet private key: ${privateKey}`, + symbol: '🔑', + }) } const alloc = [{ @@ -113,11 +120,13 @@ export const handler = async (argv: Arguments) => { }] return { chainId, validatorNumber, memberNumber, alloc } + } else { + const { address, privateKey } = await network.createWalletAddress() + return defaultNetworkConfig(address, privateKey) } - throw new ParamsError('Invalid params: Required parameter missing') })() - + const spinner = ora('Quorum Network Create ...').start() await network.create(networkCreate) - logger.info('Quorum Network Create Successfully!') + spinner.succeed('Quorum Network Create Successfully!') } } diff --git a/src/quorum/command/network/delete.ts b/src/quorum/command/network/delete.ts index 6263389e..8e546507 100644 --- a/src/quorum/command/network/delete.ts +++ b/src/quorum/command/network/delete.ts @@ -1,18 +1,16 @@ -import { Arguments } from 'yargs' import config from '../../config' import Network from '../../service/network' -import { logger, onCancel } from '../../../util' +import { onCancel } from '../../../util' import prompts from 'prompts' +import ora from 'ora' export const command = 'delete' -export const desc = '刪除現有的 Quorum Network.' +export const desc = '刪除現有的 Quorum Network' export const builder = {} -export const handler = async (argv: Arguments) => { - logger.debug('exec network delete', argv.$0) - +export const handler = async () => { const network = new Network(config) let confirmDelete = true @@ -20,14 +18,15 @@ export const handler = async (argv: Arguments) => { const response = await prompts({ type: 'confirm', name: 'value', - message: '⚠️ The following processes will remove all existing files. Confirm to delete Quorum Network?', + message: '⚠️ The following processes will remove all network files. Confirm to delete Quorum Network?', initial: false, }, { onCancel }) confirmDelete = response.value if (confirmDelete) { + const spinner = ora('Quorum Network Delete ...').start() await network.delete() - logger.info('Quorum Network delete Successfully!') + spinner.succeed('Quorum Network Delete Successfully!') } } diff --git a/src/quorum/command/network/down.ts b/src/quorum/command/network/down.ts index 90aa5618..a5540f80 100644 --- a/src/quorum/command/network/down.ts +++ b/src/quorum/command/network/down.ts @@ -3,10 +3,11 @@ import config from '../../config' import Network from '../../service/network' import { logger, onCancel } from '../../../util' import prompts from 'prompts' +import ora from 'ora' export const command = 'down' -export const desc = '停止現有的 Quorum Network.' +export const desc = '停止現有的 Quorum Network' export const builder = {} @@ -27,7 +28,8 @@ export const handler = async (argv: Arguments) => { confirmDelete = response.value if (confirmDelete) { + const spinner = ora('Quorum Network Down ...').start() await network.down() - logger.info('Quorum Network down Successfully!') + spinner.succeed('Quorum Network Down Successfully!') } } diff --git a/src/quorum/command/network/generate.ts b/src/quorum/command/network/generate.ts new file mode 100644 index 00000000..20caf867 --- /dev/null +++ b/src/quorum/command/network/generate.ts @@ -0,0 +1,81 @@ +import prompts from 'prompts' +import { Argv, Arguments } from 'yargs' +import Network from '../../service/network' +import { onCancel, ParamsError } from '../../../util/error' +import { NetworkGenerateType } from '../../model/type/network.type' +import config from '../../config' +import ora from 'ora' + +export const command = 'generate' + +export const desc = '產生 Quorum Network 所需的相關設定檔案' + +interface OptType { + interactive: boolean +} + +export const builder = (yargs: Argv) => { + return yargs + .example('bdk quorum network generate --interactive', 'Cathay BDK 互動式問答') + .option('interactive', { type: 'boolean', description: '是否使用 Cathay BDK 互動式問答', alias: 'i' }) +} + +export const handler = async (argv: Arguments) => { + const network = new Network(config) + // check bdkPath files exist or not (include useless file e.g. .DS_Store) + const confirm: boolean = await (async () => { + network.createBdkFolder() + const fileList = network.getBdkFiles() + if (fileList.length !== 0) { + const confirmDelete = (await prompts({ + type: 'confirm', + name: 'value', + message: '⚠️ Detecting quorum nodes already exists. The following processes will remove all existing files. Continue?', + initial: false, + }, { onCancel })).value + if (confirmDelete) { + const spinner = ora('Quorum Network Generate ...').start() + network.removeBdkFiles(fileList) + spinner.succeed('Remove all existing files!') + } + return confirmDelete + } else { + return true + } + })() + + if (confirm) { + const networkGenerate: NetworkGenerateType = await (async () => { + if (argv.interactive) { + const { validatorNumber, memberNumber } = await prompts([ + { + type: 'number', + name: 'validatorNumber', + message: 'How many validator do you want?', + min: 0, + initial: 0, + }, + { + type: 'number', + name: 'memberNumber', + message: 'How many member do you want?', + min: 0, + initial: 0, + }, + ], { onCancel }) + + return { validatorNumber, memberNumber } + } else { + throw new ParamsError('Invalid params: Required parameter missing') + } + })() + + if (networkGenerate.validatorNumber + networkGenerate.memberNumber === 0) { + throw new ParamsError('Invalid params: You need to generate at least one node') + } + + const spinner = ora('Quorum Network Generate ...').start() + await network.generate(networkGenerate) + spinner.succeed('Quorum Network Generate Successfully!') + } +} diff --git a/src/quorum/command/network/get.ts b/src/quorum/command/network/get.ts new file mode 100644 index 00000000..60a261f0 --- /dev/null +++ b/src/quorum/command/network/get.ts @@ -0,0 +1,99 @@ +import { Argv, Arguments } from 'yargs' +import config from '../../config' +import prompts from 'prompts' +import Network from '../../service/network' +import { onCancel, ParamsError, ProcessError } from '../../../util/error' +import ora from 'ora' + +export const command = 'get' + +export const desc = '取得 Quorum 檔案資訊' + +interface OptType { + interactive: boolean +} + +export const builder = (yargs: Argv) => { + return yargs + .example('bdk quorum network get --interactive', 'Cathay BDK 互動式問答') + .option('interactive', { type: 'boolean', description: '是否使用 Cathay BDK 互動式問答', alias: 'i' }) +} + +export const handler = async (argv: Arguments) => { + const network = new Network(config) + + if (argv.interactive) { + const getOption = [ + { title: 'network', value: 'network' }, + { title: 'node', value: 'node' }, + ] + + const { get } = await prompts([ + { + type: 'select', + name: 'get', + message: 'What kind of information do you want to get?', + choices: getOption, + }, + ], { onCancel }) + + if (get === 'network') { + const networkOption = [ + { title: 'genesis.json', value: 'genesis.json' }, + { title: 'static-nodes.json', value: 'static-nodes.json' }, + { title: 'permissioned-nodes.json', value: 'permissioned-nodes.json' }, + ] + + const { networkInfo } = await prompts([ + { + type: 'select', + name: 'networkInfo', + message: 'Which network information do you want to get?', + choices: networkOption, + }, + ], { onCancel }) + + const spinner = ora('Quorum Network Get ...').start() + const result = await network.getNetworkInfo(networkInfo) + spinner.succeed(`Quorum Network Get Result: ${result}`) + spinner.succeed('Quorum Network Get Successfully!') + } else if (get === 'node') { + const node: string = await (async () => { + const nodeList = network.getUpExportItems() + + if (nodeList.length !== 0) { + return (await prompts({ + type: 'select', + name: 'node', + message: 'Which node do you want to get?', + choices: nodeList, + }, { onCancel })).node + } else { + throw new ProcessError('[x] [file-system error]: Node not exist') + } + })() + + const nodeOption = [ + { title: 'address', value: 'address' }, + { title: 'publicKey', value: 'publicKey' }, + { title: 'privateKey', value: 'privateKey' }, + { title: 'enodeInfo', value: 'enodeInfo' }, + ] + + const { nodeInfo } = await prompts([ + { + type: 'select', + name: 'nodeInfo', + message: 'Which node information do you want to get?', + choices: nodeOption, + }, + ], { onCancel }) + const spinner = ora('Quorum Network Get ...').start() + const result = network.getNodeInfo(node, nodeInfo) + spinner.succeed(`Quorum Network Get Result: ${result}`) + spinner.succeed('Quorum Network Get Successfully!') + } + } else { + throw new ParamsError('Invalid params: Required parameter missing') + } +} diff --git a/src/quorum/command/network/join.ts b/src/quorum/command/network/join.ts new file mode 100644 index 00000000..6dde1b0b --- /dev/null +++ b/src/quorum/command/network/join.ts @@ -0,0 +1,76 @@ +import { Argv, Arguments } from 'yargs' +import config from '../../config' +import prompts from 'prompts' +import Network from '../../service/network' +import { onCancel, ParamsError, ProcessError } from '../../../util/error' +import ora from 'ora' +import { JoinNodeType } from '../../model/type/network.type' + +export const command = 'join' + +export const desc = '選擇現有節點加入 Quorum Network' + +interface OptType { + interactive: boolean +} + +export const builder = (yargs: Argv) => { + return yargs + .example('bdk quorum network join --interactive', 'Cathay BDK 互動式問答') + .option('interactive', { type: 'boolean', description: '是否使用 Cathay BDK 互動式問答', alias: 'i' }) +} + +export const handler = async (argv: Arguments) => { + const network = new Network(config) + + if (argv.interactive) { + const node: string = await (async () => { + const nodeList = network.getUpExportItems() + + if (nodeList.length !== 0) { + return (await prompts({ + type: 'select', + name: 'node', + message: 'Select the node you want to join to other network?', + choices: nodeList, + }, { onCancel })).node + } else { + throw new ProcessError('[x] [file-system error]: Node not exist') + } + })() + if (!node.includes('validator') && !node.includes('member')) { + throw new ProcessError('[x] [node-type error]: Node must be validator or member') + } + + const { ipAddress, genesisJson, staticNodesJson } = await prompts([ + { + type: 'text', + name: 'ipAddress', + message: 'Provide the ip address of Quorum network you want to join', + }, + { + type: 'text', + name: 'genesisJson', + message: 'Paste the genesis.json file of Quorum network you want to join', + }, + { + type: 'text', + name: 'staticNodesJson', + message: 'Paste the static-nodes.json file of Quorum network you want to join', + }, + ], { onCancel }) + + const joinNodeConfig: JoinNodeType = { + node: node, + ipAddress: ipAddress, + genesisJson: JSON.parse(genesisJson), + staticNodesJson: JSON.parse(staticNodesJson), + } + + const spinner = ora('Quorum Network Join ...').start() + await network.joinNode(joinNodeConfig) + spinner.succeed(`Quorum Network Join ${node} Successfully!`) + } else { + throw new ParamsError('Invalid params: Required parameter missing') + } +} diff --git a/src/quorum/command/network/up.ts b/src/quorum/command/network/up.ts index 1293fc53..83d15485 100644 --- a/src/quorum/command/network/up.ts +++ b/src/quorum/command/network/up.ts @@ -1,13 +1,13 @@ import { Argv, Arguments } from 'yargs' import config from '../../config' import Network from '../../service/network' -import { logger } from '../../../util' -import { onCancel, ParamsError } from '../../../util/error' +import { onCancel, ParamsError, ProcessError } from '../../../util/error' import prompts from 'prompts' +import ora from 'ora' export const command = 'up' -export const desc = '啟動現有的 Quorum Network.' +export const desc = '啟動現有的 Quorum Network' interface OptType { interactive: boolean @@ -25,8 +25,9 @@ export const handler = async (argv: Arguments) => { const network = new Network(config) if (argv.all) { + const spinner = ora('Quorum Network Up All ...').start() await network.upAll() - logger.info('Quorum Network up all Successfully!') + spinner.succeed('Quorum Network Up All Successfully!') } else if (argv.interactive) { const node: string = await (async () => { const nodeList = network.getUpExportItems() @@ -39,12 +40,13 @@ export const handler = async (argv: Arguments) => { choices: nodeList, }, { onCancel })).node } else { - throw new ParamsError('Invalid params: Required node not exist') + throw new ProcessError('[x] [file-system error]: Node not exist') } })() + const spinner = ora(`Quorum Network Up ${node} ...`).start() await network.upService(node) - logger.info(`Quorum Network up ${node} Successfully!`) + spinner.succeed(`Quorum Network Up ${node} Successfully!`) } else { throw new ParamsError('Invalid params: Required parameter missing') } diff --git a/src/quorum/instance/bdkFile.ts b/src/quorum/instance/bdkFile.ts index f7b4f5ef..0d01f31c 100644 --- a/src/quorum/instance/bdkFile.ts +++ b/src/quorum/instance/bdkFile.ts @@ -4,6 +4,7 @@ import { Config } from '../config' import { GenesisJsonType } from '../model/type/network.type' import ValidatorDockerComposeYaml from '../model/yaml/docker-compose/validatorDockerComposeYaml' import MemberDockerComposeYaml from '../model/yaml/docker-compose/memberDockerCompose' +import { PathError } from '../../util/error' export enum InstanceTypeEnum { validator = 'validator', @@ -26,6 +27,10 @@ export default class BdkFile { this.orgPath = '' } + public createBdkFolder () { + fs.mkdirSync(`${this.bdkPath}`, { recursive: true }) + } + public createArtifactsFolder () { fs.mkdirSync(`${this.bdkPath}/artifacts/goQuorum`, { recursive: true }) } @@ -61,13 +66,49 @@ export default class BdkFile { } public getValidatorPublicKey (i: number) { + this.checkPathExist(`${this.bdkPath}/artifacts/validator${i}`) return fs.readFileSync(`${this.bdkPath}/artifacts/validator${i}/nodekey.pub`) } + public getValidatorPrivateKey (i: number) { + this.checkPathExist(`${this.bdkPath}/artifacts/validator${i}`) + return fs.readFileSync(`${this.bdkPath}/artifacts/validator${i}/nodekey`) + } + + public getValidatorAddress (i: number) { + this.checkPathExist(`${this.bdkPath}/artifacts/validator${i}`) + return fs.readFileSync(`${this.bdkPath}/artifacts/validator${i}/address`) + } + + public getValidatorEnodeInfo (i: number) { + this.checkPathExist(`${this.bdkPath}/artifacts/goQuorum`) + const staticNodesJson: Array = this.getStaticNodesJson() + const enodeInfo = staticNodesJson.find(file => file.includes(`validator${i}`)) + return enodeInfo + } + public getMemberPublicKey (i: number) { + this.checkPathExist(`${this.bdkPath}/artifacts/member${i}`) return fs.readFileSync(`${this.bdkPath}/artifacts/member${i}/nodekey.pub`) } + public getMemberPrivateKey (i: number) { + this.checkPathExist(`${this.bdkPath}/artifacts/member${i}`) + return fs.readFileSync(`${this.bdkPath}/artifacts/member${i}/nodekey`) + } + + public getMemberAddress (i: number) { + this.checkPathExist(`${this.bdkPath}/artifacts/member${i}`) + return fs.readFileSync(`${this.bdkPath}/artifacts/member${i}/address`) + } + + public getMemberEnodeInfo (i: number) { + this.checkPathExist(`${this.bdkPath}/artifacts/goQuorum`) + const staticNodesJson: Array = this.getStaticNodesJson() + const enodeInfo = staticNodesJson.find(file => file.includes(`member${i}`)) + return enodeInfo + } + public copyStaticNodesJsonToPermissionedNodesJson () { this.createArtifactsFolder() fs.copyFileSync(`${this.bdkPath}/artifacts/goQuorum/static-nodes.json`, `${this.bdkPath}/artifacts/goQuorum/permissioned-nodes.json`) @@ -155,18 +196,40 @@ export default class BdkFile { } public getBdkPath () { + this.checkPathExist(this.bdkPath) return `${this.bdkPath}` } public getExportFiles () { + this.checkPathExist(this.bdkPath) return fs.readdirSync(this.bdkPath) } + public getGenesisJson () { + this.checkPathExist(this.bdkPath) + const genesisJson = fs.readFileSync(`${this.bdkPath}/artifacts/goQuorum/genesis.json`, 'utf8') + return JSON.parse(genesisJson) + } + + public getStaticNodesJson () { + this.checkPathExist(this.bdkPath) + const staticNodesJson = fs.readFileSync(`${this.bdkPath}/artifacts/goQuorum/static-nodes.json`, 'utf8') + return JSON.parse(staticNodesJson) + } + + public getPermissionedNodesJson () { + this.checkPathExist(this.bdkPath) + const permissionedNodesJson = fs.readFileSync(`${this.bdkPath}/artifacts/goQuorum/permissioned-nodes.json`, 'utf8') + return JSON.parse(permissionedNodesJson) + } + public getBackupPath () { + this.checkPathExist(this.backupPath) return `${this.backupPath}` } public getBackupFiles () { + this.checkPathExist(this.backupPath) return fs.readdirSync(this.backupPath) } @@ -193,4 +256,10 @@ export default class BdkFile { public createMemberDockerComposeYaml (memberDockerComposeYaml: MemberDockerComposeYaml) { fs.writeFileSync(this.getMemberDockerComposeYamlPath(), memberDockerComposeYaml.getYamlString()) } + + public checkPathExist (path: string) { + if (!fs.existsSync(path)) { + throw new PathError(`${path} no exist`) + } + } } diff --git a/src/quorum/instance/infra/InfraRunner.interface.ts b/src/quorum/instance/infra/InfraRunner.interface.ts index e259ad0d..6f437744 100644 --- a/src/quorum/instance/infra/InfraRunner.interface.ts +++ b/src/quorum/instance/infra/InfraRunner.interface.ts @@ -24,6 +24,7 @@ export interface InfraRunner { upInBackground(dockerComposeFile: string): Promise upServiceInBackground(dockerComposeFile: string, service: string): Promise downAndRemoveVolumes(dockerComposeFile: string): Promise + downServiceAndRemoveVolumes(dockerComposeFile: string, service: string): Promise restart(dockerComposeFile: string, service: string[]): Promise } diff --git a/src/quorum/instance/infra/docker/runner.ts b/src/quorum/instance/infra/docker/runner.ts index fe785699..3bc6a738 100644 --- a/src/quorum/instance/infra/docker/runner.ts +++ b/src/quorum/instance/infra/docker/runner.ts @@ -1,12 +1,12 @@ import fs from 'fs' import stream from 'stream' import YAML from 'js-yaml' -import { spawnSync } from 'child_process' +import { spawn } from 'child_process' import Dockerode from 'dockerode' import { logger } from '../../../../util/logger' import { DockerCreateOptionsType, DockerStartOptionsType, DockerRunCommandType } from '../../../model/type/docker.type' import config from '../../../config' -import { DockerError, FabricContainerError } from '../../../../util/error' +import { DockerError, QuorumContainerError } from '../../../../util/error' import { DockerResultType, InfraRunner } from '../InfraRunner.interface' import { DockerComposeYamlInterface } from '../../../model/yaml/docker-compose/dockerComposeYaml' @@ -27,15 +27,16 @@ export class Runner implements InfraRunner { public runCommand = async (payload: DockerRunCommandType): Promise => { await this.checkAndCreateNetwork(payload.network) - const { image, tag, commands } = payload + const { image, tag, commands, ignoreError } = payload const createOptions: DockerCreateOptionsType = payload.createOptions || { + AttachStdout: true, Env: (payload.envFile ? fs.readFileSync(payload.envFile, { encoding: 'utf8' }).toString().split(/\n|\r|\r\n/).filter((x) => /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/.test(x)) : []).concat(payload.env || []), HostConfig: { AutoRemove: payload.autoRemove !== undefined ? payload.autoRemove : true, NetworkMode: payload.network, Binds: payload.volumes, }, - User: `${config.UID}:${config.GID}`, + User: (payload.user !== false) ? `${config.UID}:${config.GID}` : '', } if (!this.existingImages[`${image}:${tag}`]) { const images = await this.dockerode.listImages({ filters: { reference: [`${image}:${tag}`] } }) @@ -67,12 +68,12 @@ export class Runner implements InfraRunner { startOptions) logger.silly(`run command output: \n${stdout}`) logger.debug(`docker run\n image: ${image}\n commands: ${commands.join(' ')}`) - if (dockerRunResult[0].StatusCode !== 0) { - throw new FabricContainerError(`[x] [in-docker-container error] ${stdout.split('\r\n').filter(x => x.match(/error/i) || stdout)}`, stdout) + if (dockerRunResult[0].StatusCode !== 0 && !ignoreError) { + throw new QuorumContainerError(`[x] [in-docker-container error] ${stdout.split('\r\n').filter(x => x.match(/error/i) || stdout)}`, stdout) } - return { statusCode: dockerRunResult[0].StatusCode, stdout } + return { statusCode: dockerRunResult[0].StatusCode, stdout: stdout.toString() } } catch (e: any) { - if (e instanceof FabricContainerError) { throw e } + if (e instanceof QuorumContainerError) { throw e } throw new DockerError(`[x] command [docker run]:${e.message}`) } } @@ -87,7 +88,7 @@ export class Runner implements InfraRunner { NetworkMode: payload.network, Binds: payload.volumes, }, - User: `${config.UID}:${config.GID}`, + User: (payload.user !== false) ? `${config.UID}:${config.GID}` : '', } const stdout = `docker command: \ndocker run -d -u ${config.UID}:${config.GID} ${createOptions.HostConfig?.NetworkMode ? `--network ${createOptions.HostConfig?.NetworkMode} ` : ''}${(createOptions.HostConfig?.Binds || []).map(x => `-v ${x} `).join('')}${(createOptions.Env || []).map(x => `--env ${x} `).join('')}${image}:${tag || 'latest'} ${commands.join(' ')}` logger.debug(stdout) @@ -120,16 +121,19 @@ export class Runner implements InfraRunner { } // Docker Compose - private runSpawnSync (args: Array): string { - logger.debug(`run spawnSync: docker-compose ${args.join(' ')}`) - const spawnReturn = spawnSync('docker-compose', [...args]) - // TODO ! docker 裡面的 error 不能這樣抓 - // TODO 如果 docker-compose 不存在不會報錯 - if (spawnReturn.error) { - throw new DockerError(`[x] command [docker-compose]: ${spawnReturn.error.message}`) - } - logger.silly(spawnReturn.output.join('\n')) - return spawnReturn.output.join('\n') + private runSpawn (args: Array):Promise { + return new Promise((resolve) => { + logger.debug(`run spawnSync: docker-compose ${args.join(' ')}`) + const spawnReturn = spawn('docker-compose', [...args], { env: { ...process.env, UID: `${config.UID}`, GID: `${config.GID}` } }) + // TODO ! docker 裡面的 error 不這樣抓 + // TODO 如果 docker-compose 不存在不會報錯 + spawnReturn.stdout.on('close', () => { + resolve(`docker-compose ${args.join(' ')} OK`) + }) + spawnReturn.on('error', (error) => { + throw new DockerError(`[x] command [docker-compose]: ${error.message}`) + }) + }) } public upInBackground = async (dockerComposeFile: string) => { @@ -139,7 +143,7 @@ export class Runner implements InfraRunner { await this.checkAndCreateNetwork(network) } } - return { stdout: this.runSpawnSync(['-f', dockerComposeFile, 'up', '-d']) } + return { stdout: await this.runSpawn(['-f', dockerComposeFile, 'up', '-d']) } } public upServiceInBackground = async (dockerComposeFile: string, service: string) => { @@ -149,18 +153,18 @@ export class Runner implements InfraRunner { await this.checkAndCreateNetwork(network) } } - return { stdout: this.runSpawnSync(['-f', dockerComposeFile, 'up', '-d', '--', service]) } + return { stdout: await this.runSpawn(['-f', dockerComposeFile, 'up', '-d', '--', service]) } } - // eslint-disable-next-line require-await public downAndRemoveVolumes = async (dockerComposeFile: string) => { - // 為保留其他infra的操作空間,此method的type為(dockerComposeFile: string): Promise,雖然裡面沒有await,仍用async - return { stdout: this.runSpawnSync(['-f', dockerComposeFile, 'down', '--volumes']) } + return { stdout: await this.runSpawn(['-f', dockerComposeFile, 'down', '--volumes']) } + } + + public downServiceAndRemoveVolumes = async (dockerComposeFile: string, service: string) => { + return { stdout: await this.runSpawn(['-f', dockerComposeFile, 'down', '--volumes', '--', service]) } } - // eslint-disable-next-line require-await public restart = async (dockerComposeFile: string, service: string[] = []) => { - // 為保留其他infra的操作空間,此method的type為(dockerComposeFile: string): Promise,雖然裡面沒有await,仍用async - return { stdout: this.runSpawnSync(['-f', dockerComposeFile, 'restart'].concat(service)) } + return { stdout: await this.runSpawn(['-f', dockerComposeFile, 'restart'].concat(service)) } } } diff --git a/src/quorum/instance/member.ts b/src/quorum/instance/member.ts index a5b123ed..f742a3f5 100644 --- a/src/quorum/instance/member.ts +++ b/src/quorum/instance/member.ts @@ -26,6 +26,11 @@ export default class Member extends AbstractInstance { return await this.infra.downAndRemoveVolumes(this.dockerComposePath) } + public async downOneService (service: string): Promise { + logger.debug(`Member instance ${service} down`) + return await this.infra.downServiceAndRemoveVolumes(this.dockerComposePath, service) + } + public async restart (): Promise { logger.debug('Member instance restart') return await this.infra.restart(this.dockerComposePath, [`explorer.${this.config.networkName}`]) diff --git a/src/quorum/instance/validator.ts b/src/quorum/instance/validator.ts index f392142e..150f2250 100644 --- a/src/quorum/instance/validator.ts +++ b/src/quorum/instance/validator.ts @@ -26,6 +26,11 @@ export default class Validator extends AbstractInstance { return await this.infra.downAndRemoveVolumes(this.dockerComposePath) } + public async downOneService (service: string): Promise { + logger.debug(`Validator instance ${service} down`) + return await this.infra.downServiceAndRemoveVolumes(this.dockerComposePath, service) + } + public async restart (): Promise { logger.debug('Validator instance restart') return await this.infra.restart(this.dockerComposePath, [`explorer.${this.config.networkName}`]) diff --git a/src/quorum/model/defaultNetworkConfig.ts b/src/quorum/model/defaultNetworkConfig.ts new file mode 100644 index 00000000..9426b8eb --- /dev/null +++ b/src/quorum/model/defaultNetworkConfig.ts @@ -0,0 +1,25 @@ +import { NetworkCreateType } from './type/network.type' +import ora from 'ora' + +export function defaultNetworkConfig (address: string, privateKey: string) { + ora().stopAndPersist({ + text: `Your wallet address: 0x${address}`, + symbol: '🔑', + }) + ora().stopAndPersist({ + text: `Wallet private key: ${privateKey}`, + symbol: '🔑', + }) + + const networkConfig: NetworkCreateType = { + chainId: 81712, + validatorNumber: 4, + memberNumber: 0, + alloc: [{ + account: address, + amount: '1000000000000000000000000000', + }], + } + + return networkConfig +} diff --git a/src/quorum/model/testNetworkConfig.ts b/src/quorum/model/testNetworkConfig.ts deleted file mode 100644 index 46a45050..00000000 --- a/src/quorum/model/testNetworkConfig.ts +++ /dev/null @@ -1,52 +0,0 @@ -export const testNetworkConfig = { - validators: [ - { - name: 'Orderer', - domain: 'orderer.example.com', - enableNodeOUs: true, - hostname: [ - 'orderer', - ], - ports: [ - { - port: 7050, - isPublishPort: true, - operationPort: 8443, - isPublishOperationPort: true, - }, - ], - }, - ], - members: [ - { - name: 'Org1', - domain: 'org1.example.com', - enableNodeOUs: true, - memberCount: 1, - userCount: 1, - ports: [ - { - port: 7051, - isPublishPort: true, - operationPort: 9443, - isPublishOperationPort: true, - }, - ], - }, - { - name: 'Org2', - domain: 'org2.example.com', - enableNodeOUs: true, - memberCount: 1, - userCount: 1, - ports: [ - { - port: 8051, - isPublishPort: true, - operationPort: 10443, - isPublishOperationPort: true, - }, - ], - }, - ], -} diff --git a/src/quorum/model/type/docker.type.ts b/src/quorum/model/type/docker.type.ts index 5ff89064..6025a067 100644 --- a/src/quorum/model/type/docker.type.ts +++ b/src/quorum/model/type/docker.type.ts @@ -18,6 +18,7 @@ export interface DockerCreateOptionsType { HostConfig?: DockerHostConfigType WorkingDir?: string User?: string + AttachStdout?: boolean } export interface DockerStartOptionsType { id: string @@ -26,7 +27,9 @@ export interface DockerStartOptionsType { export interface DockerRunCommandType { name?: string image: string + user?: boolean tag?: string + stdout?: string commands: string[] volumes?: string[] autoRemove?: boolean @@ -37,4 +40,5 @@ export interface DockerRunCommandType { startOptions?: DockerStartOptionsType entryPoint?: string portBindings?: Map> + ignoreError?: boolean } diff --git a/src/quorum/model/type/network.type.ts b/src/quorum/model/type/network.type.ts index 61aead92..80f7b8a2 100644 --- a/src/quorum/model/type/network.type.ts +++ b/src/quorum/model/type/network.type.ts @@ -20,6 +20,11 @@ export interface NetworkCreateType { alloc: AllocType[] } +export interface NetworkGenerateType { + validatorNumber: number + memberNumber: number +} + interface Alloc { balance: string comment?: string @@ -56,11 +61,38 @@ export interface GenesisJsonType { policy: number ceil2Nby3Block: number } + transitions: { + block?: number + transactionSizeLimit?: number + contractSizeLimit?: number + blockPeriodSeconds?: number + emptyBlockPeriodSeconds?: number + }[] txnSizeLimit: number - maxCodeSize: number isQuorum: boolean } alloc: { [key: string]: Alloc } } + +export interface JoinNodeType { + node: string + ipAddress: string + genesisJson: GenesisJsonType + staticNodesJson: Array +} + +export interface AddValidatorRemoteType { + validatorAddress: string + validatorPublicKey: string + discoveryPort: string + ipAddress: string +} + +export interface AddMemberRemoteType { + memberAddress: string + memberPublicKey: string + discoveryPort: string + ipAddress: string +} diff --git a/src/quorum/model/yaml/docker-compose/explorerDockerComposeYaml.ts b/src/quorum/model/yaml/docker-compose/explorerDockerComposeYaml.ts index b1fe54da..7788dcf6 100644 --- a/src/quorum/model/yaml/docker-compose/explorerDockerComposeYaml.ts +++ b/src/quorum/model/yaml/docker-compose/explorerDockerComposeYaml.ts @@ -3,17 +3,18 @@ import DockerComposeYaml from './dockerComposeYaml' class ExplorerDockerComposeYaml extends DockerComposeYaml { constructor (bdkPath: string, port: number = 26000) { super() - this.addNetwork('quorum-blockscout', { name: 'quorum-blockscout', driver: 'bridge' }) + this.addNetwork('quorum', {}) this.addVolume('blockscoutpostgres', {}) this.addService( 'blockscout', { - image: 'consensys/blockscout:v4.0.0-beta', + image: 'consensys/blockscout:v4.1.5-beta', restart: 'no', container_name: 'blockscout', environment: [ 'PORT=4000', - 'DATABASE_URL=ecto://postgres:postgres@blockscoutpostgres/postgres?ssl=false', + 'ECTO_USE_SSL=false', + 'DATABASE_URL=postgresql://postgres:postgres@blockscoutpostgres:5432/postgres?ssl=false', 'POSTGRES_PASSWORD=postgres', 'POSTGRES_USER=postgres', 'NETWORK=quickstart', @@ -23,6 +24,9 @@ class ExplorerDockerComposeYaml extends DockerComposeYaml { 'SHOW_PRICE_CHART=false', 'ETHEREUM_JSONRPC_VARIANT=geth', 'ETHEREUM_JSONRPC_TRANSPORT=ipc', + 'ETHEREUM_JSONRPC_HTTP_URL=http://validator0:8545', + 'ETHEREUM_JSONRPC_TRACE_URL=http://validator0:8545', + 'ETHEREUM_JSONRPC_WS_URL=ws://validator0:8546', 'IPC_PATH=/root/geth.ipc', ], entrypoint: ['/bin/sh', '-c', 'cd /opt/app/; echo $$MIX_ENV && mix do ecto.create, ecto.migrate; mix phx.server;'], @@ -35,14 +39,14 @@ class ExplorerDockerComposeYaml extends DockerComposeYaml { ports: [ `${port}:4000`, ], - networks: ['quorum-blockscout'], + networks: ['quorum'], volumes: [`${bdkPath}/validator0/data/geth.ipc:/root/geth.ipc`], }, ) this.addService( 'blockscoutpostgres', { - image: 'postgres:13.4-alpine', + image: 'postgres:13.6-alpine', container_name: 'blockscoutpostgres', environment: [ 'POSTGRES_USER=postgres', @@ -61,9 +65,7 @@ class ExplorerDockerComposeYaml extends DockerComposeYaml { timeout: '10s', retries: 5, }, - networks: [ - 'quorum-blockscout', - ], + networks: ['quorum'], }, ) } diff --git a/src/quorum/model/yaml/docker-compose/memberDockerCompose.ts b/src/quorum/model/yaml/docker-compose/memberDockerCompose.ts index 60ca9a40..a432ca2c 100644 --- a/src/quorum/model/yaml/docker-compose/memberDockerCompose.ts +++ b/src/quorum/model/yaml/docker-compose/memberDockerCompose.ts @@ -1,20 +1,26 @@ import DockerComposeYaml from './dockerComposeYaml' class MemberDockerComposeYaml extends DockerComposeYaml { - public addMember (bdkPath: string, memberNum: number, port: number) { + public addMember (bdkPath: string, memberNum: number, rpcPort: number, chainId: number, peerPort: number) { this.addNetwork('quorum', {}) this.addService(`member${memberNum}`, { - image: 'quorumengineering/quorum:22.7.0', + image: 'quorumengineering/quorum:22.7.4', + // eslint-disable-next-line no-template-curly-in-string + user: '${UID}:${GID}', container_name: `member${memberNum}`, - restart: 'always', + restart: 'no', environment: ['PRIVATE_CONFIG=ignore'], ports: [ - `${port}:8545`, + `${rpcPort}:8545`, + `${rpcPort + 1}:8546`, + `${peerPort}:${peerPort}/tcp`, + `${peerPort}:${peerPort}/udp`, ], + networks: ['quorum'], volumes: [`${bdkPath}/member${memberNum}/data/:/data`], entrypoint: [ '/bin/sh', '-c', - 'geth init --datadir /data /data/genesis.json;geth --datadir /data --networkid 1337 --nodiscover --verbosity 3 --syncmode full --nousb --http --http.addr 0.0.0.0 --http.port 8545 --http.corsdomain "*" --http.vhosts "*" --ws --ws.addr 0.0.0.0 --ws.port 8546 --ws.origins "*" --http.api admin,trace,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul,qbft --ws.api admin,trace,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul,qbft --port 30303', + `geth init --datadir /data /data/genesis.json; geth --datadir /data --networkid ${chainId} --nodiscover --verbosity 3 --syncmode full --nousb --http --http.addr 0.0.0.0 --http.port 8545 --http.corsdomain "*" --http.vhosts "*" --ws --ws.addr 0.0.0.0 --ws.port 8546 --ws.origins "*" --http.api admin,trace,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul,qbft --ws.api admin,trace,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul,qbft --port ${peerPort}`, ], }) } diff --git a/src/quorum/model/yaml/docker-compose/validatorDockerComposeYaml.ts b/src/quorum/model/yaml/docker-compose/validatorDockerComposeYaml.ts index d6563f26..33fd63f2 100644 --- a/src/quorum/model/yaml/docker-compose/validatorDockerComposeYaml.ts +++ b/src/quorum/model/yaml/docker-compose/validatorDockerComposeYaml.ts @@ -1,21 +1,26 @@ import DockerComposeYaml from './dockerComposeYaml' class ValidatorDockerComposeYaml extends DockerComposeYaml { - public addValidator (bdkPath: string, validatorNum: number, port: number) { + public addValidator (bdkPath: string, validatorNum: number, rpcPort: number, chainId: number, peerPort: number) { this.addNetwork('quorum', {}) this.addService(`validator${validatorNum}`, { - image: 'quorumengineering/quorum:22.7.0', + image: 'quorumengineering/quorum:22.7.4', + // eslint-disable-next-line no-template-curly-in-string + user: '${UID}:${GID}', container_name: `validator${validatorNum}`, restart: 'no', environment: ['PRIVATE_CONFIG=ignore'], ports: [ - `${port}:8545`, + `${rpcPort}:8545`, + `${rpcPort + 1}:8546`, + `${peerPort}:${peerPort}/tcp`, + `${peerPort}:${peerPort}/udp`, ], networks: ['quorum'], volumes: [`${bdkPath}/validator${validatorNum}/data/:/data`], entrypoint: [ '/bin/sh', '-c', - 'geth init --datadir /data /data/genesis.json; geth --datadir /data --networkid 1337 --nodiscover --verbosity 3 --syncmode full --nousb --mine --miner.threads 1 --miner.gasprice 0 --emitcheckpoints --http --http.addr 0.0.0.0 --http.port 8545 --http.corsdomain "*" --http.vhosts "*" --ws --ws.addr 0.0.0.0 --ws.port 8546 --ws.origins "*" --http.api admin,trace,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul,qbft --ws.api admin,trace,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul,qbft --port 30303', + `geth init --datadir /data /data/genesis.json; geth --datadir /data --networkid ${chainId} --nodiscover --verbosity 3 --syncmode full --nousb --mine --miner.threads 1 --miner.gasprice 0 --emitcheckpoints --http --http.addr 0.0.0.0 --http.port 8545 --http.corsdomain "*" --http.vhosts "*" --ws --ws.addr 0.0.0.0 --ws.port 8546 --ws.origins "*" --http.api admin,trace,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul,qbft --ws.api admin,trace,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul,qbft --port ${peerPort} `, ], }) } diff --git a/src/quorum/service/backup.ts b/src/quorum/service/backup.ts index 340cd6fd..e60ee833 100644 --- a/src/quorum/service/backup.ts +++ b/src/quorum/service/backup.ts @@ -24,7 +24,7 @@ export default class Backup extends AbstractService { } /** - * @description 匯出 quorum network 單一validator備份資料 + * @description 匯出 quorum network 單一 node 備份資料 */ public export (nodeName: string) { const bdkPath = this.bdkFile.getBdkPath() @@ -33,12 +33,15 @@ export default class Backup extends AbstractService { cwd: bdkPath, sync: true, } + const dockerCompose = (nodeName.match(/(validator)[0-9]+/g)) + ? 'validator-docker-compose.yaml' + : 'member-docker-compose.yaml' + try { tar .c(createOpts, [`${nodeName}`, 'artifacts', - 'member-docker-compose.yaml', - 'validator-docker-compose.yaml']) + dockerCompose]) .pipe(this.bdkFile.createBackupTar(`${nodeName}`, tarDateFormat(new Date()))) } catch (e: any) { throw new BackupError(`[x] tar compress error: ${e.message}`) diff --git a/src/quorum/service/explorer.ts b/src/quorum/service/explorer.ts index 07758a5e..d70b4d18 100644 --- a/src/quorum/service/explorer.ts +++ b/src/quorum/service/explorer.ts @@ -16,10 +16,27 @@ export default class Explorer extends AbstractService { public async delete () { await (new ExplorerInstance(this.config, this.infra).down()) + this.removeBdkFiles(this.getExplorerFiles()) } private createExplorerDockerCompose (port: number) { const explorerDockerComposeYaml = new ExplorerDockerComposeYaml(this.bdkFile.getBdkPath(), port) this.bdkFile.createExplorerDockerComposeYaml(explorerDockerComposeYaml) } + + /** @ignore */ + public getExplorerFiles () { + const array = [] + array.push(this.bdkFile.getExportFiles().filter(file => file.match(/(explorer)+/g))) + const explorerFilesList = array.flat() + + return explorerFilesList + } + + /** @ignore */ + public removeBdkFiles (files: string[]) { + for (const file of files) { + this.bdkFile.removeBdkFiles(file) + } + } } diff --git a/src/quorum/service/network.ts b/src/quorum/service/network.ts index 4aad3500..a07d4622 100644 --- a/src/quorum/service/network.ts +++ b/src/quorum/service/network.ts @@ -1,11 +1,14 @@ import { ethers } from 'ethers' import RLP from 'rlp' -import { NetworkCreateType, GenesisJsonType } from '../model/type/network.type' +import { NetworkCreateType, NetworkGenerateType, GenesisJsonType, JoinNodeType, AddValidatorRemoteType, AddMemberRemoteType } from '../model/type/network.type' import { AbstractService } from './Service.abstract' import ValidatorInstance from '../instance/validator' import ValidatorDockerComposeYaml from '../model/yaml/docker-compose/validatorDockerComposeYaml' import MemberDockerComposeYaml from '../model/yaml/docker-compose/memberDockerCompose' import MemberInstance from '../instance/member' +import { DockerResultType } from '../instance/infra/InfraRunner.interface' +import { TimeLimitError } from '../../util/error' +import { sleep } from '../../util/utils' export default class Network extends AbstractService { /** @@ -34,7 +37,7 @@ export default class Network extends AbstractService { nonce: '0x0', timestamp: `0x${Math.floor(Date.now() / 1000).toString(16)}`, extraData, - gasLimit: '0xFFFFFF', + gasLimit: '0xE0000000', gasUsed: '0x0', number: '0x0', difficulty: '0x1', @@ -55,13 +58,19 @@ export default class Network extends AbstractService { qbft: { epochLength: 30000, blockPeriodSeconds: 1, - emptyBlockPeriodSeconds: 60, - requestTimeoutSeconds: 4, + emptyBlockPeriodSeconds: 3600, + requestTimeoutSeconds: 60, policy: 0, ceil2Nby3Block: 0, }, + transitions: [ + { + block: 0, + transactionSizeLimit: 64, + contractSizeLimit: 64, + }, + ], txnSizeLimit: 64, - maxCodeSize: 0, isQuorum: true, }, alloc, @@ -75,17 +84,20 @@ export default class Network extends AbstractService { for (let i = 0; i < networkCreateConfig.validatorNumber; i++) { const validatorPublicKey = this.bdkFile.getValidatorPublicKey(i) - const validatorNode = `enode://${validatorPublicKey}@validator-${i}:30303` + const validatorNode = `enode://${validatorPublicKey}@validator${i}:` + (30303 + i) staticNodesJson.push(validatorNode) } for (let i = 0; i < networkCreateConfig.memberNumber; i++) { const memberPublicKey = this.bdkFile.getMemberPublicKey(i) - const memberNode = `enode://${memberPublicKey}@member-${i}:30303` + const memberNode = `enode://${memberPublicKey}@member${i}:` + (30403 + i) staticNodesJson.push(memberNode) } - const validatorDockerComposeYaml = new ValidatorDockerComposeYaml() + this.bdkFile.createStaticNodesJson(staticNodesJson) this.bdkFile.copyStaticNodesJsonToPermissionedNodesJson() + + // Process validator node + const validatorDockerComposeYaml = new ValidatorDockerComposeYaml() for (let i = 0; i < networkCreateConfig.validatorNumber; i++) { this.bdkFile.copyGenesisJsonToValidator(i) this.bdkFile.copyStaticNodesJsonToValidator(i) @@ -95,28 +107,304 @@ export default class Network extends AbstractService { this.bdkFile.copyPublicKeyToValidator(i) this.bdkFile.copyAddressToValidator(i) - validatorDockerComposeYaml.addValidator(this.bdkFile.getBdkPath(), i, 8545 + i) + validatorDockerComposeYaml.addValidator(bdkPath, i, 8545 + i * 2, networkCreateConfig.chainId, 30303 + i) } this.bdkFile.createValidatorDockerComposeYaml(validatorDockerComposeYaml) await (new ValidatorInstance(this.config, this.infra).up()) - const memberDockerComposeYaml = new MemberDockerComposeYaml() - for (let i = 0; i < networkCreateConfig.memberNumber; i++) { - this.bdkFile.copyGenesisJsonToMember(i) + // Process Member node + if (networkCreateConfig.memberNumber > 0) { + const memberDockerComposeYaml = new MemberDockerComposeYaml() + for (let i = 0; i < networkCreateConfig.memberNumber; i++) { + this.bdkFile.copyGenesisJsonToMember(i) + this.bdkFile.copyStaticNodesJsonToMember(i) + this.bdkFile.copyPermissionedNodesJsonToMember(i) + + this.bdkFile.copyPrivateKeyToMember(i) + this.bdkFile.copyPublicKeyToMember(i) + this.bdkFile.copyAddressToMember(i) + + memberDockerComposeYaml.addMember(bdkPath, i, 8645 + i * 2, networkCreateConfig.chainId, 30403 + i) + } + this.bdkFile.createMemberDockerComposeYaml(memberDockerComposeYaml) + + await (new MemberInstance(this.config, this.infra).up()) + } + } + + public async joinNode (joinNodeConfig: JoinNodeType) { + const nodeNum = Number(joinNodeConfig.node.replace(/(validator|member)+/g, '')) + const staticNodesJson = [] + const bdkPath = this.bdkFile.getBdkPath() + const enodeInfo = String(this.getNodeInfo(joinNodeConfig.node, 'enodeInfo')) + const publicKey = String(this.getNodeInfo(joinNodeConfig.node, 'publicKey')) + + for (let i = 0; i < joinNodeConfig.staticNodesJson.length; i++) { + if (joinNodeConfig.staticNodesJson[i].includes(publicKey)) { + staticNodesJson.push(enodeInfo) + } else { + staticNodesJson.push(joinNodeConfig.staticNodesJson[i].replace(/(validator|member)[0-9]+/g, joinNodeConfig.ipAddress)) + } + } + + this.bdkFile.createArtifactsFolder() + this.bdkFile.createGenesisJson(joinNodeConfig.genesisJson) + this.bdkFile.createDisallowedNodesJson([]) + this.bdkFile.createStaticNodesJson(staticNodesJson) + this.bdkFile.copyStaticNodesJsonToPermissionedNodesJson() + + if (joinNodeConfig.node.includes('validator')) { + this.bdkFile.copyGenesisJsonToValidator(nodeNum) + this.bdkFile.copyStaticNodesJsonToValidator(nodeNum) + this.bdkFile.copyPermissionedNodesJsonToValidator(nodeNum) + + const validatorDockerComposeYaml = new ValidatorDockerComposeYaml() + validatorDockerComposeYaml.addValidator(bdkPath, nodeNum, 8545 + nodeNum * 2, joinNodeConfig.genesisJson.config.chainId, 30303 + nodeNum) + this.bdkFile.createValidatorDockerComposeYaml(validatorDockerComposeYaml) + + await (new ValidatorInstance(this.config, this.infra).upOneService(`${joinNodeConfig.node}`)) + + let tryTime = 0 + while (await this.quorumCommand('istanbul.isValidator()', `${joinNodeConfig.node}`) !== 'true') { + if (tryTime !== 10) { + const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545') + const wallet = ethers.Wallet.createRandom().connect(provider) + const tx = { + to: '0x0000000000000000000000000000000000000000', + value: '0x0', + nonce: provider.getTransactionCount(wallet.getAddress(), 'latest'), + } + const receipt = await wallet.sendTransaction(tx) + await receipt.wait() + + tryTime += 1 + await sleep(500) + } else { + throw new TimeLimitError('[x] Time limit reached. Please check later.') + } + } + return nodeNum + } else { + this.bdkFile.copyGenesisJsonToMember(nodeNum) + this.bdkFile.copyStaticNodesJsonToMember(nodeNum) + this.bdkFile.copyPermissionedNodesJsonToMember(nodeNum) + + const memberDockerComposeYaml = new MemberDockerComposeYaml() + memberDockerComposeYaml.addMember(bdkPath, nodeNum, 8645 + nodeNum * 2, joinNodeConfig.genesisJson.config.chainId, 30403 + nodeNum) + this.bdkFile.createMemberDockerComposeYaml(memberDockerComposeYaml) + + await (new MemberInstance(this.config, this.infra).upOneService(`${joinNodeConfig.node}`)) + + let tryTime = 0 + while (parseInt(await this.quorumCommand('net.peerCount', joinNodeConfig.node)) < 1) { + if (tryTime !== 10) { + tryTime += 1 + await sleep(500) + } else { + throw new TimeLimitError('[x] Time limit reached. Please check later.') + } + } + return nodeNum + } + } + + public async addValidatorRemote (addValidatorRemoteConfig: AddValidatorRemoteType) { + const validatorCount = await this.bdkFile.getExportFiles().filter(file => file.match(/(validator)[0-9]+/g)).length + + // propose + for (let i = 0; i < validatorCount; i++) { + await this.quorumCommand(`istanbul.propose("${addValidatorRemoteConfig.validatorAddress}", true)`, 'validator' + i) + } + + const staticNodesJson = this.bdkFile.getStaticNodesJson() + const remoteValidatorNode = `enode://${addValidatorRemoteConfig.validatorPublicKey}@${addValidatorRemoteConfig.ipAddress}:${addValidatorRemoteConfig.discoveryPort}` + staticNodesJson.push(remoteValidatorNode) + + this.bdkFile.createStaticNodesJson(staticNodesJson) + this.bdkFile.copyStaticNodesJsonToPermissionedNodesJson() + + // for loop to copy static-nodes.json to validator + for (let i = 0; i < validatorCount; i++) { + this.bdkFile.copyStaticNodesJsonToValidator(i) + this.bdkFile.copyPermissionedNodesJsonToValidator(i) + } + + // for loop to copy static-nodes.json to member + const memberCount = await this.bdkFile.getExportFiles().filter(file => file.match(/(member)[0-9]+/g)).length + for (let i = 0; i < memberCount; i++) { this.bdkFile.copyStaticNodesJsonToMember(i) this.bdkFile.copyPermissionedNodesJsonToMember(i) + } + } + public async addMemberRemote (addMemberRemoteConfig: AddMemberRemoteType) { + const staticNodesJson = this.bdkFile.getStaticNodesJson() + const remoteMemberNode = `enode://${addMemberRemoteConfig.memberPublicKey}@${addMemberRemoteConfig.ipAddress}:${addMemberRemoteConfig.discoveryPort}` + staticNodesJson.push(remoteMemberNode) + + this.bdkFile.createStaticNodesJson(staticNodesJson) + this.bdkFile.copyStaticNodesJsonToPermissionedNodesJson() + + // for loop to copy static-nodes.json to validator + const validatorCount = await this.bdkFile.getExportFiles().filter(file => file.match(/(validator)[0-9]+/g)).length + for (let i = 0; i < validatorCount; i++) { + this.bdkFile.copyStaticNodesJsonToValidator(i) + this.bdkFile.copyPermissionedNodesJsonToValidator(i) + } + + // for loop to copy static-nodes.json to member + const memberCount = await this.bdkFile.getExportFiles().filter(file => file.match(/(member)[0-9]+/g)).length + for (let i = 0; i < memberCount; i++) { + this.bdkFile.copyStaticNodesJsonToMember(i) + this.bdkFile.copyPermissionedNodesJsonToMember(i) + } + } + + public generate (networkGenerateConfig: NetworkGenerateType) { + const staticNodesJson = [] + + // Add node to static-nodes.json + for (let i = 0; i < networkGenerateConfig.validatorNumber; i++) { + const { publicKey } = this.createKey(`artifacts/validator${i}`) + const validatorNode = `enode://${publicKey}@validator${i}:` + (30303 + i) + staticNodesJson.push(validatorNode) + } + for (let i = 0; i < networkGenerateConfig.memberNumber; i++) { + const { publicKey } = this.createKey(`artifacts/member${i}`) + const memberNode = `enode://${publicKey}@member${i}:` + (30403 + i) + staticNodesJson.push(memberNode) + } + this.bdkFile.createStaticNodesJson(staticNodesJson) + + // Process validator & member node + for (let i = 0; i < networkGenerateConfig.validatorNumber; i++) { + this.bdkFile.copyPrivateKeyToValidator(i) + this.bdkFile.copyPublicKeyToValidator(i) + this.bdkFile.copyAddressToValidator(i) + } + for (let i = 0; i < networkGenerateConfig.memberNumber; i++) { this.bdkFile.copyPrivateKeyToMember(i) this.bdkFile.copyPublicKeyToMember(i) this.bdkFile.copyAddressToMember(i) + } + } + + public async addValidatorLocal () { + // count validator number + const validatorCount = parseInt(await this.quorumCommand('istanbul.getValidators().length', 'validator0')) + const validatorNum = validatorCount + const newValidator = 'validator' + validatorNum + const { publicKey, address } = this.createKey(`artifacts/${newValidator}`) + const validatorNode = `enode://${publicKey}@${newValidator}:` + (30303 + validatorNum) + const chainId = parseInt(await this.quorumCommand('admin.nodeInfo.protocols.eth.network', 'validator0')) + + this.bdkFile.copyPrivateKeyToValidator(validatorNum) + this.bdkFile.copyPublicKeyToValidator(validatorNum) + this.bdkFile.copyAddressToValidator(validatorNum) + + for (let i = 0; i < validatorNum; i++) { + await this.quorumCommand(`istanbul.propose("0x${address}", true)`, 'validator' + i) + } + + this.bdkFile.copyGenesisJsonToValidator(validatorNum) + + // read & add new node to static-nodes.json + const staticNodesJson = this.bdkFile.getStaticNodesJson() + staticNodesJson.push(validatorNode) + this.bdkFile.createStaticNodesJson(staticNodesJson) + this.bdkFile.copyStaticNodesJsonToPermissionedNodesJson() - memberDockerComposeYaml.addMember(bdkPath, i, 8645 + i) + // for loop to copy static-nodes.json to validator + const validatorDockerComposeYaml = new ValidatorDockerComposeYaml() + for (let i = 0; i < validatorNum + 1; i++) { + this.bdkFile.copyStaticNodesJsonToValidator(i) + this.bdkFile.copyPermissionedNodesJsonToValidator(i) + validatorDockerComposeYaml.addValidator(this.bdkFile.getBdkPath(), i, 8545 + i * 2, chainId, 30303 + i) + } + this.bdkFile.createValidatorDockerComposeYaml(validatorDockerComposeYaml) + + // for loop to copy static-nodes.json to member + const memberCount = await this.bdkFile.getExportFiles().filter(file => file.match(/(member)[0-9]+/g)).length + for (let i = 0; i < memberCount; i++) { + this.bdkFile.copyStaticNodesJsonToMember(i) + this.bdkFile.copyPermissionedNodesJsonToMember(i) + } + + await (new ValidatorInstance(this.config, this.infra).upOneService(`${newValidator}`)) + + let tryTime = 0 + while (await this.quorumCommand('istanbul.isValidator()', `${newValidator}`) !== 'true') { + if (tryTime !== 10) { + const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545') + const wallet = ethers.Wallet.createRandom().connect(provider) + const tx = { + to: '0x0000000000000000000000000000000000000000', + value: '0x0', + nonce: provider.getTransactionCount(wallet.getAddress(), 'latest'), + } + const receipt = await wallet.sendTransaction(tx) + await receipt.wait() + + tryTime += 1 + await sleep(500) + } else { + throw new TimeLimitError('[x] Time limit reached. Please check later.') + } + } + return validatorNum + } + + public async addMemberLocal () { + // count member number + const memberCount = (await this.bdkFile.getExportFiles().filter(file => file.match(/(member)[0-9]+/g))).length + const memberNum = memberCount + const newMember = 'member' + memberNum + const { publicKey } = this.createKey(`artifacts/${newMember}`) + const memberNode = `enode://${publicKey}@${newMember}:` + (30403 + memberNum) + const chainId = parseInt(await this.quorumCommand('admin.nodeInfo.protocols.eth.network', 'validator0')) + + this.bdkFile.copyPrivateKeyToMember(memberNum) + this.bdkFile.copyPublicKeyToMember(memberNum) + this.bdkFile.copyAddressToMember(memberNum) + + this.bdkFile.copyGenesisJsonToMember(memberNum) + + // read & add new node to static-nodes.json + const staticNodesJson = this.bdkFile.getStaticNodesJson() + staticNodesJson.push(memberNode) + this.bdkFile.createStaticNodesJson(staticNodesJson) + this.bdkFile.copyStaticNodesJsonToPermissionedNodesJson() + + // for loop to copy static-nodes.json to validator + const validatorCount = parseInt(await this.quorumCommand('istanbul.getValidators().length', 'validator0')) + for (let i = 0; i < validatorCount; i++) { + this.bdkFile.copyStaticNodesJsonToValidator(i) + this.bdkFile.copyPermissionedNodesJsonToValidator(i) + } + + // for loop to copy static-nodes.json to member + const memberDockerComposeYaml = new MemberDockerComposeYaml() + for (let i = 0; i < memberCount + 1; i++) { + this.bdkFile.copyStaticNodesJsonToMember(i) + this.bdkFile.copyPermissionedNodesJsonToMember(i) + memberDockerComposeYaml.addMember(this.bdkFile.getBdkPath(), i, 8645 + i * 2, chainId, 30403 + i) } this.bdkFile.createMemberDockerComposeYaml(memberDockerComposeYaml) - await (new MemberInstance(this.config, this.infra).up()) - // TODO: check quorum network create successfully + await (new MemberInstance(this.config, this.infra).upOneService(`${newMember}`)) + + let tryTime = 0 + while (parseInt(await this.quorumCommand('net.peerCount', newMember)) < 1) { + if (tryTime !== 10) { + tryTime += 1 + await sleep(500) + } else { + throw new TimeLimitError('[x] Time limit reached. Please check later.') + } + } + + return memberNum } public async upService (service: string) { @@ -139,7 +427,97 @@ export default class Network extends AbstractService { public async delete () { await this.down() - this.removeBdkFiles(this.getBdkFiles()) + this.removeBdkFiles(this.getNetworkFiles()) + } + + public async checkNode (node: string, method: string) { + const result = await this.quorumCommand(method, node) + + return result + } + + public getNetworkInfo (networkInfo: string) { + let result + + switch (networkInfo) { + case 'genesis.json': + result = JSON.stringify(this.bdkFile.getGenesisJson()) + break + + case 'static-nodes.json': + result = JSON.stringify(this.bdkFile.getStaticNodesJson()) + break + + case 'permissioned-nodes.json': + result = JSON.stringify(this.bdkFile.getPermissionedNodesJson()) + break + } + + return result + } + + public getNodeInfo (node: string, nodeInfo: string) { + let result + const nodeNum = Number(node.replace(/(validator|member)+/g, '')) + + if (node.includes('validator')) { + switch (nodeInfo) { + case 'address': + result = this.bdkFile.getValidatorAddress(nodeNum) + break + + case 'publicKey': + result = this.bdkFile.getValidatorPublicKey(nodeNum) + break + + case 'privateKey': + result = this.bdkFile.getValidatorPrivateKey(nodeNum) + break + case 'enodeInfo': + result = this.bdkFile.getValidatorEnodeInfo(nodeNum) + break + } + } else if (node.includes('member')) { + switch (nodeInfo) { + case 'address': + result = this.bdkFile.getMemberAddress(nodeNum) + break + + case 'publicKey': + result = this.bdkFile.getMemberPublicKey(nodeNum) + break + + case 'privateKey': + result = this.bdkFile.getMemberPrivateKey(nodeNum) + break + case 'enodeInfo': + result = this.bdkFile.getMemberEnodeInfo(nodeNum) + break + } + } + + return result + } + + /** @ignore */ + private async quorumCommand (args: string, option: string) { + const result = await this.infra.runCommand({ + autoRemove: true, + user: false, + image: 'quorumengineering/quorum', + tag: '22.7.4', + volumes: [`${this.bdkFile.getBdkPath()}/${option}/data/geth.ipc:/root/geth.ipc`], + commands: [ + 'attach', + '/root/geth.ipc', + '--exec', + args, + ], + ignoreError: true, + }) as DockerResultType + // strip ANSI color + const out = result.stdout.replace(/\s+/g, '').replace(/.\[[0-9;]*m/g, '') + return out } /** @ignore */ @@ -168,11 +546,26 @@ export default class Network extends AbstractService { return { privateKey, publicKey, address } } + /** @ignore */ + public createBdkFolder () { + return this.bdkFile.createBdkFolder() + } + /** @ignore */ public getBdkFiles () { return this.bdkFile.getExportFiles() } + /** @ignore */ + public getNetworkFiles () { + const array = [] + array.push(this.bdkFile.getExportFiles().filter(file => file.match(/(validator|member)+/g))) + array.push(this.bdkFile.getExportFiles().filter(file => file.match(/(artifacts)/g))) + const networkFilesList = array.flat() + + return networkFilesList + } + /** @ignore */ public getUpExportItems () { const node = this.bdkFile.getExportFiles().filter(file => file.match(/(validator|member)[0-9]+/g)) diff --git a/src/util/error.ts b/src/util/error.ts index 4dda8102..e6bc0fb4 100644 --- a/src/util/error.ts +++ b/src/util/error.ts @@ -8,6 +8,8 @@ export class ProcessError extends BdkError {} export class ParamsError extends BdkError {} export class DockerError extends BdkError {} export class BackupError extends BdkError {} +export class TimeLimitError extends BdkError {} +export class PathError extends BdkError {} export class FabricContainerError extends BdkError { public stdout: string constructor (message: string, stdout: string) { @@ -15,6 +17,13 @@ export class FabricContainerError extends BdkError { this.stdout = stdout } } +export class QuorumContainerError extends BdkError { + public stdout: string + constructor (message: string, stdout: string) { + super(message) + this.stdout = stdout + } +} export const onCancel = (prompt: prompts.PromptObject, answers: any) => { config.isDevMode && console.log(prompt) @@ -26,6 +35,8 @@ export const onCancel = (prompt: prompts.PromptObject, answers: any) => export const errorHandler = (err: Error) => { if (err instanceof FabricContainerError) { logger.error(err.message) + } else if (err instanceof QuorumContainerError) { + logger.error(err.message) } else if (err instanceof BdkError) { logger.error(err.message) } else { diff --git a/src/util/utils.ts b/src/util/utils.ts index 0974882d..5945f77d 100644 --- a/src/util/utils.ts +++ b/src/util/utils.ts @@ -26,16 +26,5 @@ export function tarDateFormat (date: Date): string { } export const randomFromArray = (x: Array) => x[Math.floor(Math.random() * x.length)] -// export function iterateObject2 (obj: Map, callback: (x: string) => any) { -// for (const key in obj) { -// if (typeof obj[key] === 'object') { -// iterateObject(obj[key], callback) -// if (callback(key) !== key) { -// obj[callback(key)] = obj[key] -// delete obj[key] -// } -// } else { -// obj[key] = callback(obj[key]) -// } -// } -// } + +export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) diff --git a/test/fabric/service/caService.test.ts b/test/fabric/service/caService.test.ts index c80aed09..aff5664c 100644 --- a/test/fabric/service/caService.test.ts +++ b/test/fabric/service/caService.test.ts @@ -1,4 +1,4 @@ -/* global describe, it, before, after, beforeEach */ +/* global describe, it, before, after */ import fs from 'fs' import assert from 'assert' import net from 'net' diff --git a/tsconfig.json b/tsconfig.json index 4667106a..8696597d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ // "incremental": true, /* Enable incremental compilation */ "target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - "lib": ["es6"], /* Specify library files to be included in the compilation. */ + "lib": ["es2019"], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */