From a5b13f8a7b48f1c4caec9cb20b0fd20cc3b007e2 Mon Sep 17 00:00:00 2001 From: tilacog Date: Mon, 16 Oct 2023 16:59:27 -0300 Subject: [PATCH 01/16] *: merge latest main branch --- .github/workflows/check-formatting.yml | 3 +- .github/workflows/ci.yml | 17 +- .github/workflows/publish-native-binaries.yml | 11 +- docs/action-queue.md | 2 +- docs/feature-support-matrix.md | 70 +- docs/networks.md | 12 +- .../{mainnet.md => arbitrum-goerli.md} | 103 +- docs/networks/arbitrum-one.md | 79 + .../{testnet.md => ethereum-goerli.md} | 20 +- docs/networks/ethereum-mainnet.md | 79 + docs/subgraph-freshness.md | 56 + lerna.json | 2 +- package.json | 6 +- packages/indexer-agent/CHANGELOG.md | 75 +- packages/indexer-agent/README.md | 204 +- packages/indexer-agent/jest.config.js | 7 +- packages/indexer-agent/package.json | 43 +- .../indexer-agent/src/__tests__/indexer.ts | 160 +- packages/indexer-agent/src/agent.ts | 1087 ++- .../src/commands/common-options.ts | 170 + .../src/commands/error-handling.ts | 53 + .../src/commands/start-multi-network.ts | 130 + packages/indexer-agent/src/commands/start.ts | 1049 ++- packages/indexer-agent/src/cost.ts | 43 +- .../11-add-protocol-network-field.ts | 454 ++ .../migrations/11-update-deployment-names.ts | 120 - packages/indexer-agent/src/index.ts | 87 +- packages/indexer-agent/src/indexer.ts | 938 --- packages/indexer-agent/src/syncing-server.ts | 72 +- packages/indexer-agent/src/types.ts | 4 +- .../src/utils/balance-monitor.ts | 39 - packages/indexer-agent/src/utils/index.ts | 1 - packages/indexer-cli/CHANGELOG.md | 17 +- packages/indexer-cli/README.md | 61 +- packages/indexer-cli/jest.config.js | 17 +- packages/indexer-cli/package.json | 24 +- .../src/__tests__/indexer/actions.test.ts | 2 + .../src/__tests__/indexer/cost.test.ts | 7 +- .../src/__tests__/indexer/rules.test.ts | 321 +- .../indexer-actions-get-first.stdout | 10 +- .../references/indexer-actions-get.stdout | 14 +- .../indexer-rule-deployment-always.stdout | 10 +- ...deployment-deleted-offchain-success.stdout | 2 +- ...xer-rule-deployment-deleted-success.stdout | 2 +- .../indexer-rule-deployment-lifetime.stdout | 10 +- .../indexer-rule-deployment-never.stdout | 10 +- .../indexer-rule-deployment-offchain.stdout | 10 +- .../indexer-rule-deployment-rules.stdout | 10 +- .../indexer-rule-deployment-safety.stdout | 10 +- .../indexer-rule-deployment-supported.stdout | 10 +- .../indexer-rule-deployment-yaml.stdout | 1 + .../indexer-rule-global-rules.stdout | 10 +- .../indexer-rule-subgraph-offchain.stdout | 10 +- .../indexer-rule-subgraph-options.stdout | 10 +- .../indexer-rule-subgraph-rules.stdout | 10 +- .../indexer-rules-command-no-args.stdout | 12 +- .../indexer-rules-no-identifier.stderr | 1 + .../indexer-rules-no-network.stderr | 1 + packages/indexer-cli/src/__tests__/util.ts | 211 +- packages/indexer-cli/src/actions.ts | 50 +- packages/indexer-cli/src/allocations.ts | 39 +- packages/indexer-cli/src/command-helpers.ts | 45 + .../src/commands/indexer/actions/approve.ts | 35 +- .../src/commands/indexer/actions/cancel.ts | 11 +- .../src/commands/indexer/actions/execute.ts | 10 +- .../src/commands/indexer/actions/get.ts | 25 +- .../src/commands/indexer/actions/queue.ts | 25 +- .../src/commands/indexer/actions/update.ts | 13 +- .../src/commands/indexer/allocations/close.ts | 44 +- .../commands/indexer/allocations/create.ts | 30 +- .../src/commands/indexer/allocations/get.ts | 10 +- .../indexer/allocations/reallocate.ts | 21 +- .../src/commands/indexer/disputes/get.ts | 14 +- .../src/commands/indexer/rules/clear.ts | 9 +- .../src/commands/indexer/rules/delete.ts | 84 +- .../src/commands/indexer/rules/get.ts | 27 +- .../src/commands/indexer/rules/maybe.ts | 9 +- .../src/commands/indexer/rules/offchain.ts | 10 +- .../src/commands/indexer/rules/set.ts | 7 +- .../src/commands/indexer/rules/start.ts | 9 +- .../src/commands/indexer/rules/stop.ts | 9 +- .../src/commands/indexer/status.ts | 95 +- packages/indexer-cli/src/cost.ts | 18 +- packages/indexer-cli/src/disputes.ts | 20 +- packages/indexer-cli/src/rules.ts | 26 +- packages/indexer-common/CHANGELOG.md | 38 +- packages/indexer-common/jest.config.js | 5 +- packages/indexer-common/package.json | 58 +- .../invalid-address.yml | 34 + .../invalid-base58.yml | 34 + .../invalid-epoch-subgraph.yml | 35 + .../invalid-extra-field.yml | 36 + .../invalid-missing-field.yml | 35 + .../invalid-negative-max-block-distance.yml | 35 + .../invalid-network-identifier.yml | 35 + .../valid-missing.yml | 30 + .../network-specification-files/valid.yml | 34 + .../__tests__/network-specification.test.ts | 106 + .../src/__tests__/subgraph.test.ts | 258 + .../indexer-common/src/__tests__/subgraphs.ts | 88 - packages/indexer-common/src/actions.ts | 22 +- .../src/allocations/query-fees.test.ts | 53 +- .../src/allocations/query-fees.ts | 287 +- packages/indexer-common/src/epoch-subgraph.ts | 29 +- packages/indexer-common/src/errors.ts | 5 +- packages/indexer-common/src/graph-node.ts | 655 ++ packages/indexer-common/src/index.ts | 12 +- .../__tests__/allocations.test.ts | 169 + .../__tests__/{helpers.ts => helpers.test.ts} | 104 +- .../resolvers/{actions.ts => actions.test.ts} | 332 +- .../{cost-models.ts => cost-models.test.ts} | 112 +- ...dexing-rules.ts => indexing-rules.test.ts} | 261 +- .../{poi-disputes.ts => poi-disputes.test.ts} | 193 +- .../src/indexer-management/__tests__/util.ts | 188 + .../src/indexer-management/actions.ts | 236 +- .../src/indexer-management/allocations.ts | 541 +- .../src/indexer-management/client.ts | 208 +- .../src/indexer-management/index.ts | 1 - .../src/indexer-management/models/action.ts | 10 + .../models/indexing-rule.ts | 21 +- .../indexer-management/models/poi-dispute.ts | 19 + .../src/indexer-management/monitor.ts | 623 +- .../indexer-management/resolvers/actions.ts | 67 +- .../resolvers/allocations.ts | 520 +- .../resolvers/cost-models.ts | 16 +- .../resolvers/indexer-status.ts | 269 +- .../resolvers/indexing-rules.ts | 151 +- .../resolvers/poi-disputes.ts | 84 +- .../src/indexer-management/resolvers/utils.ts | 26 + .../src/indexer-management/rules.ts | 27 +- .../src/indexer-management/types.ts | 93 +- .../indexer-common/src/indexing-status.ts | 357 - packages/indexer-common/src/multi-networks.ts | 111 + .../src/network-specification.ts | 174 + .../indexer-common/src/network-subgraph.ts | 60 +- packages/indexer-common/src/network.ts | 829 +- packages/indexer-common/src/operator.ts | 583 ++ .../src/parsers/__tests__/parsers.ts | 10 + .../indexer-common/src/parsers/basic-types.ts | 34 + packages/indexer-common/src/parsers/index.ts | 1 + .../indexer-common/src/parsers/validators.ts | 30 + .../src/query-fees/allocation-utils.ts | 2 + .../indexer-common/src/query-fees/models.ts | 51 + packages/indexer-common/src/rules.ts | 2 + packages/indexer-common/src/subgraphs.ts | 216 +- packages/indexer-common/src/transactions.ts | 161 +- packages/indexer-common/src/types.ts | 86 +- packages/indexer-common/src/utils.ts | 96 + packages/indexer-common/tsconfig.json | 5 +- packages/indexer-native/lib/index.js | 8 +- packages/indexer-native/lib/index.test.js | 10 +- packages/indexer-native/native/Cargo.lock | 389 +- packages/indexer-native/native/Cargo.toml | 12 +- .../indexer-native/native/src/attestation.rs | 2 + packages/indexer-native/native/src/lib.rs | 2 +- .../native/src/signature_verification.rs | 4 +- packages/indexer-native/package.json | 8 +- packages/indexer-service/CHANGELOG.md | 44 +- packages/indexer-service/README.md | 34 +- packages/indexer-service/jest.config.js | 19 +- packages/indexer-service/package.json | 61 +- packages/indexer-service/src/allocations.ts | 11 +- .../indexer-service/src/commands/start.ts | 136 +- .../src/query-fees/allocations.ts | 45 +- .../src/server/__tests__/server.test.ts | 4 +- packages/indexer-service/src/server/status.ts | 14 +- yarn.lock | 7025 ++++++++--------- 167 files changed, 14008 insertions(+), 9527 deletions(-) rename docs/networks/{mainnet.md => arbitrum-goerli.md} (60%) create mode 100644 docs/networks/arbitrum-one.md rename docs/networks/{testnet.md => ethereum-goerli.md} (91%) create mode 100644 docs/networks/ethereum-mainnet.md create mode 100644 docs/subgraph-freshness.md create mode 100644 packages/indexer-agent/src/commands/common-options.ts create mode 100644 packages/indexer-agent/src/commands/error-handling.ts create mode 100644 packages/indexer-agent/src/commands/start-multi-network.ts create mode 100644 packages/indexer-agent/src/db/migrations/11-add-protocol-network-field.ts delete mode 100644 packages/indexer-agent/src/db/migrations/11-update-deployment-names.ts delete mode 100644 packages/indexer-agent/src/indexer.ts delete mode 100644 packages/indexer-agent/src/utils/balance-monitor.ts delete mode 100644 packages/indexer-agent/src/utils/index.ts create mode 100644 packages/indexer-cli/src/__tests__/references/indexer-rules-no-identifier.stderr create mode 100644 packages/indexer-cli/src/__tests__/references/indexer-rules-no-network.stderr create mode 100644 packages/indexer-common/src/__tests__/network-specification-files/invalid-address.yml create mode 100644 packages/indexer-common/src/__tests__/network-specification-files/invalid-base58.yml create mode 100644 packages/indexer-common/src/__tests__/network-specification-files/invalid-epoch-subgraph.yml create mode 100644 packages/indexer-common/src/__tests__/network-specification-files/invalid-extra-field.yml create mode 100644 packages/indexer-common/src/__tests__/network-specification-files/invalid-missing-field.yml create mode 100644 packages/indexer-common/src/__tests__/network-specification-files/invalid-negative-max-block-distance.yml create mode 100644 packages/indexer-common/src/__tests__/network-specification-files/invalid-network-identifier.yml create mode 100644 packages/indexer-common/src/__tests__/network-specification-files/valid-missing.yml create mode 100644 packages/indexer-common/src/__tests__/network-specification-files/valid.yml create mode 100644 packages/indexer-common/src/__tests__/network-specification.test.ts create mode 100644 packages/indexer-common/src/__tests__/subgraph.test.ts delete mode 100644 packages/indexer-common/src/__tests__/subgraphs.ts create mode 100644 packages/indexer-common/src/graph-node.ts create mode 100644 packages/indexer-common/src/indexer-management/__tests__/allocations.test.ts rename packages/indexer-common/src/indexer-management/__tests__/{helpers.ts => helpers.test.ts} (76%) rename packages/indexer-common/src/indexer-management/__tests__/resolvers/{actions.ts => actions.test.ts} (74%) rename packages/indexer-common/src/indexer-management/__tests__/resolvers/{cost-models.ts => cost-models.test.ts} (91%) rename packages/indexer-common/src/indexer-management/__tests__/resolvers/{indexing-rules.ts => indexing-rules.test.ts} (76%) rename packages/indexer-common/src/indexer-management/__tests__/resolvers/{poi-disputes.ts => poi-disputes.test.ts} (69%) create mode 100644 packages/indexer-common/src/indexer-management/__tests__/util.ts create mode 100644 packages/indexer-common/src/indexer-management/resolvers/utils.ts delete mode 100644 packages/indexer-common/src/indexing-status.ts create mode 100644 packages/indexer-common/src/multi-networks.ts create mode 100644 packages/indexer-common/src/network-specification.ts create mode 100644 packages/indexer-common/src/operator.ts create mode 100644 packages/indexer-common/src/parsers/__tests__/parsers.ts create mode 100644 packages/indexer-common/src/parsers/basic-types.ts create mode 100644 packages/indexer-common/src/parsers/index.ts create mode 100644 packages/indexer-common/src/parsers/validators.ts diff --git a/.github/workflows/check-formatting.yml b/.github/workflows/check-formatting.yml index 4f86c94d7..eecb6356c 100644 --- a/.github/workflows/check-formatting.yml +++ b/.github/workflows/check-formatting.yml @@ -3,8 +3,7 @@ name: "Check Formatting" on: push: branches: [main] - pull_request: - branches: [main] + pull_request: {} jobs: check: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfdcb8052..c3840a5e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,20 +6,15 @@ name: CI on: push: branches: [main] - pull_request: - branches: [main] + pull_request: {} jobs: build: strategy: matrix: - node-version: [14, 16, 17] + node-version: [16, 18] system: - - os: ubuntu-20.04 - include: - - node_version: 18 - system: - os: ubuntu-22.04 + - os: ubuntu-22.04 runs-on: ${{ matrix.system.os }} services: postgres: @@ -38,6 +33,10 @@ jobs: --health-retries 5 steps: - uses: actions/checkout@v2 + - name: update OS + run: | + sudo apt-get update + sudo apt install -y --no-install-recommends gcc g++ make build-essential - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: @@ -52,5 +51,5 @@ jobs: POSTGRES_TEST_DATABASE: indexer_tests POSTGRES_TEST_USERNAME: testuser POSTGRES_TEST_PASSWORD: testpass - NODE_OPTIONS: '--dns-result-order=ipv4first' + NODE_OPTIONS: "--dns-result-order=ipv4first" INDEXER_TEST_JRPC_PROVIDER_URL: ${{ secrets.TESTS_RPC_PROVIDER }} diff --git a/.github/workflows/publish-native-binaries.yml b/.github/workflows/publish-native-binaries.yml index 3638b7903..96b2ed34c 100644 --- a/.github/workflows/publish-native-binaries.yml +++ b/.github/workflows/publish-native-binaries.yml @@ -4,9 +4,11 @@ name: "Publish binaries" on: + workflow_dispatch: release: types: - created + - published jobs: publish-github: @@ -15,11 +17,11 @@ jobs: # (https://github.com/bchr02/node-pre-gyp-github/issues/42) fail-fast: false matrix: - node_version: [14, 16, 17] + node_version: [16, 17, 18, 19, 20] system: - os: macos-latest target: x86_64-apple-darwin - - os: ubuntu-18.04 + - os: ubuntu-20.04 target: x86_64-unknown-linux-gnu include: - node_version: 18 @@ -34,6 +36,11 @@ jobs: steps: - name: Checkout the repo uses: actions/checkout@v2 + - name: update OS + run: | + sudo apt-get update + sudo apt install -y --no-install-recommends gcc g++ make build-essential + if: ${{ runner.os == 'Linux' }} - name: Set up Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2.1.5 with: diff --git a/docs/action-queue.md b/docs/action-queue.md index 35eade859..5a882c4d7 100644 --- a/docs/action-queue.md +++ b/docs/action-queue.md @@ -41,7 +41,7 @@ Local usage from source # Queue allocate action (allocateFrom()) ./bin/graph-indexer indexer actions queue allocate QmeqJ6hsdyk9dVbo1tvRgAxWrVS3rkERiEMsxzPShKLco6 5000 -# Queue reallocate action (closeAndAllocate()) +# Queue reallocate action (close and allocate using multicall()) ./bin/graph-indexer indexer actions queue reallocate QmeqJ6hsdyk9dVbo1tvRgAxWrVS3rkERiEMsxzPShKLco6 0x4a58d33e27d3acbaecc92c15101fbc82f47c2ae5 55000 # Queue unallocate action (closeAllocation()) diff --git a/docs/feature-support-matrix.md b/docs/feature-support-matrix.md index d0f1502b9..86c919a66 100644 --- a/docs/feature-support-matrix.md +++ b/docs/feature-support-matrix.md @@ -1,41 +1,49 @@ # Feature support matrix -As described in [GIP-0008](https://snapshot.org/#/council.graphprotocol.eth/proposal/0xbdd884654a393620a7e8665b4289201b7542c3ee62becfad133e951b0c408444), this defines indexing & querying features which are experimental or not fully supported for indexing & query rewards and arbitration. - -Each deployment of The Graph Network has its own specific Feature support matrix, as features will be introduced to testnet & mainnet at different stages of the development and testing lifecycle. - -An example: - -| Subgraph Feature | Aliases | Implemented | Experimental | Query Arbitration | Indexing Arbitration | Indexing Rewards | -|--------------------------|---------|-------------|--------------|-------------------|----------------------|------------------| -| **Core Features** | | | | | | | -| Full-text Search | | Yes | No | No | Yes | Yes | -| Non-Fatal Errors | | Yes | Yes | Yes | Yes | Yes | -| Grafting | | Yes | Yes | Yes | Yes | Yes | -| **Data Source Types** | | | | | | | -| eip155:* | * | Yes | No | No | No | No | -| eip155:1 | mainnet | Yes | No | Yes | Yes | Yes | -| eip155:100 | gnosis | Yes | No | Yes | Yes | Yes | -| near:* | * | Yes | Yes | No | No | No | -| cosmos:* | * | Yes | Yes | No | No | No | -| arweave:* | * | Yes | Yes | No | No | No | -| eip155:42161 | arbitrum-one | Yes | Yes | Yes | Yes | Yes | -| eip155:42220 | celo | Yes | Yes | Yes | Yes | Yes | -| eip155:43114 | avalanche | Yes | Yes | Yes | Yes | Yes | -| **Data Source Features** | | | | | | | -| ipfs.cat in mappings | | Yes | Yes | No | No | No | -| ENS | | Yes | Yes | No | No | No | -| File data sources: IPFS | | Yes | Yes | No | Yes | Yes | - -The accepted `graph-node` version range is also specificied, with an "upgrade window" from the previous version. +As described in [GIP-0008](https://snapshot.org/#/council.graphprotocol.eth/proposal/0xbdd884654a393620a7e8665b4289201b7542c3ee62becfad133e951b0c408444), the Feature support matrix defines indexing & querying features which are experimental or not fully supported for indexing & query rewards and arbitration. + +The matrix below reflects the canonical Council-ratified version. As outlined in GIP-00008, Council ratification is currently required for each update, which may happen at different stages of feature development and testing lifecycle. + + +| Subgraph Feature | Aliases | Implemented | Experimental | Query Arbitration | Indexing Arbitration | Indexing Rewards | +| ------------------------ | ------------- | ----------- | ------------ | ----------------- | -------------------- | ---------------- | +| **Core Features** | | | | | | | +| Full-text Search | | Yes | No | No | Yes | Yes | +| Non-Fatal Errors | | Yes | Yes | Yes | Yes | Yes | +| Grafting | | Yes | Yes | Yes | Yes | Yes | +| **Data Source Types** | | | | | | | +| eip155:* | * | Yes | No | No | No | No | +| eip155:1 | mainnet | Yes | No | Yes | Yes | Yes | +| eip155:100 | gnosis | Yes | Yes | Yes | Yes | Yes | +| near:* | * | Yes | Yes | No | No | No | +| cosmos:* | * | Yes | Yes | No | No | No | +| arweave:* | * | Yes | Yes | No | No | No | +| eip155:42161 | artbitrum-one | Yes | Yes | Yes | Yes | Yes | +| eip155:42220 | celo | Yes | Yes | Yes | Yes | Yes | +| eip155:43114 | avalanche | Yes | Yes | Yes | Yes | Yes | +| eip155:250 | fantom | Yes | Yes | Yes | Yes | Yes | +| eip155:137 | polygon | Yes | Yes | Yes | Yes | Yes | +| **Data Source Features** | | | | | | | +| ipfs.cat in mappings | | Yes | Yes | No | No | No | +| ENS | | Yes | Yes | No | No | No | +| File data sources: IPFS | | Yes | Yes | No | Yes | Yes | +| Substreams data sources | mainnet | Yes | Yes | Yes | Yes | Yes | + +The accepted `graph-node` version range must always be specified; it always comprises the latest available version and the one immediately preceding it. +The latest for the feature matrix above: ``` -graph-node: ≥0.30.0 <0.31.0 -valid from: 787 -upgrade window: 795 +graph-node: >=0.32 <0.33 ``` +### Latest Council snapshot +[GPP-0028 Update Feature Support Matrix (Graph Node v0.32.0)](https://snapshot.org/#/council.graphprotocol.eth/proposal/0xa7dcaf27d3b8cb6c135c398753a82fb4a6fc1ff5ad666cb131109f2de506253c) + + + +### Other notes +- Currently, one single matrix is used to reflect protocol behaviour for both Ethereum mainnet and Arbitrum One. - Aliases can be used in subgraph manifest files to refer to specific networks. - Experimental features are generally not fully supported for indexing rewards and arbitration, and usage of experimental features will be considered during any arbitration that does occur. - Query fees apply to all queries, regardless of the underlying features used by a subgraph. diff --git a/docs/networks.md b/docs/networks.md index dd9d2a649..ee0afc993 100644 --- a/docs/networks.md +++ b/docs/networks.md @@ -2,8 +2,14 @@ > This content has been moved -[Mainnet configuration](./networks/mainnet.md) +[Ethereum Mainnet configuration](./networks/ethereum-mainnet.md) -[Testnet configuration](./networks/testnet.md) +[Arbitrum One configuration](./networks/arbitrum-one.md) -[Testnet setup instructions](./testnet-setup.md) \ No newline at end of file +[Ethereum Goerli configuration](./networks/ethereum-goerli.md) + +[Arbitrum Goerli configuration](./networks/arbitrum-goerli.md) + +[Testnet setup instructions](./testnet-setup.md) + +[Feature Support Matrix (Ethereum & Arbitrum One)](./feature-support-matrix.md) \ No newline at end of file diff --git a/docs/networks/mainnet.md b/docs/networks/arbitrum-goerli.md similarity index 60% rename from docs/networks/mainnet.md rename to docs/networks/arbitrum-goerli.md index 5edf0446e..bcac503f4 100644 --- a/docs/networks/mainnet.md +++ b/docs/networks/arbitrum-goerli.md @@ -1,51 +1,58 @@ -# Mainnet Configuration +# Testnet Configuration -Network information can be found at https://thegraph.com/explorer/network. The Graph Network mainnet is open for everyone to participate in as an indexer. The only requirement is a minimum stake of 100k GRT. +The Graph Network's testnet is on Goerli. Goerli network information can be found at https://testnet.thegraph.com/explorer?chain=arbitrum-goerli. ## Latest Releases -| Component | Release | -| --------------- | -------------------------------------------------------------------------- | -| contracts | [1.11.1](https://github.com/graphprotocol/contracts/releases/tag/v1.11.1) | -| indexer-agent | [0.18.6](https://github.com/graphprotocol/indexer/releases/tag/v0.18.6) | -| indexer-cli | [0.18.6](https://github.com/graphprotocol/indexer/releases/tag/v0.18.6) | -| indexer-service | [0.18.6](https://github.com/graphprotocol/indexer/releases/tag/v0.18.6) | -| graph-node | [0.30.0](https://github.com/graphprotocol/graph-node/releases/tag/v0.30.0) | +| Component | Release | +| --------------- | ------------------------------------------------------------------------------------ | +| contracts | [1.13.0](https://github.com/graphprotocol/contracts/releases/tag/v1.13.0) | +| indexer-agent | [0.20.16](https://github.com/graphprotocol/indexer/releases/tag/v0.20.16) | +| indexer-cli | [0.20.16](https://github.com/graphprotocol/indexer/releases/tag/v0.20.16) | +| indexer-service | [0.20.16](https://github.com/graphprotocol/indexer/releases/tag/v0.20.16) | +| graph-node | [v0.32.0-rc.0](https://github.com/graphprotocol/graph-node/releases/tag/v0.32.0-rc.0)| ## Network Parameters -| Parameter | Value | -| --------------------------- | -------------------- | -| Epoch length | ~ 24h (6646 blocks) | -| Maximum allocation lifetime | ~28 days (28 epochs) | +| Parameter | Value | +| --------------------------- | ----------------- | +| Epoch length | ~ 2h (554 blocks) | +| Maximum allocation lifetime | ~ 9h (4 epochs) | ## Contracts & accounts -| Name | Address | -| ------------------ | ----------------------------------------------------------------------------------------------------------------------- | -| Graph Token (GRT) | [`0xc944E90C64B2c07662A292be6244BDf05Cda44a7`](https://etherscan.io/address/0xc944e90c64b2c07662a292be6244bdf05cda44a7) | -| Staking | [`0xF55041E37E12cD407ad00CE2910B8269B01263b9`](https://etherscan.io/address/0xF55041E37E12cD407ad00CE2910B8269B01263b9) | -| Data Edge | [`0xADE906194C923b28F03F48BC5D9D987AAE21fFab`](https://etherscan.io/address/0xADE906194C923b28F03F48BC5D9D987AAE21fFab) | -| Block Oracle Owner | [`0xeb4ad97a099defc85c900a60adfd2405c455b2c0`](https://etherscan.io/address/0xeb4ad97a099defc85c900a60adfd2405c455b2c0) | +| Name | Address | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------ | +| Graph Token (GRT ) | [`0x18C924BD5E8b83b47EFaDD632b7178E2Fd36073D`](https://goerli.arbiscan.io/address/0x18C924BD5E8b83b47EFaDD632b7178E2Fd36073D) | +| Staking | [`0xcd549d0C43d915aEB21d3a331dEaB9B7aF186D26`](https://goerli.arbiscan.io/address/0xcd549d0C43d915aEB21d3a331dEaB9B7aF186D26) | +| Data Edge | [``](https://goerli.arbiscan.io/address/) | +| Block Oracle Owner | [``](https://goerli.arbiscan.io/address/) | -Other network contracts can be found in [graphprotocol/contracts](https://github.com/graphprotocol/contracts/blob/dev/addresses.json). +Other network contracts can be found in [graphprotocol/contracts](https://github.com/graphprotocol/contracts/blob/dev/addresses.json#L971). ## Configuration +The Graph testnet contracts live on Goerli, but many of the subgraphs used in the +testnet (for now) are Mainnet subgraphs. This means: + +- Indexer Agent and Indexer Service must connect to Goerli +- Graph Node must connect to at least one Mainnet Ethereum node/provider + ### Indexer Agent -| Environment Variable | CLI Argument | Value | -| ------------------------------------------- | ------------------------------- | ------------------------------------------------------- | -| `INDEXER_AGENT_ETHEREUM` | `--ethereum` | An Ethereum mainnet node/provider | -| `INDEXER_AGENT_ETHEREUM_NETWORK` | `--ethereum-network` | `mainnet` | -| `INDEXER_AGENT_INDEXER_ADDRESS` | `--indexer-address` | Ethereum address of mainnet indexer | -| `INDEXER_AGENT_INDEXER_GEO_COORDINATES` | `--indexer-geo-coordinates` | Geo coordinates of mainnet indexer infrastructure | -| `INDEXER_AGENT_MNEMONIC` | `--mnemonic` | Ethereum mnemonic for mainnet operator | -| `INDEXER_AGENT_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmV614UpBCpuusv5MsismmPYu4KqLtdeNMKpiNrX56kw6u` | -| `INDEXER_AGENT_NETWORK_SUBGRAPH_ENDPOINT` | `--network-subgraph-endpoint` | `https://gateway.thegraph.com/network` | -| `INDEXER_AGENT_DAI_CONTRACT` | `--dai-contract` | `0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48` (USDC) | -| `INDEXER_AGENT_COLLECT_RECEIPTS_ENDPOINT` | `--collect-receipts-endpoint` | `https://gateway.network.thegraph.com/collect-receipts` | -| `INDEXER_AGENT_GAS_PRICE_MAX` | `--gas-price-max` | `50` | +| Environment Variable | CLI Argument | Value | +| ------------------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------- | +| `INDEXER_AGENT_ETHEREUM` | `--ethereum` | An Ethereum Goerli node/provider | +| `INDEXER_AGENT_ETHEREUM_NETWORK` | `--ethereum-network` | `goerli` | +| `INDEXER_AGENT_INDEXER_ADDRESS` | `--indexer-address` | Ethereum address of testnet indexer | +| `INDEXER_AGENT_INDEXER_GEO_COORDINATES` | `--indexer-geo-coordinates` | Geo coordinates of testnet indexer infrastructure | +| `INDEXER_AGENT_MNEMONIC` | `--mnemonic` | Ethereum mnemonic for testnet operator | +| `INDEXER_AGENT_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmS1XDFgDH7SGSh46Y3BT4DizmERbCDqSEQD2ZMPRZMtqA` | +| `INDEXER_AGENT_NETWORK_SUBGRAPH_ENDPOINT` | `--network-subgraph-endpoint` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-arbitrum-goerli` | +| `INDEXER_AGENT_DAI_CONTRACT` | `--dai-contract` | TBD | +| `INDEXER_AGENT_COLLECT_RECEIPTS_ENDPOINT` | `--collect-receipts-endpoint` | `https://gateway-testnet-arbitrum.network.thegraph.com/collect-receipts` | +| `INDEXER_AGENT_GAS_PRICE_MAX` | `--gas-price-max` | `50` | +| `INDEXER_AGENT_EPOCH_SUBGRAPH_ENDPOINT` | `--epoch-subgraph-endpoint` | `https://api.thegraph.com/subgraphs/name/graphprotocol/arb-goerli-epoch-block-oracle` | In order to avoid collecting or claiming query fees below a certain threshold (e.g. below the cost of the two transactions), the following configuration @@ -55,37 +62,31 @@ option can be used. | -------------------------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------- | | `INDEXER_AGENT_REBATE_CLAIM_THRESHOLD` | `--rebate-claim-threshold` | Minimum rebate (in GRT) received for an allocation to claim (Default: 200) | | `INDEXER_AGENT_REBATE_CLAIM_BATCH_THRESHOLD` | `--rebate-claim-batch-threshold` | Minimum total rebates (in GRT) before a batched claim is processed (Default: 2000) | -| `INDEXER_AGENT_VOUCHER_EXPIRATION` | `--voucher-expiration` | Time (in seconds) to permanently delete vouchers with too few query fees (Default: 2160) | +| `INDEXER_AGENT_VOUCHER_EXPIRATION` | `--voucher-expiration` | Time (in seconds) to permanently delete vouchers with too few query fees (Default: 2160) | ### Indexer Service -| Environment Variable | CLI Argument | Value | -| --------------------------------------------- | ------------------------------- | ------------------------------------------------ | -| `INDEXER_SERVICE_ETHEREUM` | `--ethereum` | An Ethereum mainnet node/provider | -| `INDEXER_SERVICE_ETHEREUM_NETWORK` | `--ethereum-network` | `mainnet` | -| `INDEXER_SERVICE_INDEXER_ADDRESS` | `--indexer-address` | Ethereum address of mainnet indexer | -| `INDEXER_SERVICE_MNEMONIC` | `--mnemonic` | Ethereum mnemonic for mainnet operator | -| `INDEXER_SERVICE_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmV614UpBCpuusv5MsismmPYu4KqLtdeNMKpiNrX56kw6u` | -| `INDEXER_SERVICE_NETWORK_SUBGRAPH_ENDPOINT` | `--network-subgraph-endpoint` | `https://gateway.thegraph.com/network` | -| `INDEXER_SERVICE_CLIENT_SIGNER_ADDRESS` | `--client-signer-address` | `0x982D10c56b8BBbD6e09048F5c5f01b43C65D5aE0` | +| Environment Variable | CLI Argument | Value | +| --------------------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------- | +| `INDEXER_SERVICE_ETHEREUM` | `--ethereum` | An Ethereum Goerli node/provider | +| `INDEXER_SERVICE_ETHEREUM_NETWORK` | `--ethereum-network` | `goerli` | +| `INDEXER_SERVICE_INDEXER_ADDRESS` | `--indexer-address` | Ethereum address of testnet indexer | +| `INDEXER_SERVICE_MNEMONIC` | `--mnemonic` | Ethereum mnemonic for testnet operator | +| `INDEXER_SERVICE_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmS1XDFgDH7SGSh46Y3BT4DizmERbCDqSEQD2ZMPRZMtqA` | +| `INDEXER_SERVICE_NETWORK_SUBGRAPH_ENDPOINT` | `--network-subgraph-endpoint` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-arbitrum-goerli` | +| `INDEXER_SERVICE_CLIENT_SIGNER_ADDRESS` | `--client-signer-address` | `0xac01B0b3B2Dc5D8E0D484c02c4d077C15C96a7b4` | ### Graph Node | Environment Variable | CLI Argument | Value | | -------------------- | ---------------- | ----------------------------------- | -| `ethereum` | `--ethereum-rpc` | `mainnet:` | +| `ethereum` | `--ethereum-rpc` | `mainnet:...` | | `ipfs` | `--ipfs` | `https://ipfs.network.thegraph.com` | ## Feature support > This defines indexing & querying features which are experimental or not fully supported for indexing & query rewards and arbitration ([read more](../feature-support-matrix.md)). -``` -graph-node: ≥0.30.0 <0.31.0 -valid from: 787 -upgrade window: 795 -``` - | Subgraph Feature | Aliases | Implemented | Experimental | Query Arbitration | Indexing Arbitration | Indexing Rewards | |--------------------------|---------|-------------|--------------|-------------------|----------------------|------------------| | **Core Features** | | | | | | | @@ -95,7 +96,7 @@ upgrade window: 795 | **Data Source Types** | | | | | | | | eip155:* | * | Yes | No | No | No | No | | eip155:1 | mainnet | Yes | No | Yes | Yes | Yes | -| eip155:100 | gnosis | Yes | Yes | Yes | Yes | Yes | +| eip155:100 | gnosis | Yes | No | Yes | Yes | Yes | | near:* | * | Yes | Yes | No | No | No | | cosmos:* | * | Yes | Yes | No | No | No | | arweave:* | * | Yes | Yes | No | No | No | @@ -106,5 +107,3 @@ upgrade window: 795 | ipfs.cat in mappings | | Yes | Yes | No | No | No | | ENS | | Yes | Yes | No | No | No | | File data sources: IPFS | | Yes | Yes | No | Yes | Yes | - -[Council snapshot](https://snapshot.org/#/council.graphprotocol.eth/proposal/0x4fa76f9ae541bf883e547407270866ffeb8448f3b3fca90bb9c5bc46e31499c2) diff --git a/docs/networks/arbitrum-one.md b/docs/networks/arbitrum-one.md new file mode 100644 index 000000000..9c375a0b9 --- /dev/null +++ b/docs/networks/arbitrum-one.md @@ -0,0 +1,79 @@ +# Arbitrum One Configuration + +Network information can be found at https://thegraph.com/explorer?chain=arbitrum-one. The Graph Network mainnet is open for everyone to participate in as an indexer. The only requirement is a minimum stake of 100k GRT. + +## Latest Releases + +| Component | Release | +| --------------- | ---------------------------------------------------------------------------- | +| contracts | [1.11.1](https://github.com/graphprotocol/contracts/releases/tag/v1.11.1) | +| indexer-agent | [0.20.16](https://github.com/graphprotocol/indexer/releases/tag/v0.20.16) | +| indexer-cli | [0.20.16](https://github.com/graphprotocol/indexer/releases/tag/v0.20.16) | +| indexer-service | [0.20.16](https://github.com/graphprotocol/indexer/releases/tag/v0.20.16) | +| graph-node | [0.32.0](https://github.com/graphprotocol/graph-node/releases/tag/v0.32.0) | + +## Network Parameters + +| Parameter | Value | +| --------------------------- | -------------------- | +| Epoch length | ~ 24h (6646 blocks) | +| Maximum allocation lifetime | ~28 days (28 epochs) | + +## Contracts & accounts + +| Name | Address | +| ------------------ | ----------------------------------------------------------------------------------------------------------------------- | +| Graph Token (GRT) | [`0x9623063377AD1B27544C965cCd7342f7EA7e88C7`](https://arbiscan.io/address/0x9623063377AD1B27544C965cCd7342f7EA7e88C7) | +| Staking | [`0x00669A4CF01450B64E8A2A20E9b1FCB71E61eF03`](https://arbiscan.io/address/0x00669A4CF01450B64E8A2A20E9b1FCB71E61eF03) | +| Data Edge | [``](https://arbiscan.io/address/) | +| Block Oracle Owner | [``](https://arbiscan.io/address/) | + +Other network contracts can be found in [graphprotocol/contracts](https://github.com/graphprotocol/contracts/blob/dev/addresses.json#L752). + +## Configuration + +### Indexer Agent + +| Environment Variable | CLI Argument | Value | +| ------------------------------------------- | ------------------------------- | -------------------------------------------------------------------------------- | +| `INDEXER_AGENT_ETHEREUM` | `--ethereum` | An Ethereum mainnet node/provider | +| `INDEXER_AGENT_ETHEREUM_NETWORK` | `--ethereum-network` | `arbitrum` | +| `INDEXER_AGENT_INDEXER_ADDRESS` | `--indexer-address` | Ethereum address of mainnet indexer | +| `INDEXER_AGENT_INDEXER_GEO_COORDINATES` | `--indexer-geo-coordinates` | Geo coordinates of mainnet indexer infrastructure | +| `INDEXER_AGENT_MNEMONIC` | `--mnemonic` | Ethereum mnemonic for mainnet operator | +| `INDEXER_AGENT_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmcMEEdcVn32kfpkjwmiw5ZADFJdP2JDzm9j56h9iEVrXn` | +| `INDEXER_AGENT_NETWORK_SUBGRAPH_ENDPOINT` | `--network-subgraph-endpoint` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-arbitrum` | +| `INDEXER_AGENT_DAI_CONTRACT` | `--dai-contract` | TBD | +| `INDEXER_AGENT_COLLECT_RECEIPTS_ENDPOINT` | `--collect-receipts-endpoint` | `https://gateway.network.thegraph.com/collect-receipts` | +| `INDEXER_AGENT_GAS_PRICE_MAX` | `--gas-price-max` | `50` | +| `INDEXER_AGENT_EPOCH_SUBGRAPH_ENDPOINT` | `--epoch-subgraph-endpoint` | `https://api.thegraph.com/subgraphs/name/graphprotocol/arbitrum-epoch-block-oracle` | + + +In order to avoid collecting or claiming query fees below a certain threshold +(e.g. below the cost of the two transactions), the following configuration +option can be used. + +| Environment Variable | CLI Argument | Value | +| -------------------------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------- | +| `INDEXER_AGENT_REBATE_CLAIM_THRESHOLD` | `--rebate-claim-threshold` | Minimum rebate (in GRT) received for an allocation to claim (Default: 200) | +| `INDEXER_AGENT_REBATE_CLAIM_BATCH_THRESHOLD` | `--rebate-claim-batch-threshold` | Minimum total rebates (in GRT) before a batched claim is processed (Default: 2000) | +| `INDEXER_AGENT_VOUCHER_EXPIRATION` | `--voucher-expiration` | Time (in seconds) to permanently delete vouchers with too few query fees (Default: 2160) | + +### Indexer Service + +| Environment Variable | CLI Argument | Value | +| --------------------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------- | +| `INDEXER_SERVICE_ETHEREUM` | `--ethereum` | An Ethereum mainnet node/provider | +| `INDEXER_SERVICE_ETHEREUM_NETWORK` | `--ethereum-network` | `arbitrum` | +| `INDEXER_SERVICE_INDEXER_ADDRESS` | `--indexer-address` | Ethereum address of mainnet indexer | +| `INDEXER_SERVICE_MNEMONIC` | `--mnemonic` | Ethereum mnemonic for mainnet operator | +| `INDEXER_SERVICE_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmcMEEdcVn32kfpkjwmiw5ZADFJdP2JDzm9j56h9iEVrXn` | +| `INDEXER_SERVICE_NETWORK_SUBGRAPH_ENDPOINT` | `--network-subgraph-endpoint` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-arbitrum` | +| `INDEXER_SERVICE_CLIENT_SIGNER_ADDRESS` | `--client-signer-address` | `0xc483960d4D58eabc434Dc88a620AdFd883D6Dd4e` | + +### Graph Node + +| Environment Variable | CLI Argument | Value | +| -------------------- | ---------------- | ----------------------------------- | +| `ethereum` | `--ethereum-rpc` | `mainnet:` | +| `ipfs` | `--ipfs` | `https://ipfs.network.thegraph.com` | diff --git a/docs/networks/testnet.md b/docs/networks/ethereum-goerli.md similarity index 91% rename from docs/networks/testnet.md rename to docs/networks/ethereum-goerli.md index 482a62f22..fa0abb13f 100644 --- a/docs/networks/testnet.md +++ b/docs/networks/ethereum-goerli.md @@ -1,16 +1,16 @@ # Testnet Configuration -The Graph Network's testnet is on Goerli. Goerli network information can be found at https://testnet.thegraph.com/explorer/network. +The Graph Network's testnet is on Goerli. Goerli network information can be found at https://testnet.thegraph.com/explorer?chain=goerli. ## Latest Releases -| Component | Release | -| --------------- | ------------------------------------------------------------------------------- | -| contracts | [1.13.0](https://github.com/graphprotocol/contracts/releases/tag/v1.13.0) | -| indexer-agent | [0.20.12](https://github.com/graphprotocol/indexer/releases/tag/v0.20.12) | -| indexer-cli | [0.20.12](https://github.com/graphprotocol/indexer/releases/tag/v0.20.12) | -| indexer-service | [0.20.12](https://github.com/graphprotocol/indexer/releases/tag/v0.20.12) | -| graph-node | [0.30.0-rc.0](https://github.com/graphprotocol/graph-node/releases/tag/v0.30.0-rc.0) | +| Component | Release | +| --------------- | ------------------------------------------------------------------------------------ | +| contracts | [1.13.0](https://github.com/graphprotocol/contracts/releases/tag/v1.13.0) | +| indexer-agent | [0.20.16](https://github.com/graphprotocol/indexer/releases/tag/v0.20.16) | +| indexer-cli | [0.20.16](https://github.com/graphprotocol/indexer/releases/tag/v0.20.16) | +| indexer-service | [0.20.16](https://github.com/graphprotocol/indexer/releases/tag/v0.20.16) | +| graph-node | [v0.32.0-rc.0](https://github.com/graphprotocol/graph-node/releases/tag/v0.32.0-rc.0)| ## Network Parameters @@ -47,7 +47,7 @@ testnet (for now) are Mainnet subgraphs. This means: | `INDEXER_AGENT_INDEXER_ADDRESS` | `--indexer-address` | Ethereum address of testnet indexer | | `INDEXER_AGENT_INDEXER_GEO_COORDINATES` | `--indexer-geo-coordinates` | Geo coordinates of testnet indexer infrastructure | | `INDEXER_AGENT_MNEMONIC` | `--mnemonic` | Ethereum mnemonic for testnet operator | -| `INDEXER_AGENT_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmPVz18RFwK6hE5rZFWERk23LgrTBz2FCkZzgPSrFxFWN4` | +| `INDEXER_AGENT_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmWC8bGoqBRmiAwMRVA983CmhC4j3gMPmkM2p3F2rM35NX` | | `INDEXER_AGENT_NETWORK_SUBGRAPH_ENDPOINT` | `--network-subgraph-endpoint` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli` | | `INDEXER_AGENT_DAI_CONTRACT` | `--dai-contract` | `0x9e7e607afd22906f7da6f1ec8f432d6f244278be` (GDAI) | | `INDEXER_AGENT_COLLECT_RECEIPTS_ENDPOINT` | `--collect-receipts-endpoint` | `https://gateway.testnet.thegraph.com/collect-receipts` | @@ -71,7 +71,7 @@ option can be used. | `INDEXER_SERVICE_ETHEREUM_NETWORK` | `--ethereum-network` | `goerli` | | `INDEXER_SERVICE_INDEXER_ADDRESS` | `--indexer-address` | Ethereum address of testnet indexer | | `INDEXER_SERVICE_MNEMONIC` | `--mnemonic` | Ethereum mnemonic for testnet operator | -| `INDEXER_SERVICE_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmPVz18RFwK6hE5rZFWERk23LgrTBz2FCkZzgPSrFxFWN4` | +| `INDEXER_SERVICE_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmWC8bGoqBRmiAwMRVA983CmhC4j3gMPmkM2p3F2rM35NX` | | `INDEXER_SERVICE_NETWORK_SUBGRAPH_ENDPOINT` | `--network-subgraph-endpoint` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli` | | `INDEXER_SERVICE_CLIENT_SIGNER_ADDRESS` | `--client-signer-address` | `0xe1EC4339019eC9628438F8755f847e3023e4ff9c` | diff --git a/docs/networks/ethereum-mainnet.md b/docs/networks/ethereum-mainnet.md new file mode 100644 index 000000000..dbed3d322 --- /dev/null +++ b/docs/networks/ethereum-mainnet.md @@ -0,0 +1,79 @@ +# Mainnet Configuration + +Network information can be found at https://thegraph.com/explorer?chain=mainnet. The Graph Network mainnet is open for everyone to participate in as an indexer. The only requirement is a minimum stake of 100k GRT. + +## Latest Releases + +| Component | Release | +| --------------- | ---------------------------------------------------------------------------- | +| contracts | [1.11.1](https://github.com/graphprotocol/contracts/releases/tag/v1.11.1) | +| indexer-agent | [0.20.16](https://github.com/graphprotocol/indexer/releases/tag/v0.20.16) | +| indexer-cli | [0.20.16](https://github.com/graphprotocol/indexer/releases/tag/v0.20.16) | +| indexer-service | [0.20.16](https://github.com/graphprotocol/indexer/releases/tag/v0.20.16) | +| graph-node | [0.32.0](https://github.com/graphprotocol/graph-node/releases/tag/v0.32.0) | + +## Network Parameters + +| Parameter | Value | +| --------------------------- | -------------------- | +| Epoch length | ~ 24h (6646 blocks) | +| Maximum allocation lifetime | ~28 days (28 epochs) | + +## Contracts & accounts + +| Name | Address | +| ------------------ | ----------------------------------------------------------------------------------------------------------------------- | +| Graph Token (GRT) | [`0xc944E90C64B2c07662A292be6244BDf05Cda44a7`](https://etherscan.io/address/0xc944e90c64b2c07662a292be6244bdf05cda44a7) | +| Staking | [`0xF55041E37E12cD407ad00CE2910B8269B01263b9`](https://etherscan.io/address/0xF55041E37E12cD407ad00CE2910B8269B01263b9) | +| Data Edge | [`0xADE906194C923b28F03F48BC5D9D987AAE21fFab`](https://etherscan.io/address/0xADE906194C923b28F03F48BC5D9D987AAE21fFab) | +| Block Oracle Owner | [`0xeb4ad97a099defc85c900a60adfd2405c455b2c0`](https://etherscan.io/address/0xeb4ad97a099defc85c900a60adfd2405c455b2c0) | + +Other network contracts can be found in [graphprotocol/contracts](https://github.com/graphprotocol/contracts/blob/dev/addresses.json). + +## Configuration + +### Indexer Agent + +| Environment Variable | CLI Argument | Value | +| ------------------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------- | +| `INDEXER_AGENT_ETHEREUM` | `--ethereum` | An Ethereum mainnet node/provider | +| `INDEXER_AGENT_ETHEREUM_NETWORK` | `--ethereum-network` | `mainnet` | +| `INDEXER_AGENT_INDEXER_ADDRESS` | `--indexer-address` | Ethereum address of mainnet indexer | +| `INDEXER_AGENT_INDEXER_GEO_COORDINATES` | `--indexer-geo-coordinates` | Geo coordinates of mainnet indexer infrastructure | +| `INDEXER_AGENT_MNEMONIC` | `--mnemonic` | Ethereum mnemonic for mainnet operator | +| `INDEXER_AGENT_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmfVipKy3sKva3vYQW8vesh5xPirEPyoKybPY5pfLGcjSS` | +| `INDEXER_AGENT_NETWORK_SUBGRAPH_ENDPOINT` | `--network-subgraph-endpoint` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-mainnet` | +| `INDEXER_AGENT_DAI_CONTRACT` | `--dai-contract` | `0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48` (USDC) | +| `INDEXER_AGENT_COLLECT_RECEIPTS_ENDPOINT` | `--collect-receipts-endpoint` | `https://gateway.network.thegraph.com/collect-receipts` | +| `INDEXER_AGENT_GAS_PRICE_MAX` | `--gas-price-max` | `50` | +| `INDEXER_AGENT_EPOCH_SUBGRAPH_ENDPOINT` | `--epoch-subgraph-endpoint` | `https://api.thegraph.com/subgraphs/name/graphprotocol/mainnet-epoch-block-oracle` | + +In order to avoid collecting or claiming query fees below a certain threshold +(e.g. below the cost of the two transactions), the following configuration +option can be used. + +| Environment Variable | CLI Argument | Value | +| -------------------------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------- | +| `INDEXER_AGENT_REBATE_CLAIM_THRESHOLD` | `--rebate-claim-threshold` | Minimum rebate (in GRT) received for an allocation to claim (Default: 200) | +| `INDEXER_AGENT_REBATE_CLAIM_BATCH_THRESHOLD` | `--rebate-claim-batch-threshold` | Minimum total rebates (in GRT) before a batched claim is processed (Default: 2000) | +| `INDEXER_AGENT_VOUCHER_EXPIRATION` | `--voucher-expiration` | Time (in seconds) to permanently delete vouchers with too few query fees (Default: 2160) | + +### Indexer Service + +| Environment Variable | CLI Argument | Value | +| --------------------------------------------- | ------------------------------- | ------------------------------------------------ | +| `INDEXER_SERVICE_ETHEREUM` | `--ethereum` | An Ethereum mainnet node/provider | +| `INDEXER_SERVICE_ETHEREUM_NETWORK` | `--ethereum-network` | `mainnet` | +| `INDEXER_SERVICE_INDEXER_ADDRESS` | `--indexer-address` | Ethereum address of mainnet indexer | +| `INDEXER_SERVICE_MNEMONIC` | `--mnemonic` | Ethereum mnemonic for mainnet operator | +| `INDEXER_SERVICE_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmfVipKy3sKva3vYQW8vesh5xPirEPyoKybPY5pfLGcjSS` | +| `INDEXER_SERVICE_NETWORK_SUBGRAPH_ENDPOINT` | `--network-subgraph-endpoint` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-mainnet` | +| `INDEXER_SERVICE_CLIENT_SIGNER_ADDRESS` | `--client-signer-address` | `0x982D10c56b8BBbD6e09048F5c5f01b43C65D5aE0` | + +### Graph Node + +| Environment Variable | CLI Argument | Value | +| -------------------- | ---------------- | ----------------------------------- | +| `ethereum` | `--ethereum-rpc` | `mainnet:` | +| `ipfs` | `--ipfs` | `https://ipfs.network.thegraph.com` | + diff --git a/docs/subgraph-freshness.md b/docs/subgraph-freshness.md new file mode 100644 index 000000000..f9f30eae1 --- /dev/null +++ b/docs/subgraph-freshness.md @@ -0,0 +1,56 @@ +# Subgraph Freshness Checker Feature + +## Overview + +The `SubgraphFreshnessChecker` class is introduced to enhance the reliability and timeliness of subgraph queries in environments where the subgraph might lag significantly behind the most recent block on the blockchain. It primarily operates by validating subgraph freshness and issuing warnings if the subgraph is not sufficiently synchronized with the latest blockchain state. + +## Key Concepts + +- **Subgraph Freshness:** A metric to determine how synchronized a subgraph is with the main blockchain. It's gauged by comparing the latest indexed block in the subgraph with the most recent block on the network. + +## Feature Details + +### 1. Continuous Retry Mechanism +The `SubgraphFreshnessChecker` perpetually retries subgraph queries under circumstances where the subgraph is notably behind the most recent block on the blockchain. A warning, including the current block distance from the chain head, is issued if this condition is detected. + +### 2. Configuration +Configuration options have been expanded to allow control over the subgraph freshness checking mechanism via network specification files and Command Line Interface (CLI) parameters: + +- **maxBlockDistance:** An integer defining the acceptable distance (in blocks) between the latest indexed block in the subgraph and the most recent block on the network. If the distance exceeds this value, the subgraph is considered "stale," prompting a retry mechanism and possibly a warning. + +- **freshnessSleepMilliseconds:** An integer dictating the waiting duration (in milliseconds) before a query is retried when the subgraph is deemed stale. + +### Example Configuration + +Here is a snippet of an Arbitrum network specification file with the suggested options for Arbitrum One and Arbitrum Goerli: + +```yaml +subgraphs: + maxBlockDistance: 60 + freshnessSleepMilliseconds: 10000 +``` + +## Practical Implications + +Upon investigating testnets for block distances, we observed that the block distances for Arbitrum-Goerli typically fall under a 50-block threshold. Conversely, no significant distances were recorded in Goerli. + +The following default values have been established based on **Ethereum** observations: + +- **maxBlockDistance:** 0 blocks +- **freshnessSleepMilliseconds:** 5000 (5 seconds) + +The recommended settings for **Arbitrum** networks are: + +- **maxBlockDistance:** 60 blocks +- **freshnessSleepMilliseconds:** 10000 (10 seconds) + + +### Potential Risk Warning + +Suppose the Agent or Service utilizes the default (Ethereum) settings on Arbitrum networks. In that case, a warning will inform users about the risk that queries may forever be considered non-fresh. + +Adjust the `maxBlockDistance` and `freshnessSleepMilliseconds` according to each network condition. + +## Disabling this feature + +This feature can be virtually turned off by setting a very high value for the **maxBlockDistance** option, which will effectively cause the freshness check always to pass. diff --git a/lerna.json b/lerna.json index f3db95619..53d9e61fa 100644 --- a/lerna.json +++ b/lerna.json @@ -4,5 +4,5 @@ ], "npmClient": "yarn", "useWorkspaces": true, - "version": "0.20.14" + "version": "0.20.23" } diff --git a/package.json b/package.json index 223e00446..5d9b7f8f1 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ }, "resolutions": { "ethers": "5.7.0", - "sequelize": "6.19.0", + "sequelize": "6.33.0", "@ethersproject/bignumber": "5.7.0", "@ethersproject/providers": "5.7.0", "@urql/core": "2.4.4", @@ -34,12 +34,12 @@ }, "overrides": { "ethers": "5.7.0", - "sequelize": "6.19.0", + "sequelize": "6.33.0", "@ethersproject/bignumber": "5.7.0", "@ethersproject/providers": "5.7.0", "@urql/core": "2.4.4", "@urql/exchange-execute": "1.2.2", - "graphql": "16.3.0" + "graphql": "16.8.0" }, "engines": { "node": ">=12.22.0" diff --git a/packages/indexer-agent/CHANGELOG.md b/packages/indexer-agent/CHANGELOG.md index 3ab89dfb8..452dbb496 100644 --- a/packages/indexer-agent/CHANGELOG.md +++ b/packages/indexer-agent/CHANGELOG.md @@ -6,9 +6,70 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.20.14] - 2023-03-29 +## [0.20.23] - 2023-09-29 +### Added +- Subgraph Freshness check for Epoch and Network subgraphs +- `manual` deployment management mode + +### Fixed +- Deprecate old payment systems after Exponential Rebates contract changes +- Updated README files + +## [0.20.22] - 2023-08-28 +### Fixed +- Fixed bug causing the network subgraph to be removed after syncing + +## [0.20.21] - 2023-08-22 +### Fixed +- Add missing fields when parsing network specification from CLI options + +### Changed +- Upgraded `common-ts` dependency to v2.0.3 + +## [0.20.20] - 2023-08-21 +### Fixed +- Properly skip reconciling allocations on a network in MANUAL mode +- Missing string interpolation in migration file +- Indexer registration was running for already registered indexers + +## [0.20.19] - 2023-08-18 +### Fixed +- Contract interface changes involving Exponential Rebates +- Included the protocolNetwork field when checking for duplicate action targets + +### Added +- Enforced active connections with Graph-Node and the database during startup +- PostgreSQL connection pool size can be configured with the `postgres-pool-size` *(defaults to `50`)*. + ### Changed -- Subgraph deployment names are now format as: `//` +- Skip dispute checking on epochs with unavailable start block hash + +## [0.20.18] - 2023-08-11 +### Added +- A new migration to add the + - Actions + - IndexingRules + - POIDisputes + - allocation_receipts + - vouchers + - transfer_receipts + - transfers + - allocation_summaries +- The Agent can now be configured for multiple protocol networks. + To enable this feature, start the agent with the environment variable `INDEXER_AGENT_MULTINETWORK_MODE` set to + `true` and specify a directory containing YAML network specification files, one per network. +- The Agent can be configured to automatically support subgraph transfers from L1 to L2. To enable + this feature, set the `enable-auto-migration-support` startup option to `true`. + +### Changed +- The Agent GraphQL API was updated to accept (and in some cases require) a `protocolNetwork` + parameter to determine which network should be used for queries or mutations. +- The `/network` endpoint exposed by the Agent now requires an additional path segment to disambiguate + which protocol network it should target, like `/network/mainnet` or `/network/arbitrum-one`. + +## [0.20.17] - 2023-06-19 +### Changed +- Use updated batch stake feasibility check, improve potential action batch efficiency ## [0.20.12] - 2023-02-19 ### Changed @@ -434,8 +495,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Update @graphprotocol/common-ts to 0.2.2 -[Unreleased]: https://github.com/graphprotocol/indexer/compare/v0.20.14...HEAD -[0.20.14]: https://github.com/graphprotocol/indexer/compare/v0.20.12...v0.20.14 +[Unreleased]: https://github.com/graphprotocol/indexer/compare/v0.20.23-rc.0...HEAD +[0.20.23]: https://github.com/graphprotocol/indexer/compare/v0.20.22...v0.20.23 +[0.20.22]: https://github.com/graphprotocol/indexer/compare/v0.20.21...v0.20.22 +[0.20.21]: https://github.com/graphprotocol/indexer/compare/v0.20.20...v0.20.21 +[0.20.20]: https://github.com/graphprotocol/indexer/compare/v0.20.19...v0.20.20 +[0.20.19]: https://github.com/graphprotocol/indexer/compare/v0.20.18...v0.20.19 +[0.20.18]: https://github.com/graphprotocol/indexer/compare/v0.20.17...v0.20.18 +[0.20.17]: https://github.com/graphprotocol/indexer/compare/v0.20.12...v0.20.17 [0.20.12]: https://github.com/graphprotocol/indexer/compare/v0.20.11...v0.20.12 [0.20.11]: https://github.com/graphprotocol/indexer/compare/v0.20.9...v0.20.11 [0.20.9]: https://github.com/graphprotocol/indexer/compare/v0.20.7...v0.20.9 diff --git a/packages/indexer-agent/README.md b/packages/indexer-agent/README.md index e878801fd..25a087884 100644 --- a/packages/indexer-agent/README.md +++ b/packages/indexer-agent/README.md @@ -8,66 +8,188 @@ Start the agent Ethereum - --ethereum Ethereum node or provider URL [string] [required] - --ethereum-network Ethereum network [string] [default: "rinkeby"] - --ethereum-polling-interval Polling interval for the Ethereum provider (ms) - [number] [default: 4000] - --mnemonic Mnemonic for the operator wallet + --network-provider, --ethereum Ethereum node or provider URL [string] [required] - --indexer-address Ethereum address of the indexer + --ethereum-polling-interval Polling interval for the Ethereum provider + (ms) [number] [default: 4000] + --gas-increase-timeout Time (in seconds) after which transactions + will be resubmitted with a higher gas price + [number] [default: 240] + --gas-increase-factor Factor by which gas prices are increased when + resubmitting transactions + [number] [default: 1.2] + --gas-price-max The maximum gas price (gwei) to use for + transactions + [deprecated] [number] [default: 100] + --base-fee-per-gas-max The maximum base fee per gas (gwei) to use for + transactions, for legacy transactions this + will be treated as the max gas price [number] + --transaction-attempts The maximum number of transaction attempts + (Use 0 for unlimited) [number] [default: 0] + --mnemonic Mnemonic for the operator wallet + [string] [required] + --indexer-address Ethereum address of the indexer [string] [required] Indexer Infrastructure - --graph-node-query-endpoint Graph Node endpoint for querying subgraphs + --index-node-ids Node IDs of Graph nodes to use for indexing + (separated by commas) [array] [required] + --indexer-management-port Port to serve the indexer management API at + [number] [default: 8000] + --metrics-port Port to serve Prometheus metrics at + [number] [default: 7300] + --syncing-port Port to serve the network subgraph and other + syncing data for indexer service at + [number] [default: 8002] + --log-level Log level [string] [default: "debug"] + --graph-node-query-endpoint Graph Node endpoint for querying subgraphs [string] [required] - --graph-node-status-endpoint Graph Node endpoint for indexing statuses etc. + --graph-node-status-endpoint Graph Node endpoint for indexing statuses + etc. [string] [required] + --graph-node-admin-endpoint Graph Node endpoint for applying and + updating subgraph deployments [string] [required] - --graph-node-admin-endpoint Graph Node endpoint for applying and updating - subgraph deployments [string] [required] - --public-indexer-url Indexer endpoint for receiving requests from the - network [string] [required] - --indexer-geo-coordinates Coordinates describing the Indexer's location - using latitude and longitude - [array] [default: ["31.780715","-41.179504"]] - --index-node-ids Node IDs of Graph nodes to use for indexing - (separated by commas) [array] [required] - --indexer-management-port Port to serve the indexer management API at - [number] [default: 8000] - --metrics-port Port to serve Prometheus metrics at [number] - --restake-rewards Restake claimed indexer rewards, if set to - 'false' rewards will be returned to the wallet - [boolean] [default: true] - --log-level Log level [string] [default: "debug"] - --allocation-management Indexer agent allocation management - automation mode (auto|manual|oversight) + --enable-auto-migration-support Auto migrate allocations from L1 to L2 + (multi-network mode must be enabled) + [boolean] [default: false] + --public-indexer-url Indexer endpoint for receiving requests from + the network [string] [required] + --indexer-geo-coordinates Coordinates describing the Indexer's + location using latitude and longitude + [string] [default: ["31.780715","-41.179504"]] + --restake-rewards Restake claimed indexer rewards, if set to + 'false' rewards will be returned to the + wallet [boolean] [default: true] + --allocation-management Indexer agent allocation management + automation mode (auto|manual) [string] [default: "auto"] - --auto-allocation-min-batch-size Minimum number of allocation - transactions inside a batch for auto - management mode [number] [default: 1] + --auto-allocation-min-batch-size Minimum number of allocation transactions + inside a batch for auto allocation + management. No obvious upperbound, with + default of 1 [number] [default: 1] Network Subgraph - --network-subgraph-deployment Network subgraph deployment [string] - --network-subgraph-endpoint Endpoint to query the network subgraph from + --network-subgraph-deployment Network subgraph deployment [string] + --network-subgraph-endpoint Endpoint to query the network subgraph from [string] + --allocate-on-network-subgraph Whether to allocate to the network subgraph + [boolean] [default: false] Protocol + --epoch-subgraph-endpoint Endpoint to query the epoch block oracle subgraph + from [string] [required] --default-allocation-amount Default amount of GRT to allocate to a subgraph - deployment [string] [default: "0.01"] + deployment [number] [default: 0.01] + --register Whether to register the indexer on chain + [boolean] [default: true] +Query Fees + --rebate-claim-threshold Minimum value of rebate for a single + allocation (in GRT) in order for it + to be included in a batch rebate + claim on-chain + [number] [default: 200] + --rebate-claim-batch-threshold Minimum total value of all rebates + in an batch (in GRT) before the + batch is claimed on-chain + [number] [default: 2000] + --rebate-claim-max-batch-size Maximum number of rebates inside a + batch. Upper bound is constrained by + available system memory, and by the + block gas limit + [number] [default: 100] + --voucher-redemption-threshold Minimum value of rebate for a single + allocation (in GRT) in order for it + to be included in a batch rebate + claim on-chain + [number] [default: 200] + --voucher-redemption-batch-threshold Minimum total value of all rebates + in an batch (in GRT) before the + batch is claimed on-chain + [number] [default: 2000] + --voucher-redemption-max-batch-size Maximum number of rebates inside a + batch. Upper bound is constrained by + available system memory, and by the + block gas limit + [number] [default: 100] + --gateway-endpoint, Gateway endpoint base URL + --collect-receipts-endpoint [string] [required] Cost Models - --inject-dai Inject the GRT per DAI conversion rate into cost model variables - [boolean] [default: true] + --inject-dai Inject the GRT to DAI/USDC conversion rate into cost model + variables [boolean] [default: true] Postgres - --postgres-host Postgres host [string] [required] - --postgres-port Postgres port [number] [default: 5432] - --postgres-username Postgres username [string] [default: "postgres"] - --postgres-password Postgres password [string] [default: ""] - --postgres-database Postgres database name [string] [required] + --postgres-host Postgres host [string] [required] + --postgres-port Postgres port [number] [default: 5432] + --postgres-username Postgres username [string] [default: "postgres"] + --postgres-password Postgres password [string] [default: ""] + --postgres-database Postgres database name [string] [required] + --postgres-pool-size Postgres maximum connection pool size + [number] [default: 50] +Disputes + --poi-disputable-epochs The number of epochs in the past to look for + potential POI disputes [number] [default: 1] + --poi-dispute-monitoring Monitor the network for potential POI disputes + [boolean] [default: false] Options: - --version Show version number [boolean] - --help Show help [boolean] + --version Show version number [boolean] + --help Show help [boolean] + --offchain-subgraphs Subgraphs to index that are not on chain + (comma-separated) [array] [default: []] + --dai-contract Address of the DAI or USDC contract to use for the + --inject-dai conversion rate + [string] [default: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"] + +``` + +### `graph-indexer-agent start` in Multi Network Mode + +To use the Indexer Agent in Multi Network Mode, set the environment variable +`INDEXER_AGENT_MULTINETWORK_MODE` to `"true"` before running the command. + +``` +Start the Agent in multiple Protocol Networks + +Indexer Infrastructure + --index-node-ids Node IDs of Graph nodes to use for indexing + (separated by commas) [array] [required] + --indexer-management-port Port to serve the indexer management API at + [number] [default: 8000] + --metrics-port Port to serve Prometheus metrics at + [number] [default: 7300] + --syncing-port Port to serve the network subgraph and other + syncing data for indexer service at + [number] [default: 8002] + --log-level Log level [string] [default: "debug"] + --graph-node-query-endpoint Graph Node endpoint for querying subgraphs + [string] [required] + --graph-node-status-endpoint Graph Node endpoint for indexing statuses + etc. [string] [required] + --graph-node-admin-endpoint Graph Node endpoint for applying and updating + subgraph deployments [string] [required] + --enable-auto-migration-support Auto migrate allocations from L1 to L2 + (multi-network mode must be enabled) + [boolean] [default: false] + +Postgres + --postgres-host Postgres host [string] [required] + --postgres-port Postgres port [number] [default: 5432] + --postgres-username Postgres username [string] [default: "postgres"] + --postgres-password Postgres password [string] [default: ""] + --postgres-database Postgres database name [string] [required] + --postgres-pool-size Postgres maximum connection pool size + [number] [default: 50] + +Options: + --version Show version number [boolean] + --help Show help [boolean] + -p-offchain-subgraphs Subgraphs to index that are not on + chain (comma-separated) + [array] [default: []] + --network-specifications-directory, Path to a directory containing + --dir network specification files + [string] [required] ``` # Copyright diff --git a/packages/indexer-agent/jest.config.js b/packages/indexer-agent/jest.config.js index 7a42563ed..3d76b8f04 100644 --- a/packages/indexer-agent/jest.config.js +++ b/packages/indexer-agent/jest.config.js @@ -1,15 +1,18 @@ -const bail = (s) => { +const bail = s => { throw new Error(s) } module.exports = { collectCoverage: true, + forceExit: true, preset: 'ts-jest', testEnvironment: 'node', testPathIgnorePatterns: ['/node_modules/', '/dist/', '.yalc'], globals: { __DATABASE__: { - host: process.env.POSTGRES_TEST_HOST || bail('POSTGRES_TEST_HOST is not defined'), + host: + process.env.POSTGRES_TEST_HOST || + bail('POSTGRES_TEST_HOST is not defined'), port: parseInt(process.env.POSTGRES_TEST_PORT || '5432'), username: process.env.POSTGRES_TEST_USERNAME || diff --git a/packages/indexer-agent/package.json b/packages/indexer-agent/package.json index fa7c47631..0dabd68d1 100644 --- a/packages/indexer-agent/package.json +++ b/packages/indexer-agent/package.json @@ -1,6 +1,6 @@ { "name": "@graphprotocol/indexer-agent", - "version": "0.20.14", + "version": "0.20.23", "description": "Indexer agent", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -14,10 +14,10 @@ "scripts": { "format": "prettier --write 'src/**/*.ts'", "lint": "eslint . --ext .ts,.tsx --fix", - "compile": "tsc", + "compile": "tsc --build", "prepare": "yarn format && yarn lint && yarn compile", "start": "node ./dist/index.js start", - "test": "jest --colors --verbose", + "test": "jest --colors --verbose --detectOpenHandles", "test:ci": "jest --verbose --ci", "clean": "rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo", "migrator:pending": "node src/db/cli/migrator pending", @@ -29,8 +29,8 @@ "graph-indexer-agent": "bin/graph-indexer-agent" }, "dependencies": { - "@graphprotocol/common-ts": "2.0.1", - "@graphprotocol/indexer-common": "^0.20.14", + "@graphprotocol/common-ts": "2.0.7", + "@graphprotocol/indexer-common": "^0.20.23", "@thi.ng/heaps": "^1.3.1", "@uniswap/sdk": "3.0.3", "axios": "0.26.1", @@ -39,10 +39,13 @@ "ethers": "5.7.0", "evt": "1.9.12", "global": "4.4.0", - "graphql": "16.3.0", + "graphql": "16.8.0", "graphql-tag": "2.12.6", "isomorphic-fetch": "3.0.0", "jayson": "3.6.6", + "lodash.isequal": "4.5.0", + "lodash.mapvalues": "^4.6.0", + "lodash.zip": "^4.2.0", "ngeohash": "0.6.3", "p-filter": "2.1.0", "p-map": "4.0.0", @@ -51,29 +54,35 @@ "p-retry": "4.6.1", "umzug": "3.0.0", "yaml": "^2.0.0-10", - "yargs": "17.4.1" + "yargs": "17.4.1", + "zod": "^3.21.4", + "zod-validation-error": "^1.3.0" }, "devDependencies": { "@types/bs58": "4.0.1", "@types/isomorphic-fetch": "0.0.36", - "@types/jest": "27.4.1", + "@types/jest": "29.5.4", + "@types/lodash.countby": "^4.6.7", + "@types/lodash.isequal": "4.5.6", + "@types/lodash.mapvalues": "^4.6.7", + "@types/lodash.zip": "^4.2.7", "@types/ngeohash": "0.6.4", - "@types/node": "17.0.23", + "@types/node": "20.6.1", "@types/yargs": "17.0.10", - "@typescript-eslint/eslint-plugin": "5.19.0", - "@typescript-eslint/parser": "5.19.0", - "eslint": "8.13.0", + "@typescript-eslint/eslint-plugin": "6.7.0", + "@typescript-eslint/parser": "6.7.0", + "eslint": "8.49.0", "eslint-config-prettier": "^8.5.0", - "jest": "27.5.1", - "prettier": "2.6.2", - "ts-jest": "27.1.4", + "jest": "<30.0.0-0", + "prettier": "3.0.3", + "ts-jest": "29.1.1", "ts-node": "10.7.0", "typechain": "8.0.0", - "typescript": "4.6.3" + "typescript": "5.2.2" }, "resolutions": { "ethers": "5.7.0", - "sequelize": "6.19.0", + "sequelize": "6.33.0", "@ethersproject/bignumber": "5.7.0", "@ethersproject/providers": "5.7.0" }, diff --git a/packages/indexer-agent/src/__tests__/indexer.ts b/packages/indexer-agent/src/__tests__/indexer.ts index 3515df032..212b6a48c 100644 --- a/packages/indexer-agent/src/__tests__/indexer.ts +++ b/packages/indexer-agent/src/__tests__/indexer.ts @@ -1,25 +1,27 @@ import { - connectContracts, connectDatabase, createLogger, + createMetrics, Logger, - NetworkContracts, + Metrics, parseGRT, - toAddress, } from '@graphprotocol/common-ts' import { - AllocationManagementMode, createIndexerManagementClient, defineIndexerManagementModels, IndexerManagementClient, IndexerManagementModels, - IndexingStatusResolver, - NetworkSubgraph, + GraphNode, + Operator, + Network, POIDisputeAttributes, + specification, + QueryFeeModels, + defineQueryFeeModels, + MultiNetworks, } from '@graphprotocol/indexer-common' -import { BigNumber, Wallet } from 'ethers' +import { BigNumber } from 'ethers' import { Sequelize } from 'sequelize' -import { Indexer } from '../indexer' const TEST_DISPUTE_1: POIDisputeAttributes = { allocationID: '0xbAd8935f75903A1eF5ea62199d98Fd7c3c1ab20C', @@ -40,6 +42,7 @@ const TEST_DISPUTE_1: POIDisputeAttributes = { previousEpochReferenceProof: '0xd04b5601739a1638719696d0735c92439267a89248c6fd21388d9600f5c942f6', status: 'potential', + protocolNetwork: 'eip155:5', } const TEST_DISPUTE_2: POIDisputeAttributes = { allocationID: '0x085fd2ADc1B96c26c266DecAb6A3098EA0eda619', @@ -60,6 +63,7 @@ const TEST_DISPUTE_2: POIDisputeAttributes = { previousEpochReferenceProof: '0xd04b5601739a1638719696d0735c92439267a89248c6fd21388d9600f5c942f6', status: 'potential', + protocolNetwork: 'eip155:5', } const POI_DISPUTES_CONVERTERS_FROM_GRAPHQL: Record< @@ -79,6 +83,7 @@ const POI_DISPUTES_CONVERTERS_FROM_GRAPHQL: Record< previousEpochStartBlockNumber: x => +x, previousEpochReferenceProof: x => x, status: x => x, + protocolNetwork: x => x, } /** @@ -104,51 +109,98 @@ declare const __DATABASE__: never let sequelize: Sequelize let models: IndexerManagementModels -let wallet: Wallet -let address: string -let contracts: NetworkContracts +let queryFeeModels: QueryFeeModels let logger: Logger let indexerManagementClient: IndexerManagementClient -let indexer: Indexer +let graphNode: GraphNode +let operator: Operator +let metrics: Metrics + +const PUBLIC_JSON_RPC_ENDPOINT = 'https://ethereum-goerli.publicnode.com' + +const testProviderUrl = + process.env.INDEXER_TEST_JRPC_PROVIDER_URL ?? PUBLIC_JSON_RPC_ENDPOINT + +const setupAll = async () => { + metrics = createMetrics() +} const setup = async () => { + // Clearing the registry prevents duplicate metric registration in the default registry. + metrics.registry.clear() logger = createLogger({ name: 'IndexerAgent', async: false, level: 'trace', }) - wallet = Wallet.createRandom() - sequelize = await connectDatabase(__DATABASE__) models = defineIndexerManagementModels(sequelize) - address = '0x3C17A4c7cD8929B83e4705e04020fA2B1bca2E55' - contracts = await connectContracts(wallet, 5) - await sequelize.sync({ force: true }) - - const statusEndpoint = 'http://localhost:8030/graphql' - const ipfsEndpoint = 'https://ipfs.network.thegraph.com' - const indexingStatusResolver = new IndexingStatusResolver({ - logger: logger, - statusEndpoint: 'statusEndpoint', - }) + const ipfsEndpoint = 'https://ipfs.network.thegraph.com' // TODO: make this configurable and use within graft auto-resolver + queryFeeModels = defineQueryFeeModels(sequelize) + sequelize = await sequelize.sync({ force: true }) + + const indexNodeIDs = ['node_1'] - const networkSubgraph = await NetworkSubgraph.create({ + graphNode = new GraphNode( logger, - endpoint: - 'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-testnet', - deployment: undefined, + 'http://test-admin-endpoint.xyz', + 'https://test-query-endpoint.xyz', + 'https://test-status-endpoint.xyz', + indexNodeIDs, + ) + + const networkSpecification = specification.NetworkSpecification.parse({ + networkIdentifier: 'eip155:5', + gateway: { + url: 'http://localhost:8030/', + }, + networkProvider: { + url: testProviderUrl, + }, + indexerOptions: { + address: '0xf56b5d582920E4527A818FBDd801C0D80A394CB8', + mnemonic: + 'famous aspect index polar tornado zero wedding electric floor chalk tenant junk', + url: 'http://test-indexer.xyz', + }, + subgraphs: { + networkSubgraph: { + url: 'http://test-url.xyz', + }, + epochSubgraph: { + url: 'http://test-url.xyz', + }, + }, + transactionMonitoring: { + gasIncreaseTimeout: 240000, + gasIncreaseFactor: 1.2, + baseFeePerGasMax: 100 * 10 ** 9, + maxTransactionAttempts: 0, + }, + dai: { + contractAddress: '0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448', + inject: false, + }, }) - const indexNodeIDs = ['node_1'] - const autoGraftResolverLimit = 1 + const autoGraftResolverLimit = 1 // TODO: use a sensible value in tests. (Do we need graft auto-resolver in tests?) + const network = await Network.create( + logger, + networkSpecification, + queryFeeModels, + graphNode, + metrics, + ) + + const multiNetworks = new MultiNetworks( + [network], + (n: Network) => n.specification.networkIdentifier, + ) + indexerManagementClient = await createIndexerManagementClient({ models, - address: toAddress(address), - contracts: contracts, - indexingStatusResolver, + graphNode, indexNodeIDs, - deploymentManagementEndpoint: statusEndpoint, - networkSubgraph, logger, defaults: { globalIndexingRule: { @@ -156,25 +208,10 @@ const setup = async () => { parallelAllocations: 1, }, }, - features: { - injectDai: false, - }, - ipfsEndpoint, - autoGraftResolverLimit, + multiNetworks, }) - indexer = new Indexer( - logger, - 'test', - indexingStatusResolver, - indexerManagementClient, - ['test'], - parseGRT('1000'), - address, - AllocationManagementMode.AUTO, - ipfsEndpoint, - autoGraftResolverLimit, - ) + operator = new Operator(logger, indexerManagementClient, networkSpecification) } const teardown = async () => { @@ -182,6 +219,8 @@ const teardown = async () => { } describe('Indexer tests', () => { + jest.setTimeout(60_000) + beforeAll(setupAll) beforeEach(setup) afterEach(teardown) @@ -206,11 +245,12 @@ describe('Indexer tests', () => { previousEpochReferenceProof: '0xd04b5601739a1638719696d0735c92439267a89248c6fd21388d9600f5c942f6', status: 'potential', + protocolNetwork: 'eip155:5', } const disputes = [badDispute] - await expect(indexer.storePoiDisputes(disputes)).rejects.toThrow( + await expect(operator.storePoiDisputes(disputes)).rejects.toThrow( 'Failed to store potential POI disputes', ) }) @@ -222,13 +262,13 @@ describe('Indexer tests', () => { const expectedResult = disputes.map((dispute: Record) => { return disputeFromGraphQL(dispute) }) - await expect(indexer.storePoiDisputes(disputes)).resolves.toEqual( + await expect(operator.storePoiDisputes(disputes)).resolves.toEqual( expectedResult, ) - await expect(indexer.storePoiDisputes(disputes)).resolves.toEqual( + await expect(operator.storePoiDisputes(disputes)).resolves.toEqual( expectedResult, ) - await expect(indexer.storePoiDisputes(disputes)).resolves.toEqual( + await expect(operator.storePoiDisputes(disputes)).resolves.toEqual( expectedResult, ) }) @@ -241,11 +281,11 @@ describe('Indexer tests', () => { return disputeFromGraphQL(dispute) }) const expectedFilteredResult = [disputeFromGraphQL(TEST_DISPUTE_2)] - await expect(indexer.storePoiDisputes(disputes)).resolves.toEqual( + await expect(operator.storePoiDisputes(disputes)).resolves.toEqual( expectedResult, ) - await expect(indexer.fetchPOIDisputes('potential', 205)).resolves.toEqual( - expectedFilteredResult, - ) + await expect( + operator.fetchPOIDisputes('potential', 205, 'eip155:5'), + ).resolves.toEqual(expectedFilteredResult) }) }) diff --git a/packages/indexer-agent/src/agent.ts b/packages/indexer-agent/src/agent.ts index b03ed3c32..b91801786 100644 --- a/packages/indexer-agent/src/agent.ts +++ b/packages/indexer-agent/src/agent.ts @@ -1,13 +1,14 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { + Eventual, join, Logger, Metrics, SubgraphDeploymentID, timer, - toAddress, } from '@graphprotocol/common-ts' import { + ActivationCriteria, ActionStatus, Allocation, AllocationManagementMode, @@ -16,24 +17,34 @@ import { indexerError, IndexerErrorCode, IndexingDecisionBasis, + IndexerManagementClient, IndexingRuleAttributes, Network, - NetworkMonitor, - NetworkSubgraph, POIDisputeAttributes, - ReceiptCollector, RewardsPool, Subgraph, + SubgraphDeployment, SubgraphIdentifierType, evaluateDeployments, AllocationDecision, - formatDeploymentName, + GraphNode, + Operator, + validateProviderNetworkIdentifier, + MultiNetworks, + NetworkMapped, + TransferredSubgraphDeployment, + networkIsL2, + networkIsL1, + DeploymentManagementMode, } from '@graphprotocol/indexer-common' -import { Indexer } from './indexer' -import { AgentConfig } from './types' + import PQueue from 'p-queue' import pMap from 'p-map' import pFilter from 'p-filter' +import mapValues from 'lodash.mapvalues' +import zip from 'lodash.zip' + +type ActionReconciliationContext = [AllocationDecision[], number, number] const deploymentInList = ( list: SubgraphDeploymentID[], @@ -114,155 +125,405 @@ export const convertSubgraphBasedRulesToDeploymentBased = ( return rules } -const deploymentIDSet = (deployments: SubgraphDeploymentID[]): Set => - new Set(deployments.map(id => id.bytes32)) +// Represents a pair of Network and Operator instances belonging to the same protocol +// network. Used when mapping over multiple protocol networks. +type NetworkAndOperator = { + network: Network + operator: Operator +} + +// Extracts the network identifier from a pair of matching Network and Operator objects. +function networkAndOperatorIdentity({ + network, + operator, +}: NetworkAndOperator): string { + const networkId = network.specification.networkIdentifier + const operatorId = operator.specification.networkIdentifier + if (networkId !== operatorId) { + throw new Error( + `Network and Operator pairs have different network identifiers: ${networkId} != ${operatorId}`, + ) + } + return networkId +} + +// Helper function to produce a `MultiNetworks` while validating its +// inputs. +function createMultiNetworks( + networks: Network[], + operators: Operator[], +): MultiNetworks { + // Validate that Networks and Operator arrays have even lengths and + // contain unique, matching network identifiers. + const visited = new Set() + const validInputs = + networks.length === operators.length && + networks.every((network, index) => { + const sameIdentifier = + network.specification.networkIdentifier === + operators[index].specification.networkIdentifier + if (!sameIdentifier) { + return false + } + if (visited.has(network.specification.networkIdentifier)) { + return false + } + visited.add(network.specification.networkIdentifier) + return true + }) -class Agent { + if (!validInputs) { + throw new Error( + 'Invalid Networks and Operator pairs used in Agent initialization', + ) + } + // Note on undefineds: `lodash.zip` can return `undefined` if array lengths are + // uneven, but we have just checked that. + const networksAndOperators = zip(networks, operators).map(pair => { + const [network, operator] = pair + return { network: network!, operator: operator! } + }) + return new MultiNetworks(networksAndOperators, networkAndOperatorIdentity) +} + +export class Agent { logger: Logger metrics: Metrics - indexer: Indexer - network: Network - networkMonitor: NetworkMonitor - networkSubgraph: NetworkSubgraph - allocateOnNetworkSubgraph: boolean - registerIndexer: boolean + graphNode: GraphNode + multiNetworks: MultiNetworks + indexerManagement: IndexerManagementClient offchainSubgraphs: SubgraphDeploymentID[] - receiptCollector: ReceiptCollector + autoMigrationSupport: boolean + deploymentManagement: DeploymentManagementMode constructor( logger: Logger, metrics: Metrics, - indexer: Indexer, - network: Network, - networkMonitor: NetworkMonitor, - networkSubgraph: NetworkSubgraph, - allocateOnNetworkSubgraph: boolean, - registerIndexer: boolean, + graphNode: GraphNode, + operators: Operator[], + indexerManagement: IndexerManagementClient, + networks: Network[], offchainSubgraphs: SubgraphDeploymentID[], - receiptCollector: ReceiptCollector, + autoMigrationSupport: boolean, + deploymentManagement: DeploymentManagementMode, ) { this.logger = logger.child({ component: 'Agent' }) this.metrics = metrics - this.indexer = indexer - this.network = network - this.networkMonitor = networkMonitor - this.networkSubgraph = networkSubgraph - this.allocateOnNetworkSubgraph = allocateOnNetworkSubgraph - this.registerIndexer = registerIndexer + this.graphNode = graphNode + this.indexerManagement = indexerManagement + this.multiNetworks = createMultiNetworks(networks, operators) this.offchainSubgraphs = offchainSubgraphs - this.receiptCollector = receiptCollector + this.autoMigrationSupport = !!autoMigrationSupport + this.deploymentManagement = deploymentManagement } async start(): Promise { + // -------------------------------------------------------------------------------- + // * Connect to Graph Node + // -------------------------------------------------------------------------------- this.logger.info(`Connect to Graph node(s)`) - await this.indexer.connect() + try { + await this.graphNode.connect() + } catch { + this.logger.critical( + `Could not connect to Graph node(s) and query indexing statuses. Exiting. `, + ) + process.exit(1) + } this.logger.info(`Connected to Graph node(s)`) - // Ensure there is a 'global' indexing rule - await this.indexer.ensureGlobalIndexingRule() + // -------------------------------------------------------------------------------- + // * Ensure there is a 'global' indexing rule + // * Ensure NetworkSubgraph is indexing + // * Register the Indexer in the Network + // -------------------------------------------------------------------------------- + await this.multiNetworks.map( + async ({ network, operator }: NetworkAndOperator) => { + try { + await operator.ensureGlobalIndexingRule() + await this.ensureNetworkSubgraphIsIndexing(network) + await network.register() + } catch (err) { + this.logger.critical( + `Failed to prepare indexer for ${network.specification.networkIdentifier}`, + { + error: err.message, + }, + ) + process.exit(1) + } + }, + ) - if (this.registerIndexer) { - await this.network.register() - } + this.reconciliationLoop() + return this + } - const currentEpochNumber = timer(600_000).tryMap( - async () => this.networkMonitor.currentEpochNumber(), + reconciliationLoop() { + const logger = this.logger.child({ component: 'ReconciliationLoop' }) + const currentEpochNumber: Eventual> = timer( + 600_000, + ).tryMap( + async () => + await this.multiNetworks.map(({ network }) => { + logger.trace('Fetching current epoch number', { + protocolNetwork: network.specification.networkIdentifier, + }) + return network.networkMonitor.currentEpochNumber() + }), { onError: error => - this.logger.warn(`Failed to fetch current epoch`, { error }), + logger.warn(`Failed to fetch current epoch`, { error }), }, ) - const channelDisputeEpochs = timer(600_000).tryMap( - () => this.network.contracts.staking.channelDisputeEpochs(), + const maxAllocationEpochs: Eventual> = timer( + 600_000, + ).tryMap( + () => + this.multiNetworks.map(({ network }) => { + logger.trace('Fetching max allocation epochs', { + protocolNetwork: network.specification.networkIdentifier, + }) + return network.contracts.staking.maxAllocationEpochs() + }), { onError: error => - this.logger.warn(`Failed to fetch channel dispute epochs`, { error }), + logger.warn(`Failed to fetch max allocation epochs`, { error }), }, ) - const maxAllocationEpochs = timer(600_000).tryMap( - () => this.network.contracts.staking.maxAllocationEpochs(), + const indexingRules: Eventual> = + timer(20_000).tryMap( + async () => { + return this.multiNetworks.map(async ({ network, operator }) => { + logger.trace('Fetching indexing rules', { + protocolNetwork: network.specification.networkIdentifier, + }) + let rules = await operator.indexingRules(true) + const subgraphRuleIds = rules + .filter( + rule => rule.identifierType == SubgraphIdentifierType.SUBGRAPH, + ) + .map(rule => rule.identifier!) + const subgraphsMatchingRules = + await network.networkMonitor.subgraphs(subgraphRuleIds) + if (subgraphsMatchingRules.length >= 1) { + const epochLength = + await network.contracts.epochManager.epochLength() + const blockPeriod = 15 + const bufferPeriod = epochLength.toNumber() * blockPeriod * 100 // 100 epochs + rules = convertSubgraphBasedRulesToDeploymentBased( + rules, + subgraphsMatchingRules, + bufferPeriod, + ) + } + return rules + }) + }, + { + onError: error => + logger.warn(`Failed to obtain indexing rules, trying again later`, { + error, + }), + }, + ) + + const activeDeployments: Eventual = timer( + 60_000, + ).tryMap( + () => { + logger.trace('Fetching active deployments') + return this.graphNode.subgraphDeployments() + }, { onError: error => - this.logger.warn(`Failed to fetch max allocation epochs`, { error }), + logger.warn( + `Failed to obtain active deployments, trying again later`, + { error }, + ), }, ) - const indexingRules = timer(20_000).tryMap( + const networkDeployments: Eventual> = + timer(240_000).tryMap( + async () => + await this.multiNetworks.map(({ network }) => { + logger.trace('Fetching network deployments', { + protocolNetwork: network.specification.networkIdentifier, + }) + return network.networkMonitor.subgraphDeployments() + }), + { + onError: error => + logger.warn( + `Failed to obtain network deployments, trying again later`, + { error }, + ), + }, + ) + + const eligibleTransferDeployments: Eventual< + NetworkMapped + > = timer(300_000).tryMap( async () => { - let rules = await this.indexer.indexingRules(true) - const subgraphRuleIds = rules - .filter( - rule => rule.identifierType == SubgraphIdentifierType.SUBGRAPH, - ) - .map(rule => rule.identifier!) - const subgraphsMatchingRules = await this.networkMonitor.subgraphs( - subgraphRuleIds, - ) - if (subgraphsMatchingRules.length >= 1) { - const epochLength = - await this.network.contracts.epochManager.epochLength() - const blockPeriod = 15 - const bufferPeriod = epochLength.toNumber() * blockPeriod * 100 // 100 epochs - rules = convertSubgraphBasedRulesToDeploymentBased( - rules, - subgraphsMatchingRules, - bufferPeriod, + // Return early if the auto migration feature is disabled. + if (!this.autoMigrationSupport) { + logger.trace( + 'Auto Migration feature is disabled, skipping querying transferred subgraphs', ) + return this.multiNetworks.map(async () => []) } - return rules + + const statuses = await this.graphNode.indexingStatus([]) + return this.multiNetworks.map(async ({ network }) => { + const protocolNetwork = network.specification.networkIdentifier + logger.trace('Fetching deployments eligible for L2 transfer', { + protocolNetwork, + }) + const transfers = + await network.networkMonitor.transferredDeployments() + logger.trace( + `Found ${transfers.length} transferred subgraphs in the network`, + { protocolNetwork }, + ) + return transfers + .map(transfer => { + const status = statuses.find( + status => + status.subgraphDeployment.ipfsHash == transfer.ipfsHash, + ) + if (status) { + transfer.ready = status.synced && status.health == 'healthy' + } + return transfer + }) + .filter(transfer => transfer.ready == true) + }) }, { onError: error => - this.logger.warn( - `Failed to obtain indexing rules, trying again later`, + logger.warn( + `Failed to obtain transferred deployments, trying again later`, { error }, ), }, ) - const activeDeployments = timer(60_000).tryMap( - () => this.indexer.subgraphDeployments(), - { - onError: error => - this.logger.warn( - `Failed to obtain active deployments, trying again later`, - { - error, - }, - ), + // While in the L1 -> L2 transfer period this will be an intermediate value + // with the final value including transfer considerations + const intermediateNetworkDeploymentAllocationDecisions: Eventual< + NetworkMapped + > = join({ + networkDeployments, + indexingRules, + }).tryMap( + ({ indexingRules, networkDeployments }) => { + return mapValues( + this.multiNetworks.zip(indexingRules, networkDeployments), + ([indexingRules, networkDeployments]: [ + IndexingRuleAttributes[], + SubgraphDeployment[], + ]) => { + // Identify subgraph deployments on the network that are worth picking up; + // these may overlap with the ones we're already indexing + logger.trace('Evaluating which deployments are worth allocating to') + return indexingRules.length === 0 + ? [] + : evaluateDeployments(logger, networkDeployments, indexingRules) + }, + ) }, - ) - - const networkDeployments = timer(240_000).tryMap( - async () => await this.networkMonitor.subgraphDeployments(), { onError: error => - this.logger.warn( - `Failed to obtain network deployments, trying again later`, - { - error, - }, - ), + logger.warn(`Failed to evaluate deployments, trying again later`, { + error, + }), }, ) - const networkDeploymentAllocationDecisions = join({ - networkDeployments, - indexingRules, + // Update targetDeployments and networkDeplomentAllocationDecisions using transferredSubgraphDeployments data + // This will be somewhat custom and will likely be yanked out later after the transfer stage is complete + // Cases: + // - L1 subgraph that had the transfer started: keep synced and allocated to for at least one week + // post transfer. + // - L2 subgraph that has been transferred: + // - if already synced, allocate to it immediately using default allocation amount + // - if not synced, no changes + const networkDeploymentAllocationDecisions: Eventual< + NetworkMapped + > = join({ + intermediateNetworkDeploymentAllocationDecisions, + eligibleTransferDeployments, }).tryMap( - ({ indexingRules, networkDeployments }) => { - // Identify subgraph deployments on the network that are worth picking up; - // these may overlap with the ones we're already indexing - return indexingRules.length === 0 - ? [] - : evaluateDeployments(this.logger, networkDeployments, indexingRules) - }, + ({ + intermediateNetworkDeploymentAllocationDecisions, + eligibleTransferDeployments, + }) => + mapValues( + this.multiNetworks.zip( + intermediateNetworkDeploymentAllocationDecisions, + eligibleTransferDeployments, + ), + ([allocationDecisions, eligibleTransferDeployments]: [ + AllocationDecision[], + TransferredSubgraphDeployment[], + ]) => { + logger.debug( + `Found ${eligibleTransferDeployments.length} deployments eligible for transfer`, + { eligibleTransferDeployments }, + ) + const oneWeekAgo = Math.floor(Date.now() / 1_000) - 86_400 * 7 + return allocationDecisions.map(decision => { + const matchingTransfer = eligibleTransferDeployments.find( + deployment => + deployment.ipfsHash == decision.deployment.ipfsHash && + deployment.startedTransferToL2At.toNumber() > oneWeekAgo, + ) + if (matchingTransfer) { + logger.debug('Found a matching subgraph transfer', { + matchingTransfer, + }) + // L1 deployments being transferred need to be supported for one week post transfer + // to ensure continued support. + if (networkIsL1(matchingTransfer.protocolNetwork)) { + decision.toAllocate = true + decision.ruleMatch.activationCriteria = + ActivationCriteria.L2_TRANSFER_SUPPORT + logger.debug( + `Allocating towards L1 subgraph deployment to support its transfer`, + { + subgraphDeployment: matchingTransfer, + allocationDecision: decision, + }, + ) + } + // L2 Deployments + if ( + networkIsL2(matchingTransfer.protocolNetwork) && + !!matchingTransfer.transferredToL2 + ) { + decision.toAllocate = true + decision.ruleMatch.activationCriteria = + ActivationCriteria.L2_TRANSFER_SUPPORT + logger.debug( + `Allocating towards transferred L2 subgraph deployment`, + { + subgraphDeployment: matchingTransfer, + allocationDecision: decision, + }, + ) + } + } + return decision + }) + }, + ), { onError: error => - this.logger.warn( - `Failed to obtain target allocations, trying again later`, + logger.warn( + `Failed to merge L2 transfer decisions, trying again later`, { error, }, @@ -272,103 +533,117 @@ class Agent { // let targetDeployments be an union of targetAllocations // and offchain subgraphs. - const targetDeployments = join({ + const targetDeployments: Eventual = join({ ticker: timer(120_000), indexingRules, networkDeploymentAllocationDecisions, }).tryMap( async ({ indexingRules, networkDeploymentAllocationDecisions }) => { - const rules = indexingRules - const targetDeploymentIDs = new Set( - networkDeploymentAllocationDecisions + logger.trace('Resolving target deployments') + const targetDeploymentIDs: Set = new Set( + // Concatenate all AllocationDecisions from all protocol networks + Object.values(networkDeploymentAllocationDecisions) + .flat() .filter(decision => decision.toAllocate === true) .map(decision => decision.deployment), ) - // add offchain subgraphs to the deployment list - // from rules - rules + // Add offchain subgraphs to the deployment list from rules + Object.values(indexingRules) + .flat() .filter( rule => rule?.decisionBasis === IndexingDecisionBasis.OFFCHAIN, ) - .map(rule => { + .forEach(rule => { targetDeploymentIDs.add(new SubgraphDeploymentID(rule.identifier)) }) - // from startup args - this.offchainSubgraphs.map(deployment => { + // From startup args + this.offchainSubgraphs.forEach(deployment => { targetDeploymentIDs.add(deployment) }) return [...targetDeploymentIDs] }, { onError: error => - this.logger.warn( + logger.warn( `Failed to obtain target deployments, trying again later`, - { - error, - }, + { error }, ), }, ) - const activeAllocations = timer(120_000).tryMap( - () => this.networkMonitor.allocations(AllocationStatus.ACTIVE), + const activeAllocations: Eventual> = timer( + 120_000, + ).tryMap( + () => + this.multiNetworks.map(({ network }) => { + logger.trace('Fetching active allocations', { + protocolNetwork: network.specification.networkIdentifier, + }) + return network.networkMonitor.allocations(AllocationStatus.ACTIVE) + }), { onError: () => - this.logger.warn( + logger.warn( `Failed to obtain active allocations, trying again later`, ), }, ) - const recentlyClosedAllocations = join({ + // `activeAllocations` is used to trigger this Eventual, but not really needed + // inside. + const recentlyClosedAllocations: Eventual = join({ activeAllocations, currentEpochNumber, }).tryMap( // eslint-disable-next-line @typescript-eslint/no-unused-vars - ({ activeAllocations, currentEpochNumber }) => - this.networkMonitor.recentlyClosedAllocations( + async ({ activeAllocations: _, currentEpochNumber }) => { + const allocationsByNetwork = await this.multiNetworks.mapNetworkMapped( currentEpochNumber, - 1, //TODO: Parameterize with a user provided value - ), - { - onError: () => - this.logger.warn( - `Failed to obtain active allocations, trying again later`, - ), + async ({ network }, epochNumber): Promise => { + logger.trace('Fetching recently closed allocations', { + protocolNetwork: network.specification.networkIdentifier, + currentEpochNumber, + }) + return network.networkMonitor.recentlyClosedAllocations( + epochNumber, + 1, + ) + }, + ) + return Object.values(allocationsByNetwork).flat() }, - ) - - const claimableAllocations = join({ - currentEpochNumber, - channelDisputeEpochs, - }).tryMap( - ({ currentEpochNumber, channelDisputeEpochs }) => - this.network.claimableAllocations( - currentEpochNumber - channelDisputeEpochs, - ), { onError: () => - this.logger.warn( - `Failed to obtain claimable allocations, trying again later`, + logger.warn( + `Failed to obtain active allocations, trying again later`, ), }, ) - this.logger.info(`Waiting for network data before reconciling every 120s`) - const disputableAllocations = join({ + const disputableAllocations: Eventual> = join({ currentEpochNumber, activeDeployments, }).tryMap( - ({ currentEpochNumber, activeDeployments }) => - this.network.disputableAllocations( + async ({ currentEpochNumber, activeDeployments }) => + this.multiNetworks.mapNetworkMapped( currentEpochNumber, - activeDeployments, - 0, + ({ network }: NetworkAndOperator, currentEpochNumber: number) => { + logger.trace('Fetching disputable allocations', { + protocolNetwork: network.specification.networkIdentifier, + currentEpochNumber, + }) + return network.networkMonitor.disputableAllocations( + currentEpochNumber, + activeDeployments, + 0, + ) + }, ), + { onError: () => - this.logger.warn( + logger.warn( `Failed to fetch disputable allocations, trying again later`, ), }, @@ -376,8 +651,6 @@ class Agent { join({ ticker: timer(240_000), - paused: this.network.transactionManager.paused, - isOperator: this.network.transactionManager.isOperator, currentEpochNumber, maxAllocationEpochs, activeDeployments, @@ -385,12 +658,9 @@ class Agent { activeAllocations, networkDeploymentAllocationDecisions, recentlyClosedAllocations, - claimableAllocations, disputableAllocations, }).pipe( async ({ - paused, - isOperator, currentEpochNumber, maxAllocationEpochs, activeDeployments, @@ -398,113 +668,119 @@ class Agent { activeAllocations, networkDeploymentAllocationDecisions, recentlyClosedAllocations, - claimableAllocations, disputableAllocations, }) => { - this.logger.info(`Reconcile with the network`, { + logger.info(`Reconcile with the network`, { currentEpochNumber, }) - // Do nothing else if the network is paused - if (paused) { - return this.logger.info( - `The network is currently paused, not doing anything until it resumes`, + try { + const disputableEpochs = await this.multiNetworks.mapNetworkMapped( + currentEpochNumber, + async ( + { network }: NetworkAndOperator, + currentEpochNumber: number, + ) => + currentEpochNumber - + network.specification.indexerOptions.poiDisputableEpochs, ) - } - // Do nothing if we're not authorized as an operator for the indexer - if (!isOperator) { - return this.logger.error( - `Not authorized as an operator for the indexer`, - { - err: indexerError(IndexerErrorCode.IE034), - indexer: toAddress(this.network.indexerAddress), - operator: toAddress( - this.network.transactionManager.wallet.address, - ), + // Find disputable allocations + await this.multiNetworks.mapNetworkMapped( + this.multiNetworks.zip(disputableEpochs, disputableAllocations), + async ( + { network, operator }: NetworkAndOperator, + [disputableEpoch, disputableAllocations]: [number, Allocation[]], + ): Promise => { + await this.identifyPotentialDisputes( + disputableAllocations, + disputableEpoch, + operator, + network, + ) }, ) - } - - // Do nothing if there are already approved actions in the queue awaiting execution - const approvedActions = await this.indexer.fetchActions({ - status: ActionStatus.APPROVED, - }) - if (approvedActions.length > 0) { - return this.logger.info( - `There are ${approvedActions.length} approved actions awaiting execution, will reconcile with the network once they are executed`, - ) - } - - // Claim rebate pool rewards from finalized allocations - try { - await this.claimRebateRewards(claimableAllocations) } catch (err) { - this.logger.warn(`Failed to claim rebate rewards`, { err }) + logger.warn(`Failed POI dispute monitoring`, { err }) } - try { - const disputableEpoch = - currentEpochNumber - this.network.indexerConfigs.poiDisputableEpochs - // Find disputable allocations - await this.identifyPotentialDisputes( - disputableAllocations, - disputableEpoch, - ) - } catch (err) { - this.logger.warn(`Failed POI dispute monitoring`, { err }) + const eligibleAllocations: Allocation[] = [ + ...recentlyClosedAllocations, + ...Object.values(activeAllocations).flat(), + ] + + // Reconcile deployments + switch (this.deploymentManagement) { + case DeploymentManagementMode.AUTO: + try { + await this.reconcileDeployments( + activeDeployments, + targetDeployments, + eligibleAllocations, + ) + } catch (err) { + logger.warn( + `Exited early while reconciling deployments. Skipped reconciling actions.`, + { + err: indexerError(IndexerErrorCode.IE005, err), + }, + ) + return + } + break + case DeploymentManagementMode.MANUAL: + this.logger.debug( + `Skipping subgraph deployment reconciliation since DeploymentManagementMode = 'manual'`, + ) + break + default: + throw new Error( + `Unexpected parameter for DeploymentManagementMode: ${this.deploymentManagement}`, + ) } try { - await this.reconcileDeployments( - activeDeployments, - targetDeployments, - [...recentlyClosedAllocations, ...activeAllocations], - ) - // Reconcile allocation actions await this.reconcileActions( networkDeploymentAllocationDecisions, - activeAllocations, currentEpochNumber, maxAllocationEpochs, ) } catch (err) { - this.logger.warn( - `Exited early while reconciling deployments/allocations`, - { - err: indexerError(IndexerErrorCode.IE005, err), - }, - ) + logger.warn(`Exited early while reconciling actions`, { + err: indexerError(IndexerErrorCode.IE005, err), + }) + return } }, ) - - return this - } - - async claimRebateRewards(allocations: Allocation[]): Promise { - if (allocations.length > 0) { - this.logger.info(`Claim rebate rewards`, { - claimable: allocations.map(allocation => ({ - id: allocation.id, - deployment: allocation.subgraphDeployment.id.display, - createdAtEpoch: allocation.createdAtEpoch, - amount: allocation.queryFeeRebates, - })), - }) - await this.network.claimMany(allocations) - } } async identifyPotentialDisputes( disputableAllocations: Allocation[], disputableEpoch: number, + operator: Operator, + network: Network, ): Promise { // TODO: Support supplying status = 'any' to fetchPOIDisputes() to fetch all previously processed allocations in a single query + + this.logger.trace(`Identifying potential disputes`, { + protocolNetwork: network.specification.networkIdentifier, + }) + const alreadyProcessed = ( - await this.indexer.fetchPOIDisputes('potential', disputableEpoch) - ).concat(await this.indexer.fetchPOIDisputes('valid', disputableEpoch)) + await operator.fetchPOIDisputes( + 'potential', + disputableEpoch, + operator.specification.networkIdentifier, + ) + ).concat( + await operator.fetchPOIDisputes( + 'valid', + disputableEpoch, + operator.specification.networkIdentifier, + ), + ) const newDisputableAllocations = disputableAllocations.filter( allocation => @@ -512,15 +788,17 @@ class Agent { dispute => dispute.allocationID == allocation.id, ), ) - if (newDisputableAllocations.length == 0) { + if (newDisputableAllocations.length === 0) { this.logger.trace( 'No new disputable allocations to process for potential disputes', + { protocolNetwork: network.specification.networkIdentifier }, ) return } this.logger.debug( `Found new allocations onchain for subgraphs we have indexed. Let's compare POIs to identify any potential indexing disputes`, + { protocolNetwork: network.specification.networkIdentifier }, ) const uniqueRewardsPools: RewardsPool[] = await Promise.all( @@ -533,16 +811,18 @@ class Agent { ] .filter(pool => pool.closedAtEpochStartBlockHash) .map(async pool => { - const closedAtEpochStartBlock = await this.network.ethereum.getBlock( - pool.closedAtEpochStartBlockHash!, - ) + const closedAtEpochStartBlock = + await network.networkProvider.getBlock( + pool.closedAtEpochStartBlockHash!, + ) // Todo: Lazily fetch this, only if the first reference POI doesn't match - const previousEpochStartBlock = await this.network.ethereum.getBlock( - pool.previousEpochStartBlockHash!, - ) + const previousEpochStartBlock = + await network.networkProvider.getBlock( + pool.previousEpochStartBlockHash!, + ) pool.closedAtEpochStartBlockNumber = closedAtEpochStartBlock.number - pool.referencePOI = await this.indexer.statusResolver.proofOfIndexing( + pool.referencePOI = await this.graphNode.proofOfIndexing( pool.subgraphDeployment, { number: closedAtEpochStartBlock.number, @@ -552,15 +832,14 @@ class Agent { ) pool.previousEpochStartBlockHash = previousEpochStartBlock.hash pool.previousEpochStartBlockNumber = previousEpochStartBlock.number - pool.referencePreviousPOI = - await this.indexer.statusResolver.proofOfIndexing( - pool.subgraphDeployment, - { - number: previousEpochStartBlock.number, - hash: previousEpochStartBlock.hash, - }, - pool.allocationIndexer, - ) + pool.referencePreviousPOI = await this.graphNode.proofOfIndexing( + pool.subgraphDeployment, + { + number: previousEpochStartBlock.number, + hash: previousEpochStartBlock.hash, + }, + pool.allocationIndexer, + ) return pool }), ) @@ -608,6 +887,7 @@ class Agent { previousEpochStartBlockNumber: rewardsPool!.previousEpochStartBlockNumber!, status, + protocolNetwork: network.specification.networkIdentifier, } as POIDisputeAttributes }, ) @@ -615,7 +895,7 @@ class Agent { const potentialDisputes = disputes.filter( dispute => dispute.status == 'potential', ).length - const stored = await this.indexer.storePoiDisputes(disputes) + const stored = await operator.storePoiDisputes(disputes) this.logger.info(`Disputable allocations' POIs validated`, { potentialDisputes: potentialDisputes, @@ -623,48 +903,46 @@ class Agent { }) } + // This function assumes that allocations and deployments passed to it have already + // been retrieved from multiple networks. async reconcileDeployments( activeDeployments: SubgraphDeploymentID[], targetDeployments: SubgraphDeploymentID[], eligibleAllocations: Allocation[], ): Promise { - activeDeployments = uniqueDeployments(activeDeployments) - targetDeployments = uniqueDeployments(targetDeployments) - // Note eligibleAllocations are active or recently closed allocations still eligible for queries from the gateway - const eligibleAllocationDeployments = uniqueDeployments( - eligibleAllocations.map(allocation => allocation.subgraphDeployment.id), - ) - + const logger = this.logger.child({ function: 'reconcileDeployments' }) + logger.debug('Reconcile deployments') + // ---------------------------------------------------------------------------------------- // Ensure the network subgraph deployment is _always_ indexed - if (this.networkSubgraph.deployment) { - if ( - !deploymentInList(targetDeployments, this.networkSubgraph.deployment.id) - ) { - targetDeployments.push(this.networkSubgraph.deployment.id) + // ---------------------------------------------------------------------------------------- + let indexingNetworkSubgraph = false + await this.multiNetworks.map(async ({ network }) => { + if (network.networkSubgraph.deployment) { + const networkDeploymentID = network.networkSubgraph.deployment.id + if (!deploymentInList(targetDeployments, networkDeploymentID)) { + targetDeployments.push(networkDeploymentID) + indexingNetworkSubgraph = true + } } - } + }) + // ---------------------------------------------------------------------------------------- + // Inspect Deployments and Networks + // ---------------------------------------------------------------------------------------- // Ensure all subgraphs in offchain subgraphs list are _always_ indexed for (const offchainSubgraph of this.offchainSubgraphs) { if (!deploymentInList(targetDeployments, offchainSubgraph)) { targetDeployments.push(offchainSubgraph) } } + activeDeployments = uniqueDeployments(activeDeployments) + targetDeployments = uniqueDeployments(targetDeployments) - // only show Reconcile when active ids != target ids - // TODO: Fix this check, always returning true - if ( - deploymentIDSet(activeDeployments) != deploymentIDSet(targetDeployments) - ) { - // Turning to trace until the above conditional is fixed - this.logger.debug('Reconcile deployments', { - syncing: activeDeployments.map(id => id.display), - target: targetDeployments.map(id => id.display), - withActiveOrRecentlyClosedAllocation: eligibleAllocationDeployments.map( - id => id.display, - ), - }) - } + // Note eligibleAllocations are active or recently closed allocations still eligible + // for queries from the gateway + const eligibleAllocationDeployments = uniqueDeployments( + eligibleAllocations.map(allocation => allocation.subgraphDeployment.id), + ) // Identify which subgraphs to deploy and which to remove const deploy = targetDeployments.filter( @@ -677,24 +955,33 @@ class Agent { ) if (deploy.length + remove.length !== 0) { - this.logger.info('Deployment changes', { + logger.info('Deployment changes', { + indexingNetworkSubgraph, + syncing: activeDeployments.map(id => id.display), + target: targetDeployments.map(id => id.display), + withActiveOrRecentlyClosedAllocation: eligibleAllocationDeployments.map( + id => id.display, + ), deploy: deploy.map(id => id.display), remove: remove.map(id => id.display), }) + } else { + logger.debug('No deployment changes are necessary') } + // ---------------------------------------------------------------------------------------- + // Execute Deployments (Add, Remove) + // ---------------------------------------------------------------------------------------- + // Deploy/remove up to 10 subgraphs in parallel const queue = new PQueue({ concurrency: 10 }) // Index all new deployments worth indexing await queue.addAll( deploy.map(deployment => async () => { - const subgraphDeployment = - await this.networkMonitor.requireSubgraphDeployment( - deployment.ipfsHash, - ) - const name = formatDeploymentName(subgraphDeployment) - this.logger.info(`Index subgraph deployment`, { + const name = `indexer-agent/${deployment.ipfsHash.slice(-10)}` + + logger.info(`Index subgraph deployment`, { name, deployment: deployment.display, }) @@ -702,28 +989,26 @@ class Agent { // Ensure the deployment is deployed to the indexer // Note: we're not waiting here, as sometimes indexing a subgraph // will block if the IPFS files cannot be retrieved - try { - this.indexer.ensure(name, deployment) - } catch { - this.indexer.resolveGrafting(name, deployment, 0) - } + await this.graphNode.ensure(name, deployment) }), ) // Stop indexing deployments that are no longer worth indexing await queue.addAll( - remove.map(deployment => async () => this.indexer.remove(deployment)), + remove.map(deployment => async () => this.graphNode.remove(deployment)), ) await queue.onIdle() + logger.debug('Finished reconciling deployments') } async identifyExpiringAllocations( - logger: Logger, + _logger: Logger, activeAllocations: Allocation[], deploymentAllocationDecision: AllocationDecision, epoch: number, maxAllocationEpochs: number, + network: Network, ): Promise { const desiredAllocationLifetime = deploymentAllocationDecision.ruleMatch .rule?.allocationLifetime @@ -744,7 +1029,7 @@ class Agent { async (allocation: Allocation) => { try { const onChainAllocation = - await this.network.contracts.staking.getAllocation(allocation.id) + await network.contracts.staking.getAllocation(allocation.id) return onChainAllocation.closedAtEpoch.eq('0') } catch (err) { this.logger.warn( @@ -761,21 +1046,23 @@ class Agent { ) return expiredAllocations } + async reconcileDeploymentAllocationAction( deploymentAllocationDecision: AllocationDecision, + activeAllocations: Allocation[], epoch: number, maxAllocationEpochs: number, + network: Network, + operator: Operator, ): Promise { const logger = this.logger.child({ deployment: deploymentAllocationDecision.deployment.ipfsHash, + protocolNetwork: network.specification.networkIdentifier, epoch, }) - // Acuracy check: re-fetch allocations to ensure that we have a fresh state since the start of the reconciliation loop - const activeAllocations = await this.networkMonitor.allocations( - AllocationStatus.ACTIVE, - ) - + // TODO: Can we replace `filter` for `find` here? Is there such a case when we + // would have multiple allocations for the same subgraph? const activeDeploymentAllocations = activeAllocations.filter( allocation => allocation.subgraphDeployment.id.bytes32 === @@ -784,7 +1071,7 @@ class Agent { switch (deploymentAllocationDecision.toAllocate) { case false: - return await this.indexer.closeEligibleAllocations( + return await operator.closeEligibleAllocations( logger, deploymentAllocationDecision, activeDeploymentAllocations, @@ -793,14 +1080,16 @@ class Agent { case true: { // If no active allocations, create one if (activeDeploymentAllocations.length === 0) { - return await this.indexer.createAllocation( + // Fetch the latest closed allocation, if any + const mostRecentlyClosedAllocation = ( + await network.networkMonitor.closedAllocations( + deploymentAllocationDecision.deployment, + ) + )[0] + return await operator.createAllocation( logger, deploymentAllocationDecision, - ( - await this.networkMonitor.closedAllocations( - deploymentAllocationDecision.deployment, - ) - )[0], + mostRecentlyClosedAllocation, ) } @@ -811,9 +1100,10 @@ class Agent { deploymentAllocationDecision, epoch, maxAllocationEpochs, + network, ) if (expiringAllocations.length > 0) { - await this.indexer.refreshExpiredAllocations( + await operator.refreshExpiredAllocations( logger, deploymentAllocationDecision, expiringAllocations, @@ -824,79 +1114,150 @@ class Agent { } async reconcileActions( - networkDeploymentAllocationDecisions: AllocationDecision[], - activeAllocations: Allocation[], - epoch: number, - maxAllocationEpochs: number, + networkDeploymentAllocationDecisions: NetworkMapped, + epoch: NetworkMapped, + maxAllocationEpochs: NetworkMapped, ): Promise { - if ( - this.indexer.allocationManagementMode == AllocationManagementMode.MANUAL - ) { - this.logger.trace( - `Skipping allocation reconciliation since AllocationManagementMode = 'manual'`, - { - activeAllocations, - targetDeployments: networkDeploymentAllocationDecisions - .filter(decision => decision.toAllocate) - .map(decision => decision.deployment.ipfsHash), + // -------------------------------------------------------------------------------- + // Filter out networks set to `manual` allocation management mode, and ensure the + // Network Subgraph is NEVER allocated towards + // -------------------------------------------------------------------------------- + const validatedAllocationDecisions = + await this.multiNetworks.mapNetworkMapped( + networkDeploymentAllocationDecisions, + async ( + { network }: NetworkAndOperator, + allocationDecisions: AllocationDecision[], + ) => { + if ( + network.specification.indexerOptions.allocationManagementMode === + AllocationManagementMode.MANUAL + ) { + this.logger.trace( + `Skipping allocation reconciliation since AllocationManagementMode = 'manual'`, + { + protocolNetwork: network.specification.networkIdentifier, + targetDeployments: allocationDecisions + .filter(decision => decision.toAllocate) + .map(decision => decision.deployment.ipfsHash), + }, + ) + return [] as AllocationDecision[] + } + const networkSubgraphDeployment = network.networkSubgraph.deployment + if ( + networkSubgraphDeployment && + !network.specification.indexerOptions.allocateOnNetworkSubgraph + ) { + const networkSubgraphIndex = allocationDecisions.findIndex( + decision => + decision.deployment.bytes32 == + networkSubgraphDeployment.id.bytes32, + ) + if (networkSubgraphIndex >= 0) { + allocationDecisions[networkSubgraphIndex].toAllocate = false + } + } + return allocationDecisions }, ) - return - } - // Ensure the network subgraph is never allocated towards + //---------------------------------------------------------------------------------------- + // For every network, loop through all deployments and queue allocation actions if needed + //---------------------------------------------------------------------------------------- + await this.multiNetworks.mapNetworkMapped( + this.multiNetworks.zip3( + validatedAllocationDecisions, + epoch, + maxAllocationEpochs, + ), + async ( + { network, operator }: NetworkAndOperator, + [ + allocationDecisions, + epoch, + maxAllocationEpochs, + ]: ActionReconciliationContext, + ) => { + // Do nothing if there are already approved actions in the queue awaiting execution + const approvedActions = await operator.fetchActions({ + status: ActionStatus.APPROVED, + protocolNetwork: network.specification.networkIdentifier, + }) + if (approvedActions.length > 0) { + this.logger.info( + `There are ${approvedActions.length} approved actions awaiting execution, will reconcile with the network once they are executed`, + { protocolNetwork: network.specification.networkIdentifier }, + ) + return + } + + // Accuracy check: re-fetch allocations to ensure that we have a fresh state since the + // start of the reconciliation loop. This means we don't use the allocations coming from + // the Eventual input. + const activeAllocations: Allocation[] = + await network.networkMonitor.allocations(AllocationStatus.ACTIVE) + + this.logger.trace(`Reconcile allocation actions`, { + protocolNetwork: network.specification.networkIdentifier, + epoch, + maxAllocationEpochs, + targetDeployments: allocationDecisions + .filter(decision => decision.toAllocate) + .map(decision => decision.deployment.ipfsHash), + activeAllocations: activeAllocations.map(allocation => ({ + id: allocation.id, + deployment: allocation.subgraphDeployment.id.ipfsHash, + createdAtEpoch: allocation.createdAtEpoch, + })), + }) + + return pMap(allocationDecisions, async decision => + this.reconcileDeploymentAllocationAction( + decision, + activeAllocations, + epoch, + maxAllocationEpochs, + network, + operator, + ), + ) + }, + ) + } + + // TODO: This could be a initialization check inside Network.create() once/if the Indexer Service + // uses Network instances. + async ensureNetworkSubgraphIsIndexing(network: Network) { if ( - !this.allocateOnNetworkSubgraph && - this.networkSubgraph.deployment?.id.bytes32 + network.specification.subgraphs.networkSubgraph.deployment !== undefined ) { - const networkSubgraphDeploymentId = this.networkSubgraph.deployment.id - const networkSubgraphIndex = - networkDeploymentAllocationDecisions.findIndex( - decision => - decision.deployment.bytes32 == networkSubgraphDeploymentId.bytes32, + try { + // TODO: Check both the local deployment and the external subgraph endpoint + // Make sure the network subgraph is being indexed + await this.graphNode.ensure( + `indexer-agent/${network.specification.subgraphs.networkSubgraph.deployment.slice( + -10, + )}`, + new SubgraphDeploymentID( + network.specification.subgraphs.networkSubgraph.deployment, + ), + ) + + // Validate if the Network Subgraph belongs to the current provider's network. + // This check must be performed after we ensure the Network Subgraph is being indexed. + await validateProviderNetworkIdentifier( + network.specification.networkIdentifier, + network.specification.subgraphs.networkSubgraph.deployment, + this.graphNode, + this.logger, + ) + } catch (e) { + this.logger.warn( + 'Failed to deploy and validate Network Subgraph on index-nodes. Will use external subgraph endpoint instead', + e, ) - if (networkSubgraphIndex >= 0) { - networkDeploymentAllocationDecisions[networkSubgraphIndex].toAllocate = - false } } - - this.logger.trace(`Reconcile allocation actions`, { - epoch, - maxAllocationEpochs, - targetDeployments: networkDeploymentAllocationDecisions - .filter(decision => decision.toAllocate) - .map(decision => decision.deployment.ipfsHash), - activeAllocations: activeAllocations.map(allocation => ({ - id: allocation.id, - deployment: allocation.subgraphDeployment.id.ipfsHash, - createdAtEpoch: allocation.createdAtEpoch, - })), - }) - - // Loop through all deployments on network and queue allocation actions if needed - await pMap(networkDeploymentAllocationDecisions, async decision => { - await this.reconcileDeploymentAllocationAction( - decision, - epoch, - maxAllocationEpochs, - ) - }) } } - -export const startAgent = async (config: AgentConfig): Promise => { - const agent = new Agent( - config.logger, - config.metrics, - config.indexer, - config.network, - config.networkMonitor, - config.networkSubgraph, - config.allocateOnNetworkSubgraph, - config.registerIndexer, - config.offchainSubgraphs, - config.receiptCollector, - ) - return await agent.start() -} diff --git a/packages/indexer-agent/src/commands/common-options.ts b/packages/indexer-agent/src/commands/common-options.ts new file mode 100644 index 000000000..9d40e82b7 --- /dev/null +++ b/packages/indexer-agent/src/commands/common-options.ts @@ -0,0 +1,170 @@ +import fs from 'fs' + +import { Argv } from 'yargs' +import { parse as yaml_parse } from 'yaml' +import { parseDeploymentManagementMode } from '@graphprotocol/indexer-common' + +// Injects all CLI options shared between this module's commands into a `yargs.Argv` object. +export function injectCommonStartupOptions(argv: Argv): Argv { + argv + .option('index-node-ids', { + description: + 'Node IDs of Graph nodes to use for indexing (separated by commas)', + type: 'string', + array: true, + required: true, + coerce: ( + arg, // TODO: we shouldn't need to coerce because yargs already separates values by space + ) => + arg.reduce( + (acc: string[], value: string) => [...acc, ...value.split(',')], + [], + ), + group: 'Indexer Infrastructure', + }) + .option('indexer-management-port', { + description: 'Port to serve the indexer management API at', + type: 'number', + default: 8000, + required: false, + group: 'Indexer Infrastructure', + }) + .option('metrics-port', { + description: 'Port to serve Prometheus metrics at', + type: 'number', + default: 7300, + required: false, + group: 'Indexer Infrastructure', + }) + .option('syncing-port', { + description: + 'Port to serve the network subgraph and other syncing data for indexer service at', + type: 'number', + default: 8002, + required: false, + group: 'Indexer Infrastructure', + }) + .option('log-level', { + description: 'Log level', + type: 'string', + default: 'debug', + group: 'Indexer Infrastructure', + }) + .option('offchain-subgraphs', { + description: 'Subgraphs to index that are not on chain (comma-separated)', + type: 'string', + array: true, + default: [], + coerce: arg => + arg + .reduce( + (acc: string[], value: string) => [...acc, ...value.split(',')], + [], + ) + .map((id: string) => id.trim()) + .filter((id: string) => id.length > 0), + }) + .option('postgres-host', { + description: 'Postgres host', + type: 'string', + required: true, + group: 'Postgres', + }) + .option('postgres-port', { + description: 'Postgres port', + type: 'number', + default: 5432, + group: 'Postgres', + }) + .option('postgres-username', { + description: 'Postgres username', + type: 'string', + required: false, + default: 'postgres', + group: 'Postgres', + }) + .option('postgres-password', { + description: 'Postgres password', + type: 'string', + default: '', + required: false, + group: 'Postgres', + }) + .option('postgres-database', { + description: 'Postgres database name', + type: 'string', + required: true, + group: 'Postgres', + }) + .option('postgres-pool-size', { + description: 'Postgres maximum connection pool size', + type: 'number', + default: 50, + group: 'Postgres', + }) + .option('graph-node-query-endpoint', { + description: 'Graph Node endpoint for querying subgraphs', + type: 'string', + required: true, + group: 'Indexer Infrastructure', + }) + .option('graph-node-status-endpoint', { + description: 'Graph Node endpoint for indexing statuses etc.', + type: 'string', + required: true, + group: 'Indexer Infrastructure', + }) + .option('graph-node-admin-endpoint', { + description: + 'Graph Node endpoint for applying and updating subgraph deployments', + type: 'string', + required: true, + group: 'Indexer Infrastructure', + }) + .option('enable-auto-migration-support', { + description: + 'Auto migrate allocations from L1 to L2 (multi-network mode must be enabled)', + type: 'boolean', + required: false, + default: false, + group: 'Indexer Infrastructure', + }) + .option('deployment-management', { + describe: 'Subgraph deployments management mode', + required: false, + default: 'auto', + choices: ['auto', 'manual'], + coerce: parseDeploymentManagementMode, + group: 'Indexer Infrastructure', + }) + .config({ + key: 'config-file', + description: 'Indexer agent configuration file (YAML format)', + parseFn: function (cfgFilePath: string) { + return yaml_parse(fs.readFileSync(cfgFilePath, 'utf-8')) + }, + }) + .check(argv => { + // Unset arguments set to empty strings. + // This can happen when users set their options as enviroment variables and don't + // assign any value to them. + // + // For example: + // ```sh + // export INDEXER_AGENT_OFFCHAIN_SUBGRAPHS= + // export INDEXER_AGENT_OFFCHAIN_SUBGRAPHS= + // ``` + // + // In NodeJs, those enviroment variables will be set as an empty string instead of + // being undefined, which can cause parse errors when the Agent is building a + // NetworkSpecification. + for (const [key, value] of Object.entries(argv)) { + if (value === '') { + delete argv[key] + } + } + return true + }) + + return argv +} diff --git a/packages/indexer-agent/src/commands/error-handling.ts b/packages/indexer-agent/src/commands/error-handling.ts new file mode 100644 index 000000000..8d4cd8714 --- /dev/null +++ b/packages/indexer-agent/src/commands/error-handling.ts @@ -0,0 +1,53 @@ +import { ZodError } from 'zod' +import { + fromZodError, + FromZodErrorOptions, + ValidationError, +} from 'zod-validation-error' + +type ErrorFormatOptions = Pick< + Required, + 'issueSeparator' | 'prefix' | 'prefixSeparator' +> + +const errorFormatOptions: ErrorFormatOptions = { + // Arbitrary character sequence that is unlikely to appear as part of a validation + // message. It is used for splitting the concateneted error message into individual + // issues. + issueSeparator: '@#validation-error#@', + prefixSeparator: ':', + prefix: 'Indexer Agent Configuration Error(s)', +} + +// Converts a ValidationError into human-friendly error messages. It utilizes +// 'zod-validation-error' to produce these messages from Zod errors, then re-formats the +// concatenated messages into a list with one issue per line, optionally including the +// original file path in the error message. +function formatError( + error: ValidationError, + errorFormatOptions: ErrorFormatOptions, + filePath?: string, +) { + const prefix = errorFormatOptions.prefix + errorFormatOptions.prefixSeparator + const issues = error + .toString() + .substring(prefix.length) + .split(errorFormatOptions.issueSeparator) + .map(issue => issue.trim()) + .map(issue => `- ${issue}`) + .join('\n') + const file = filePath ? ` [ file: ${filePath} ]` : '' + return `${prefix}${file}\n${issues}` +} + +// Helper funciton that processeses a ZodError and displays validation issues in the +// terminal using a human-friendly format +export function displayZodParsingError(error: ZodError, filePath?: string) { + const validationError = fromZodError(error, errorFormatOptions) + const formattedError = formatError( + validationError, + errorFormatOptions, + filePath, + ) + console.error(formattedError) +} diff --git a/packages/indexer-agent/src/commands/start-multi-network.ts b/packages/indexer-agent/src/commands/start-multi-network.ts new file mode 100644 index 000000000..8f7d8214b --- /dev/null +++ b/packages/indexer-agent/src/commands/start-multi-network.ts @@ -0,0 +1,130 @@ +import * as fs from 'fs' +import * as path from 'path' +import { specification as spec } from '@graphprotocol/indexer-common' +import * as YAML from 'yaml' +import { Argv } from 'yargs' +import { injectCommonStartupOptions } from './common-options' +import { displayZodParsingError } from './error-handling' +import { Logger } from '@graphprotocol/common-ts' + +export const startMultiNetwork = { + command: 'start', + describe: 'Start the Agent in multiple Protocol Networks', + builder: (args: Argv): Argv => { + const updatedArgs = injectCommonStartupOptions(args) + return updatedArgs.option('network-specifications-directory', { + alias: 'dir', + description: 'Path to a directory containing network specification files', + type: 'string', + required: true, + }) + }, + // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any + handler: (_argv: any) => {}, +} + +export function parseNetworkSpecifications( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + argv: any, + logger: Logger, +): spec.NetworkSpecification[] { + const dir: string = argv.dir + const yamlFiles = scanDirectoryForYamlFiles(dir, logger) + return parseYamlFiles(yamlFiles) +} + +function scanDirectoryForYamlFiles( + directoryPath: string, + logger: Logger, +): string[] { + const yamlFiles: string[] = [] + + // Check if the directory exists + if (!fs.existsSync(directoryPath)) { + throw new Error(`Directory does not exist: ${directoryPath}`) + } + + // Check if the provided path is a directory + const isDirectory = fs.lstatSync(directoryPath).isDirectory() + if (!isDirectory) { + throw new Error(`Provided path is not a directory: ${directoryPath}`) + } + + // Read the directory + const files = fs.readdirSync(directoryPath) + logger.trace( + `Network configuration directory contains ${files.length} file(s)`, + { directoryPath, files }, + ) + + // Iterate over each file in the directory + for (const file of files) { + const filePath = path.join(directoryPath, file) + + // Check if the file is a regular file and has a YAML extension + const isFile = fs.lstatSync(filePath).isFile() + const isYaml = /\.ya?ml$/i.test(file) + logger.trace(`Network specification candidate file found: '${file}'`, { + isFile, + isYaml, + }) + if (isFile && isYaml) { + try { + // Check if the file can be read + fs.accessSync(filePath, fs.constants.R_OK) + yamlFiles.push(filePath) + } catch (error) { + throw new Error(`Cannot read file: ${filePath}`) + } + } + } + + // Check if at least one YAMl file was found + if (yamlFiles.length === 0) { + throw new Error( + `No YAML file was found in '${directoryPath}'. At least one file is required.`, + ) + } + + return yamlFiles +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function readYamlFile(filePath: string): any { + const text = fs.readFileSync(filePath, 'utf8') + let content + try { + content = YAML.parse(text) + } catch (yamlParseError) { + throw new Error( + `Failed to parse network specification YAML file at ${filePath}.\n${yamlParseError}`, + ) + } + if (!content) { + throw new Error( + `Failed to parse network specification YAML file: ${filePath}.\nFile is empty.`, + ) + } + return content +} + +function parseYamlFile(filePath: string): spec.NetworkSpecification { + let yamlContent + try { + yamlContent = readYamlFile(filePath) + } catch (error) { + console.log(error.message) + process.exit(1) + } + + try { + return spec.NetworkSpecification.parse(yamlContent) + } catch (error) { + displayZodParsingError(error, filePath) + process.exit(1) + } +} + +function parseYamlFiles(filePaths: string[]): spec.NetworkSpecification[] { + return filePaths.map(parseYamlFile) +} diff --git a/packages/indexer-agent/src/commands/start.ts b/packages/indexer-agent/src/commands/start.ts index 57caf174d..f701a52b8 100644 --- a/packages/indexer-agent/src/commands/start.ts +++ b/packages/indexer-agent/src/commands/start.ts @@ -1,53 +1,57 @@ -import fs from 'fs' import path from 'path' +import axios from 'axios' import { Argv } from 'yargs' -import { parse as yaml_parse } from 'yaml' import { SequelizeStorage, Umzug } from 'umzug' - import { - connectContracts, - connectDatabase, - createLogger, createMetrics, + connectDatabase, createMetricsServer, formatGRT, - parseGRT, - SubgraphDeploymentID, - toAddress, Logger, + SubgraphDeploymentID, } from '@graphprotocol/common-ts' import { - AllocationReceiptCollector, createIndexerManagementClient, createIndexerManagementServer, defineIndexerManagementModels, defineQueryFeeModels, + GraphNode, indexerError, IndexerErrorCode, - IndexingStatusResolver, + MultiNetworks, Network, - NetworkSubgraph, + Operator, registerIndexerErrorMetrics, - AllocationManagementMode, - NetworkMonitor, - EpochSubgraph, resolveChainId, + specification as spec, } from '@graphprotocol/indexer-common' -import { startAgent } from '../agent' -import { Indexer } from '../indexer' -import { Wallet } from 'ethers' -import { Network as NetworkMetadata } from '@ethersproject/networks' +import { Agent } from '../agent' import { startCostModelAutomation } from '../cost' import { createSyncingServer } from '../syncing-server' -import { monitorEthBalance } from '../utils' +import { injectCommonStartupOptions } from './common-options' +import pMap from 'p-map' +import { NetworkSpecification } from '@graphprotocol/indexer-common/dist/network-specification' +import { BigNumber } from 'ethers' +import { displayZodParsingError } from './error-handling' + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type AgentOptions = { [key: string]: any } & Argv['argv'] + +const DEFAULT_SUBGRAPH_MAX_BLOCK_DISTANCE = 0 +const SUGGESTED_SUBGRAPH_MAX_BLOCK_DISTANCE_ON_L2 = + 50 + DEFAULT_SUBGRAPH_MAX_BLOCK_DISTANCE +const DEFAULT_SUBGRAPH_FRESHNESS_SLEEP_MILLISECONDS = 5_000 -export default { +export const start = { command: 'start', describe: 'Start the agent', - builder: (yargs: Argv): Argv => { - return yargs - .option('ethereum', { + builder: (args: Argv): Argv => { + const updatedArgs = injectCommonStartupOptions(args) + return updatedArgs + .option('network-provider', { + alias: 'ethereum', description: 'Ethereum node or provider URL', + array: false, type: 'string', required: true, group: 'Ethereum', @@ -64,7 +68,6 @@ export default { type: 'number', default: 240, group: 'Ethereum', - coerce: arg => arg * 1000, }) .option('gas-increase-factor', { description: @@ -79,7 +82,6 @@ export default { default: 100, deprecated: true, group: 'Ethereum', - coerce: arg => arg * 10 ** 9, }) .option('base-fee-per-gas-max', { description: @@ -87,7 +89,6 @@ export default { type: 'number', required: false, group: 'Ethereum', - coerce: arg => arg * 10 ** 9, }) .option('transaction-attempts', { description: @@ -108,25 +109,6 @@ export default { required: true, group: 'Ethereum', }) - .option('graph-node-query-endpoint', { - description: 'Graph Node endpoint for querying subgraphs', - type: 'string', - required: true, - group: 'Indexer Infrastructure', - }) - .option('graph-node-status-endpoint', { - description: 'Graph Node endpoint for indexing statuses etc.', - type: 'string', - required: true, - group: 'Indexer Infrastructure', - }) - .option('graph-node-admin-endpoint', { - description: - 'Graph Node endpoint for applying and updating subgraph deployments', - type: 'string', - required: true, - group: 'Indexer Infrastructure', - }) .option('public-indexer-url', { description: 'Indexer endpoint for receiving requests from the network', type: 'string', @@ -136,22 +118,34 @@ export default { .options('indexer-geo-coordinates', { description: `Coordinates describing the Indexer's location using latitude and longitude`, type: 'string', - array: true, + nargs: 2, default: ['31.780715', '-41.179504'], group: 'Indexer Infrastructure', - coerce: arg => - arg.reduce( - (acc: string[], value: string) => [...acc, ...value.split(' ')], - [], - ), + coerce: function ( + coordinates: string | [string, string], + ): [number, number] { + if (typeof coordinates === 'string') { + // When this value is set in an enviromnent variable, yarns passes + // it as a single string. + + // Yargs should have passed 2 arguments to this functions, so we + // expect this array has two elements + return coordinates.split(' ').map(parseFloat) as [number, number] + } + // When this value is set in the command line, yargs passes it as an + // array of two strings. + return coordinates.map(parseFloat) as [number, number] + }, }) .option('network-subgraph-deployment', { description: 'Network subgraph deployment', + array: false, type: 'string', group: 'Network Subgraph', }) .option('network-subgraph-endpoint', { description: 'Endpoint to query the network subgraph from', + array: false, type: 'string', group: 'Network Subgraph', }) @@ -163,53 +157,32 @@ export default { }) .option('epoch-subgraph-endpoint', { description: 'Endpoint to query the epoch block oracle subgraph from', + array: false, type: 'string', required: true, group: 'Protocol', }) - .option('index-node-ids', { - description: - 'Node IDs of Graph nodes to use for indexing (separated by commas)', - type: 'string', - array: true, - required: true, - coerce: arg => - arg.reduce( - (acc: string[], value: string) => [...acc, ...value.split(',')], - [], - ), - group: 'Indexer Infrastructure', - }) - .option('default-allocation-amount', { + .option('subgraph-max-block-distance', { description: - 'Default amount of GRT to allocate to a subgraph deployment', - type: 'string', - default: '0.01', - required: false, - group: 'Protocol', - coerce: arg => parseGRT(arg), - }) - .option('indexer-management-port', { - description: 'Port to serve the indexer management API at', + 'How many blocks subgraphs are allowed to stay behind chain head', type: 'number', - default: 8000, - required: false, - group: 'Indexer Infrastructure', + default: DEFAULT_SUBGRAPH_MAX_BLOCK_DISTANCE, + group: 'Protocol', }) - .option('metrics-port', { - description: 'Port to serve Prometheus metrics at', + .option('subgraph-freshness-sleep-milliseconds', { + description: + 'How long to wait before retrying subgraph query if it is not fresh', type: 'number', - defaut: 7300, - required: false, - group: 'Indexer Infrastructure', + default: DEFAULT_SUBGRAPH_FRESHNESS_SLEEP_MILLISECONDS, + group: 'Protocol', }) - .option('syncing-port', { + .option('default-allocation-amount', { description: - 'Port to serve the network subgraph and other syncing data for indexer service at', + 'Default amount of GRT to allocate to a subgraph deployment', type: 'number', - default: 8002, + default: 0.01, required: false, - group: 'Indexer Infrastructure', + group: 'Protocol', }) .option('restake-rewards', { description: `Restake claimed indexer rewards, if set to 'false' rewards will be returned to the wallet`, @@ -219,17 +192,15 @@ export default { }) .option('rebate-claim-threshold', { description: `Minimum value of rebate for a single allocation (in GRT) in order for it to be included in a batch rebate claim on-chain`, - type: 'string', - default: '200', // This value (the marginal gain of a claim in GRT), should always exceed the marginal cost of a claim (in ETH gas) + type: 'number', + default: 200, // This value (the marginal gain of a claim in GRT), should always exceed the marginal cost of a claim (in ETH gas) group: 'Query Fees', - coerce: arg => parseGRT(arg), }) .option('rebate-claim-batch-threshold', { description: `Minimum total value of all rebates in an batch (in GRT) before the batch is claimed on-chain`, - type: 'string', - default: '2000', + type: 'number', + default: 2000, group: 'Query Fees', - coerce: arg => parseGRT(arg), }) .option('rebate-claim-max-batch-size', { description: `Maximum number of rebates inside a batch. Upper bound is constrained by available system memory, and by the block gas limit`, @@ -239,17 +210,15 @@ export default { }) .option('voucher-redemption-threshold', { description: `Minimum value of rebate for a single allocation (in GRT) in order for it to be included in a batch rebate claim on-chain`, - type: 'string', - default: '200', // This value (the marginal gain of a claim in GRT), should always exceed the marginal cost of a claim (in ETH gas) + type: 'number', + default: 200, // This value (the marginal gain of a claim in GRT), should always exceed the marginal cost of a claim (in ETH gas) group: 'Query Fees', - coerce: arg => parseGRT(arg), }) .option('voucher-redemption-batch-threshold', { description: `Minimum total value of all rebates in an batch (in GRT) before the batch is claimed on-chain`, - type: 'string', - default: '2000', + type: 'number', + default: 2000, group: 'Query Fees', - coerce: arg => parseGRT(arg), }) .option('voucher-redemption-max-batch-size', { description: `Maximum number of rebates inside a batch. Upper bound is constrained by available system memory, and by the block gas limit`, @@ -283,65 +252,12 @@ export default { // Default to USDC default: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', }) - .option('postgres-host', { - description: 'Postgres host', - type: 'string', - required: true, - group: 'Postgres', - }) - .option('postgres-port', { - description: 'Postgres port', - type: 'number', - default: 5432, - group: 'Postgres', - }) - .option('postgres-username', { - description: 'Postgres username', - type: 'string', - required: false, - default: 'postgres', - group: 'Postgres', - }) - .option('postgres-password', { - description: 'Postgres password', - type: 'string', - default: '', - required: false, - group: 'Postgres', - }) - .option('postgres-database', { - description: 'Postgres database name', - type: 'string', - required: true, - group: 'Postgres', - }) - .option('log-level', { - description: 'Log level', - type: 'string', - default: 'debug', - group: 'Indexer Infrastructure', - }) .option('register', { description: 'Whether to register the indexer on chain', type: 'boolean', default: true, group: 'Protocol', }) - .option('offchain-subgraphs', { - description: - 'Subgraphs to index that are not on chain (comma-separated)', - type: 'string', - array: true, - default: [], - coerce: arg => - arg - .reduce( - (acc: string[], value: string) => [...acc, ...value.split(',')], - [], - ) - .map((id: string) => id.trim()) - .filter((id: string) => id.length > 0), - }) .option('poi-disputable-epochs', { description: 'The number of epochs in the past to look for potential POI disputes', @@ -355,12 +271,34 @@ export default { default: false, group: 'Disputes', }) + .option('gateway-endpoint', { + description: 'Gateway endpoint base URL', + alias: 'collect-receipts-endpoint', + type: 'string', + array: false, + required: true, + group: 'Query Fees', + }) + .option('allocation-management', { + description: + 'Indexer agent allocation management automation mode (auto|manual) ', + type: 'string', + required: false, + default: 'auto', + group: 'Indexer Infrastructure', + }) + .option('auto-allocation-min-batch-size', { + description: `Minimum number of allocation transactions inside a batch for auto allocation management. No obvious upperbound, with default of 1`, + type: 'number', + default: 1, + group: 'Indexer Infrastructure', + }) .check(argv => { if ( !argv['network-subgraph-endpoint'] && !argv['network-subgraph-deployment'] ) { - return `At least one of --network-subgraph-endpoint and --network-subgraph-deployment must be provided` + return 'At least one of --network-subgraph-endpoint and --network-subgraph-deployment must be provided' } if (argv['indexer-geo-coordinates']) { const [geo1, geo2] = argv['indexer-geo-coordinates'] @@ -369,7 +307,7 @@ export default { } } if (argv['gas-increase-timeout']) { - if (argv['gas-increase-timeout'] < 30000) { + if (argv['gas-increase-timeout'] < 30) { return 'Invalid --gas-increase-timeout provided. Must be at least 30 seconds' } } @@ -382,454 +320,441 @@ export default { ) { return 'Invalid --rebate-claim-max-batch-size provided. Must be > 0 and an integer.' } - if ( - !Number.isInteger(argv['auto-graft-resolver-limit']) || - argv['auto-graft-resolver-limit'] < 0 - ) { - return 'Invalid --auto-graft-resolver-limit provided. Must be >= 0 and an integer.' - } return true }) - .option('collect-receipts-endpoint', { - description: 'Client endpoint for collecting receipts', - type: 'string', - required: false, - group: 'Query Fees', - }) - .option('allocation-management', { - description: - 'Indexer agent allocation management automation mode (auto|manual) ', - type: 'string', - required: false, - default: 'auto', - group: 'Indexer Infrastructure', - }) - .option('auto-allocation-min-batch-size', { - description: `Minimum number of allocation transactions inside a batch for auto allocation management. No obvious upperbound, with default of 1`, - type: 'number', - default: 1, - group: 'Indexer Infrastructure', - }) - .config({ - key: 'config-file', - description: 'Indexer agent configuration file (YAML format)', - parseFn: function (cfgFilePath: string) { - return yaml_parse(fs.readFileSync(cfgFilePath, 'utf-8')) - }, - }) }, - handler: async ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - argv: { [key: string]: any } & Argv['argv'], - ): Promise => { - const logger = createLogger({ - name: 'IndexerAgent', - async: false, - level: argv.logLevel, - }) + // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any + handler: (_argv: any) => {}, +} - if (argv.gasIncreaseTimeout < 90000) { - logger.warn( - 'Gas increase timeout is set to less than 90 seconds (~ 6 blocks). This may lead to high gas usage', - { gasIncreaseTimeout: argv.gasIncreaseTimeout / 1000.0 }, - ) - } +export async function createNetworkSpecification( + argv: AgentOptions, + logger: Logger, +): Promise { + const gateway = { + url: argv.gatewayEndpoint, + } - if (argv.gasIncreaseFactor > 1.5) { - logger.warn( - `Gas increase factor is set to > 1.5. This may lead to high gas usage`, - { gasIncreaseFactor: argv.gasIncreaseFactor }, - ) - } + const indexerOptions = { + address: argv.indexerAddress, + mnemonic: argv.mnemonic, + url: argv.publicIndexerUrl, + geoCoordinates: argv.indexerGeoCoordinates, + restakeRewards: argv.restakeRewards, + rebateClaimThreshold: argv.rebateClaimThreshold, + rebateClaimBatchThreshold: argv.rebateClaimBatchThreshold, + rebateClaimMaxBatchSize: argv.rebateClaimMaxBatchSize, + poiDisputeMonitoring: argv.poiDisputeMonitoring, + poiDisputableEpochs: argv.poiDisputableEpochs, + defaultAllocationAmount: argv.defaultAllocationAmount, + voucherRedemptionThreshold: argv.voucherRedemptionThreshold, + voucherRedemptionBatchThreshold: argv.voucherRedemptionBatchThreshold, + voucherRedemptionMaxBatchSize: argv.voucherRedemptionMaxBatchSize, + allocationManagementMode: argv.allocationManagement, + autoAllocationMinBatchSize: argv.autoAllocationMinBatchSize, + allocateOnNetworkSubgraph: argv.allocateOnNetworkSubgraph, + register: argv.register, + } - if (argv.rebateClaimThreshold.lt(argv.voucherRedemptionThreshold)) { - logger.warn( - `Rebate single minimum claim value is less than voucher minimum redemption value, but claims depend on redemptions`, - { - voucherRedemptionThreshold: formatGRT( - argv.voucherRedemptionThreshold, - ), - rebateClaimThreshold: formatGRT(argv.rebateClaimThreshold), - }, - ) - } + const transactionMonitoring = { + gasIncreaseTimeout: argv.gasIncreaseTimeout, + gasIncreaseFactor: argv.gasIncreaseFactor, + gasPriceMax: argv.gasPriceMax, + baseFeePerGasMax: argv.baseFeeGasMax, + maxTransactionAttempts: argv.maxTransactionAttempts, + } - if (argv.rebateClaimThreshold.eq(0)) { - logger.warn( - `Minimum query fee rebate value is 0 GRT, which may lead to claiming unprofitable rebates`, - ) - } + const subgraphs = { + maxBlockDistance: argv.subgraphMaxBlockDistance, + freshnessSleepMilliseconds: argv.subgraphFreshnessSleepMilliseconds, + networkSubgraph: { + deployment: argv.networkSubgraphDeployment, + url: argv.networkSubgraphEndpoint, + }, + epochSubgraph: { + // TODO: We should consider indexing the Epoch Subgraph, similar + // to how we currently do it for the Network Subgraph. + url: argv.epochSubgraphEndpoint, + }, + } - if (argv.rebateClaimMaxBatchSize > 200) { - logger.warn( - `Setting the max batch size for rebate claims to more than 200 may result in batches that are too large to fit into a block`, - { rebateClaimMaxBatchSize: argv.rebateClaimMaxBatchSize }, - ) - } + const dai = { + contractAddress: argv.daiContractAddress, + inject: argv.injectDai, + } + + const networkProvider = { + url: argv.networkProvider, + pollingInterval: argv.ethereumPollingInterval, + } - if (argv.voucherRedemptionThreshold.eq(0)) { + // Since we can't infer the network identifier, we must ask the configured + // JSON RPC provider for its `chainID`. + const chainId = await fetchChainId(networkProvider.url) + const networkIdentifier = resolveChainId(chainId) + + // Warn about inappropriate max block distance for subgraph threshold checks for given networks. + if (networkIdentifier.startsWith('eip155:42161')) { + // Arbitrum-One and Arbitrum-Goerli + if ( + subgraphs.maxBlockDistance <= SUGGESTED_SUBGRAPH_MAX_BLOCK_DISTANCE_ON_L2 + ) { logger.warn( - `Minimum voucher redemption value is 0 GRT, which may lead to redeeming unprofitable vouchers`, + `Consider increasing 'subgraph-max-block-distance' for Arbitrum networks`, + { + problem: + 'A low subgraph freshness threshold might cause the Agent to discard too many subgraph queries in fast-paced networks.', + hint: `Increase the 'subgraph-max-block-distance' parameter to a value that accomodates for block and indexing speeds.`, + configuredValue: subgraphs.maxBlockDistance, + }, ) } - - if (argv.voucherRedemptionMaxBatchSize > 200) { + if ( + subgraphs.freshnessSleepMilliseconds <= + DEFAULT_SUBGRAPH_FRESHNESS_SLEEP_MILLISECONDS + ) { logger.warn( - `Setting the max batch size for voucher redemptions to more than 200 may result in batches that are too large to fit into a block`, - { voucherRedemptionMaxBatchSize: argv.voucherRedemptionMaxBatchSize }, + `Consider increasing 'subgraph-freshness-sleep-milliseconds' for Arbitrum networks`, + { + problem: + 'A short subgraph freshness wait time might be insufficient for the subgraph to sync with fast-paced networks.', + hint: `Increase the 'subgraph-freshness-sleep-milliseconds' parameter to a value that accomodates for block and indexing speeds.`, + configuredValue: subgraphs.freshnessSleepMilliseconds, + }, ) } + } - process.on('unhandledRejection', err => { - logger.warn(`Unhandled promise rejection`, { - err: indexerError(IndexerErrorCode.IE035, err), - }) - }) - - process.on('uncaughtException', err => { - logger.warn(`Uncaught exception`, { - err: indexerError(IndexerErrorCode.IE036, err), - }) + try { + return spec.NetworkSpecification.parse({ + networkIdentifier, + gateway, + indexerOptions, + transactionMonitoring, + subgraphs, + networkProvider, + dai, }) + } catch (parsingError) { + displayZodParsingError(parsingError) + process.exit(1) + } +} - // Spin up a metrics server - const metrics = createMetrics() - createMetricsServer({ - logger: logger.child({ component: 'MetricsServer' }), - registry: metrics.registry, - port: argv.metricsPort, +export async function run( + argv: AgentOptions, + networkSpecifications: spec.NetworkSpecification[], + logger: Logger, +): Promise { + // -------------------------------------------------------------------------------- + // * Configure event listeners for unhandled promise rejections and uncaught + // exceptions. + // -------------------------------------------------------------------------------- + process.on('unhandledRejection', err => { + logger.warn(`Unhandled promise rejection`, { + err: indexerError(IndexerErrorCode.IE035, err), }) + }) - // Register indexer error metrics so we can track any errors that happen - // inside the agent - registerIndexerErrorMetrics(metrics) - - const indexingStatusResolver = new IndexingStatusResolver({ - logger: logger, - statusEndpoint: argv.graphNodeStatusEndpoint, + process.on('uncaughtException', err => { + logger.warn(`Uncaught exception`, { + err: indexerError(IndexerErrorCode.IE036, err), }) + }) + + // -------------------------------------------------------------------------------- + // * Metrics Server + // -------------------------------------------------------------------------------- + const metrics = createMetrics() + createMetricsServer({ + logger: logger.child({ component: 'MetricsServer' }), + registry: metrics.registry, + port: argv.metricsPort, + }) + + // Register indexer error metrics so we can track any errors that happen + // inside the agent + registerIndexerErrorMetrics(metrics) + + // -------------------------------------------------------------------------------- + // * Graph Node + // ---------------------------------------------------------------- ---------------- + const graphNode = new GraphNode( + logger, + argv.graphNodeAdminEndpoint, + argv.graphNodeQueryEndpoint, + argv.graphNodeStatusEndpoint, + argv.indexNodeIds, + ) - // Parse the Network Subgraph optional argument - const networkSubgraphDeploymentId = argv.networkSubgraphDeployment - ? new SubgraphDeploymentID(argv.networkSubgraphDeployment) - : undefined - - const networkSubgraph = await NetworkSubgraph.create({ - logger, - endpoint: argv.networkSubgraphEndpoint, - deployment: - networkSubgraphDeploymentId !== undefined - ? { - indexingStatusResolver: indexingStatusResolver, - deployment: networkSubgraphDeploymentId, - graphNodeQueryEndpoint: argv.graphNodeQueryEndpoint, - } - : undefined, + // -------------------------------------------------------------------------------- + // * Database - Connection + // -------------------------------------------------------------------------------- + logger.info('Connect to database', { + host: argv.postgresHost, + port: argv.postgresPort, + database: argv.postgresDatabase, + poolMax: argv.postgresPoolSize, + }) + const sequelize = await connectDatabase({ + logging: undefined, + host: argv.postgresHost, + port: argv.postgresPort, + username: argv.postgresUsername, + password: argv.postgresPassword, + database: argv.postgresDatabase, + poolMin: 0, + poolMax: argv.postgresPoolSize, + }) + logger.info('Successfully connected to database') + + // -------------------------------------------------------------------------------- + // * Database - Migrations + // -------------------------------------------------------------------------------- + logger.info(`Run database migrations`) + + // If the application is being executed using ts-node __dirname may be in /src rather than /dist + const migrations_path = __dirname.includes('dist') + ? path.join(__dirname, '..', 'db', 'migrations', '*.js') + : path.join(__dirname, '..', '..', 'dist', 'db', 'migrations', '*.js') + + try { + const umzug = new Umzug({ + migrations: { + glob: migrations_path, + }, + context: { + queryInterface: sequelize.getQueryInterface(), + logger, + graphNodeAdminEndpoint: argv.graphNodeAdminEndpoint, + networkSpecifications, + }, + storage: new SequelizeStorage({ sequelize }), + logger: console, }) - - const networkProvider = await Network.provider( - logger, - metrics, - argv.ethereum, - argv.ethereumPollingInterval, - ) - - const networkMeta = await networkProvider.getNetwork() - - logger.info(`Connect to contracts`, { - network: networkMeta.name, - chainId: networkMeta.chainId, - providerNetworkChainID: networkProvider.network.chainId, + const pending = await umzug.pending() + const executed = await umzug.executed() + logger.debug(`Migrations status`, { pending, executed }) + await umzug.up() + } catch (err) { + logger.fatal(`Failed to run database migrations`, { + err: indexerError(IndexerErrorCode.IE001, err), }) + process.exit(1) + } + logger.info(`Successfully ran database migrations`) + + // -------------------------------------------------------------------------------- + // * Database - Sync Models + // -------------------------------------------------------------------------------- + logger.info(`Sync database models`) + const managementModels = defineIndexerManagementModels(sequelize) + const queryFeeModels = defineQueryFeeModels(sequelize) + await sequelize.sync() + logger.info(`Successfully synced database models`) + + // -------------------------------------------------------------------------------- + // * Networks + // -------------------------------------------------------------------------------- + logger.info('Connect to network/s', { + networks: networkSpecifications.map(spec => spec.networkIdentifier), + }) + + const networks: Network[] = await pMap( + networkSpecifications, + async (spec: NetworkSpecification) => + Network.create(logger, spec, queryFeeModels, graphNode, metrics), + ) - logger.info(`Connect wallet`, { - network: networkMeta.name, - chainId: networkMeta.chainId, - }) - let wallet = Wallet.fromMnemonic(argv.mnemonic) - wallet = wallet.connect(networkProvider) - logger.info(`Connected wallet`) - - let contracts = undefined - try { - contracts = await connectContracts(wallet, networkMeta.chainId) - } catch (err) { - logger.error( - `Failed to connect to contracts, please ensure you are using the intended Ethereum network`, - { - err, - }, - ) - process.exit(1) - } - logger.info(`Successfully connected to contracts`, { - curation: contracts.curation.address, - disputeManager: contracts.disputeManager.address, - epochManager: contracts.epochManager.address, - gns: contracts.gns.address, - rewardsManager: contracts.rewardsManager.address, - serviceRegistry: contracts.serviceRegistry.address, - staking: contracts.staking.address, - token: contracts.token.address, - }) + // -------------------------------------------------------------------------------- + // * Indexer Management (GraphQL) Server + // -------------------------------------------------------------------------------- + const multiNetworks = new MultiNetworks( + networks, + (n: Network) => n.specification.networkIdentifier, + ) - const indexerAddress = toAddress(argv.indexerAddress) + const indexerManagementClient = await createIndexerManagementClient({ + models: managementModels, + graphNode, + indexNodeIDs: argv.indexNodeIds, + logger, + defaults: { + globalIndexingRule: { + // TODO: Update this, there will be defaults per network + allocationAmount: BigNumber.from(100), + parallelAllocations: 1, + }, + }, + multiNetworks, + }) + + // -------------------------------------------------------------------------------- + // * Indexer Management Server + // -------------------------------------------------------------------------------- + logger.info('Launch indexer management API server', { + port: argv.indexerManagementPort, + }) + await createIndexerManagementServer({ + logger, + client: indexerManagementClient, + port: argv.indexerManagementPort, + }) + logger.info(`Successfully launched indexer management API server`) + + // -------------------------------------------------------------------------------- + // * Cost Model Automation + // -------------------------------------------------------------------------------- + await startCostModelAutomation({ + logger, + networks, + indexerManagement: indexerManagementClient, + metrics, + }) + + // -------------------------------------------------------------------------------- + // * Syncing Server + // -------------------------------------------------------------------------------- + logger.info(`Launch syncing server`) + + await createSyncingServer({ + logger, + networkSubgraphs: await multiNetworks.map( + async network => network.networkSubgraph, + ), + port: argv.syncingPort, + }) + logger.info(`Successfully launched syncing server`) + + // -------------------------------------------------------------------------------- + // * Operator + // -------------------------------------------------------------------------------- + const operators: Operator[] = await pMap( + networkSpecifications, + async (spec: NetworkSpecification) => + new Operator(logger, indexerManagementClient, spec), + ) - const epochSubgraph = await EpochSubgraph.create(argv.epochSubgraphEndpoint) + // -------------------------------------------------------------------------------- + // * The Agent itself + // -------------------------------------------------------------------------------- + const agent = new Agent( + logger, + metrics, + graphNode, + operators, + indexerManagementClient, + networks, + argv.offchainSubgraphs.map((s: string) => new SubgraphDeploymentID(s)), + argv.enableAutoMigrationSupport, + argv.deploymentManagement, + ) + await agent.start() +} - const networkMonitor = new NetworkMonitor( - resolveChainId(networkMeta.chainId), - contracts, - toAddress(indexerAddress), - logger.child({ component: 'NetworkMonitor' }), - indexingStatusResolver, - networkSubgraph, - networkProvider, - epochSubgraph, +// Review CLI arguments, emit non-interrupting warnings about expected behavior. +// Perform this check immediately after parsing the command line arguments. +// Ideally, this check could be made inside yargs.check, but we can't access a Logger +// instance in that context. +export function reviewArgumentsForWarnings(argv: AgentOptions, logger: Logger) { + const { + gasIncreaseTimeout, + gasIncreaseFactor, + rebateClaimThreshold, + voucherRedemptionThreshold, + rebateClaimMaxBatchSize, + voucherRedemptionMaxBatchSize, + collectReceiptsEndpoint, + } = argv + + logger.debug('Reviewing Indexer Agent configuration') + + const advisedGasIncreaseTimeout = 90000 + const advisedGasIncreaseFactor = 1.5 + const advisedRebateClaimMaxBatchSize = 200 + const advisedVoucherRedemptionMaxBatchSize = 200 + + if (collectReceiptsEndpoint) { + logger.warn( + "The option '--collect-receipts-endpoint' is deprecated. " + + "Please use the option '--gateway-endpoint' to inform the Gateway base URL.", ) + } - logger.info('Connect to database', { - host: argv.postgresHost, - port: argv.postgresPort, - database: argv.postgresDatabase, - }) - const sequelize = await connectDatabase({ - logging: undefined, - host: argv.postgresHost, - port: argv.postgresPort, - username: argv.postgresUsername, - password: argv.postgresPassword, - database: argv.postgresDatabase, - }) - logger.info('Successfully connected to database') - - // Automatic database migrations - logger.info(`Run database migrations`) - - // If the application is being executed using ts-node __dirname may be in /src rather than /dist - const migrations_path = __dirname.includes('dist') - ? path.join(__dirname, '..', 'db', 'migrations', '*.js') - : path.join(__dirname, '..', '..', 'dist', 'db', 'migrations', '*.js') - - try { - const umzug = new Umzug({ - migrations: { - glob: migrations_path, - }, - context: { - queryInterface: sequelize.getQueryInterface(), - logger, - indexingStatusResolver, - graphNodeAdminEndpoint: argv.graphNodeAdminEndpoint, - networkMonitor, - }, - storage: new SequelizeStorage({ sequelize }), - logger: console, - }) - const pending = await umzug.pending() - const executed = await umzug.executed() - logger.debug(`Migrations status`, { pending, executed }) - await umzug.up() - } catch (err) { - logger.fatal(`Failed to run database migrations`, { - err: indexerError(IndexerErrorCode.IE001, err), - }) - process.exit(1) - } - logger.info(`Successfully ran database migrations`) - - logger.info(`Sync database models`) - const managementModels = defineIndexerManagementModels(sequelize) - const queryFeeModels = defineQueryFeeModels(sequelize) - await sequelize.sync() - logger.info(`Successfully synced database models`) - - logger.info('Connect to network') - const maxGasFee = argv.baseFeeGasMax || argv.gasPriceMax - const network = await Network.create( - logger, - networkProvider, - contracts, - wallet, - indexerAddress, - argv.publicIndexerUrl, - argv.indexerGeoCoordinates, - networkSubgraph, - argv.restakeRewards, - argv.rebateClaimThreshold, - argv.rebateClaimBatchThreshold, - argv.rebateClaimMaxBatchSize, - argv.poiDisputeMonitoring, - argv.poiDisputableEpochs, - argv.gasIncreaseTimeout, - argv.gasIncreaseFactor, - maxGasFee, - argv.transactionAttempts, + if (gasIncreaseTimeout < advisedGasIncreaseTimeout) { + logger.warn( + `Gas increase timeout is set to less than ${ + gasIncreaseTimeout / 1000 + } seconds. This may lead to high gas usage`, + { gasIncreaseTimeout: gasIncreaseTimeout / 1000.0 }, ) - logger.info('Successfully connected to network', { - restakeRewards: argv.restakeRewards, - }) + } - const receiptCollector = new AllocationReceiptCollector({ - logger, - metrics, - transactionManager: network.transactionManager, - models: queryFeeModels, - allocationExchange: network.contracts.allocationExchange, - collectEndpoint: argv.collectReceiptsEndpoint, - voucherRedemptionThreshold: argv.voucherRedemptionThreshold, - voucherRedemptionBatchThreshold: argv.voucherRedemptionBatchThreshold, - voucherRedemptionMaxBatchSize: argv.voucherRedemptionMaxBatchSize, - }) - await receiptCollector.queuePendingReceiptsFromDatabase() - - logger.info('Launch indexer management API server') - const allocationManagementMode = - AllocationManagementMode[ - argv.allocationManagement.toUpperCase() as keyof typeof AllocationManagementMode - ] - const indexerManagementClient = await createIndexerManagementClient({ - models: managementModels, - address: indexerAddress, - contracts, - indexingStatusResolver, - indexNodeIDs: argv.indexNodeIds, - deploymentManagementEndpoint: argv.graphNodeAdminEndpoint, - networkSubgraph, - logger, - defaults: { - globalIndexingRule: { - allocationAmount: argv.defaultAllocationAmount, - parallelAllocations: 1, - }, - }, - features: { - injectDai: argv.injectDai, + if (gasIncreaseFactor > advisedGasIncreaseTimeout) { + logger.warn( + `Gas increase factor is set to > ${advisedGasIncreaseFactor}. ` + + 'This may lead to high gas usage', + { gasIncreaseFactor: gasIncreaseFactor }, + ) + } + if (rebateClaimThreshold < voucherRedemptionThreshold) { + logger.warn( + 'Rebate single minimum claim value is less than voucher minimum redemption value, ' + + 'but claims depend on redemptions', + { + voucherRedemptionThreshold: formatGRT(voucherRedemptionThreshold), + rebateClaimThreshold: formatGRT(rebateClaimThreshold), }, - transactionManager: network.transactionManager, - receiptCollector, - networkMonitor, - allocationManagementMode, - autoAllocationMinBatchSize: argv.autoAllocationMinBatchSize, - ipfsEndpoint: argv.ipfsEndpoint, - autoGraftResolverLimit: argv.autoGraftResolverLimit, - }) - - await createIndexerManagementServer({ - logger, - client: indexerManagementClient, - port: argv.indexerManagementPort, - }) - logger.info(`Successfully launched indexer management API server`) - - const indexer = new Indexer( - logger, - argv.graphNodeAdminEndpoint, - indexingStatusResolver, - indexerManagementClient, - argv.indexNodeIds, - argv.defaultAllocationAmount, - indexerAddress, - allocationManagementMode, - argv.ipfsEndpoint, - argv.autoGraftResolverLimit, ) + } - if (networkSubgraphDeploymentId !== undefined) { - // Make sure the network subgraph is being indexed - // - // TODO: once the Network Subgraph is published to the Network, we can use the - // `formatDeploymentName` function instead of using a hardcoded deployment name. - await indexer.ensure( - `graphprotocol/network-subgraph/${networkSubgraphDeploymentId.ipfsHash}`, - networkSubgraphDeploymentId, - ) - - // Validate if the Network Subgraph belongs to the current provider's network. - // This check must be performed after we ensure the Network Subgraph is being indexed. - try { - await validateNetworkId( - networkMeta, - argv.networkSubgraphDeployment, - indexingStatusResolver, - logger, - ) - } catch (e) { - logger.critical('Failed to validate Network Subgraph. Exiting.', e) - process.exit(1) - } - } + if (rebateClaimThreshold === 0) { + logger.warn( + `Minimum query fee rebate value is 0 GRT, which may lead to claiming unprofitable rebates`, + ) + } - // Monitor ETH balance of the operator and write the latest value to a metric - await monitorEthBalance(logger, wallet, metrics) + if (rebateClaimMaxBatchSize > advisedRebateClaimMaxBatchSize) { + logger.warn( + `Setting the max batch size for rebate claims to more than ${advisedRebateClaimMaxBatchSize}` + + 'may result in batches that are too large to fit into a block', + { rebateClaimMaxBatchSize: rebateClaimMaxBatchSize }, + ) + } - logger.info(`Launch syncing server`) - await createSyncingServer({ - logger, - networkSubgraph, - port: argv.syncingPort, - }) - logger.info(`Successfully launched syncing server`) - - startCostModelAutomation({ - logger, - ethereum: networkProvider, - contracts: network.contracts, - indexerManagement: indexerManagementClient, - injectDai: argv.injectDai, - daiContractAddress: toAddress(argv.daiContract), - metrics, - }) + if (voucherRedemptionThreshold == 0) { + logger.warn( + `Minimum voucher redemption value is 0 GRT, which may lead to redeeming unprofitable vouchers`, + ) + } - await startAgent({ - logger, - metrics, - indexer, - network, - networkMonitor, - networkSubgraph, - allocateOnNetworkSubgraph: argv.allocateOnNetworkSubgraph, - registerIndexer: argv.register, - offchainSubgraphs: argv.offchainSubgraphs.map( - (s: string) => new SubgraphDeploymentID(s), - ), - receiptCollector, - }) - }, + if (voucherRedemptionMaxBatchSize > advisedVoucherRedemptionMaxBatchSize) { + logger.warn( + `Setting the max batch size for voucher redemptions to more than ${advisedVoucherRedemptionMaxBatchSize} ` + + 'may result in batches that are too large to fit into a block', + { voucherRedemptionMaxBatchSize: voucherRedemptionMaxBatchSize }, + ) + } } -// Compares the CAIP-2 chain ID between the Ethereum provider and the Network Subgraph and requires -// they are equal. -async function validateNetworkId( - providerNetwork: NetworkMetadata, - networkSubgraphDeploymentIpfsHash: string, - indexingStatusResolver: IndexingStatusResolver, - logger: Logger, -) { - const subgraphNetworkId = new SubgraphDeploymentID( - networkSubgraphDeploymentIpfsHash, - ) - const { network: subgraphNetworkChainName } = - await indexingStatusResolver.subgraphFeatures(subgraphNetworkId) - - if (!subgraphNetworkChainName) { - // This is unlikely to happen because we expect that the Network Subgraph manifest is valid. - const errorMsg = 'Failed to fetch the networkId for the Network Subgraph' - logger.error(errorMsg, { networkSubgraphDeploymentIpfsHash }) - throw new Error(errorMsg) +// Retrieves the network identifier in contexts where we haven't yet instantiated the JSON +// RPC Provider, which has additional and more complex dependencies. +async function fetchChainId(url: string): Promise { + const payload = { + jsonrpc: '2.0', + id: 0, + method: 'eth_chainId', } - - const providerChainId = resolveChainId(providerNetwork.chainId) - const networkSubgraphChainId = resolveChainId(subgraphNetworkChainName) - if (providerChainId !== networkSubgraphChainId) { - const errorMsg = - 'The configured provider and the Network Subgraph have different CAIP-2 chain IDs. ' + - 'Please ensure that both Network Subgraph and the Ethereum provider are correctly configured.' - logger.error(errorMsg, { - networkSubgraphDeploymentIpfsHash, - networkSubgraphChainId, - providerChainId, - }) - throw new Error(errorMsg) + try { + const response = await axios.post(url, payload) + if (response.status !== 200) { + throw `HTTP ${response.status}` + } + if (!response.data || !response.data.result) { + throw `Received invalid response body from provider: ${response.data}` + } + return parseInt(response.data.result, 16) + } catch (error) { + throw new Error(`Failed to fetch chainID from provider: ${error}`) } } diff --git a/packages/indexer-agent/src/cost.ts b/packages/indexer-agent/src/cost.ts index 6b36de232..aa564786d 100644 --- a/packages/indexer-agent/src/cost.ts +++ b/packages/indexer-agent/src/cost.ts @@ -8,7 +8,7 @@ import { timer, Address, } from '@graphprotocol/common-ts' -import { IndexerManagementClient } from '@graphprotocol/indexer-common' +import { IndexerManagementClient, Network } from '@graphprotocol/indexer-common' import { Contract, providers } from 'ethers' interface CostModelAutomationMetrics { @@ -30,37 +30,46 @@ const registerMetrics = (metrics: Metrics): CostModelAutomationMetrics => ({ }), }) -export interface CostModelAutomationOptions { +// Public API +export interface StartCostModelAutomationOptions { + logger: Logger + networks: Network[] + indexerManagement: IndexerManagementClient + metrics: Metrics +} + +// Internal API +interface CostModelAutomationOptions { logger: Logger ethereum: providers.BaseProvider contracts: NetworkContracts indexerManagement: IndexerManagementClient - injectDai: boolean daiContractAddress: Address - metrics: Metrics + metrics: CostModelAutomationMetrics } -export const startCostModelAutomation = ({ +export const startCostModelAutomation = async ({ logger, - ethereum, - contracts, + networks, indexerManagement, - injectDai, - daiContractAddress, metrics, -}: CostModelAutomationOptions): void => { +}: StartCostModelAutomationOptions): Promise => { logger = logger.child({ component: 'CostModelAutomation' }) const automationMetrics = registerMetrics(metrics) - if (injectDai) { - monitorAndInjectDai({ + // We could have this run per network but we probably only need to run it for Mainnet + const mainnet = networks.find( + n => n.specification.networkIdentifier === 'eip155:1', + ) + if (mainnet && mainnet.specification.dai.inject) { + await monitorAndInjectDai({ logger, - ethereum, - contracts, + ethereum: mainnet.networkProvider, + contracts: mainnet.contracts, indexerManagement, metrics: automationMetrics, - daiContractAddress, + daiContractAddress: mainnet.specification.dai.contractAddress, }) } } @@ -74,9 +83,7 @@ const monitorAndInjectDai = async ({ indexerManagement, metrics, daiContractAddress, -}: Omit & { - metrics: CostModelAutomationMetrics -}): Promise => { +}: CostModelAutomationOptions): Promise => { // Identify the decimals used by the DAI or USDC contract const chainId = ethereum.network.chainId const stableCoin = new Contract(daiContractAddress, ERC20_ABI, ethereum) diff --git a/packages/indexer-agent/src/db/migrations/11-add-protocol-network-field.ts b/packages/indexer-agent/src/db/migrations/11-add-protocol-network-field.ts new file mode 100644 index 000000000..f865c9bdb --- /dev/null +++ b/packages/indexer-agent/src/db/migrations/11-add-protocol-network-field.ts @@ -0,0 +1,454 @@ +import { Logger } from '@graphprotocol/common-ts' +import { specification } from '@graphprotocol/indexer-common' +import { QueryTypes, DataTypes, QueryInterface, Op } from 'sequelize' + +const MANUAL_CONSTRAINT_NAME_FRAGMENT = '_composite_manual' + +interface MigrationContext { + queryInterface: QueryInterface + logger: Logger + networkSpecifications: specification.NetworkSpecification[] +} + +interface Context { + context: MigrationContext +} + +interface ForeignKey { + table: string + // This table's column that holds the FK + columnName: string + refColumnName: string +} + +interface MigrationInput { + table: string + oldPrimaryKeyColumns: string[] + newColumn: string + referencedBy?: ForeignKey[] +} + +interface MigrationTarget extends MigrationInput { + oldPrimaryKeyConstraint: string + newPrimaryKeyConstraint: string +} + +const defaults: Pick = { + newColumn: 'protocolNetwork', +} + +const migrationInputs: MigrationInput[] = [ + { + table: 'Actions', + oldPrimaryKeyColumns: ['id'], + }, + { + table: 'IndexingRules', + oldPrimaryKeyColumns: ['identifier'], + }, + { + table: 'POIDisputes', + oldPrimaryKeyColumns: ['allocationID'], + }, + { + table: 'allocation_receipts', + oldPrimaryKeyColumns: ['id', 'allocation'], + }, + { + table: 'vouchers', + oldPrimaryKeyColumns: ['allocation'], + }, + { + table: 'transfer_receipts', + oldPrimaryKeyColumns: ['id', 'signer'], + }, + { + table: 'transfers', + oldPrimaryKeyColumns: ['signer', 'routingId'], + }, + { + table: 'allocation_summaries', + oldPrimaryKeyColumns: ['allocation'], + referencedBy: [ + { + table: 'allocation_receipts', + columnName: 'allocation', + refColumnName: 'allocation', + }, + { + table: 'transfers', + columnName: 'allocation', + refColumnName: 'allocation', + }, + { + table: 'vouchers', + columnName: 'allocation', + refColumnName: 'allocation', + }, + ], + }, +].map(input => ({ ...input, ...defaults })) + +export async function up({ context }: Context): Promise { + const { queryInterface, networkSpecifications, logger } = context + const m = new Migration(queryInterface, logger) + + // This migration requires that just one network is used if the database holds previous data. + const hasExistingRows = await m.hasExistingRows(migrationInputs) + if (hasExistingRows && networkSpecifications.length !== 1) { + throw new Error( + `Migration expects only one network specification, but found ${networkSpecifications.length}. ` + + `Please avoid using other network specifications until this migration completes. ` + + `Make sure to use the same network specification that matches the data in the database.`, + ) + } + const networkChainId = networkSpecifications[0].networkIdentifier + + for (const input of migrationInputs) { + await m.addPrimaryKeyMigration(input, networkChainId) + } +} + +export async function down({ context }: Context): Promise { + const { queryInterface, networkSpecifications, logger } = context + const m = new Migration(queryInterface, logger) + + // This migration requires that just one network is used + if (networkSpecifications.length !== 1) { + throw new Error( + `Migration expects only one network specification, but found ${networkSpecifications.length}. ` + + `Please avoid using other network specifications until this migration completes. ` + + `Make sure to use the same network specification for the network data that will be left in the database.`, + ) + } + const networkChainId = networkSpecifications[0].networkIdentifier + + for (const input of migrationInputs) { + await m.removePrimaryKeyMigration(input, networkChainId) + + // TODO: Cascade the removal of primary keys + // TODO: Restore the foreign keys of cascaded constraint removals + } +} + +// Helper migration class +class Migration { + queryInterface: QueryInterface + logger: Logger + constructor(queryInterface: QueryInterface, logger: Logger) { + this.queryInterface = queryInterface + this.logger = logger + } + + // Main migration steps in the UP direction + async addPrimaryKeyMigration( + input: MigrationInput, + networkChainId: string, + ): Promise { + // Skip migration for this table if it doesn't exist. + const tableExists = await this.checkTableExists(input) + if (!tableExists) { + this.logger.info( + `Table '${input.table}' does not exist, migration not necessary`, + ) + return + } + // Skip migration for this table if it already has the 'protocolNetwork' column + const columnExists = await this.checkColumnExists(input) + if (columnExists) { + return + } + + // Infer primary key constraint names + const target = await this.processMigrationInput(input) + + // Add protocolNetwork columns + await this.addColumn(target) + + // Loosen constraints + await this.removeConstraint(target) + + // Populate the `protocolNetwork` columns with the provided network ID + await this.updateTable(target, networkChainId) + + // Restore constraints + await this.restorePrimaryKeyConstraint(target) + + // Alter the `protocolNetwork` columns to be NOT NULL + await this.alterColumn(target) + + // Restore broken foreign keys + await this.restoreBrokenForeignKeys(target) + } + + // Main migration steps in the DOWN direction + async removePrimaryKeyMigration( + input: MigrationInput, + networkChainId: string, + ): Promise { + // Infer primary key constraint names + const target = await this.processMigrationInputDown(input) + + // Drop the new primary key constraint + await this.removeNewConstraint(target) + + // Delete rows from other protocol networks + await this.deleteRowsFromOtherNetworks(target, networkChainId) + + // Drop the new columns + await this.dropColumn(target) + + // Restore the old primary key constraint + await this.restoreOldPrimaryKeyConstraint(target) + } + + async checkTableExists( + input: Pick, + ): Promise { + const exists = await this.queryInterface.tableExists(input.table) + if (!exists) { + return false + } + return true + } + + async checkColumnExists(input: MigrationInput): Promise { + this.logger.debug( + `Checking if table '${input.table}' has the '${input.newColumn}' column`, + ) + const tableSpecification = await this.queryInterface.describeTable( + input.table, + ) + const column = tableSpecification[input.newColumn] + if (!column) { + return false + } + // Check if the existing column is a primary key (it should be) + if (!column.primaryKey) { + throw new Error( + `Column '${input.newColumn}' of table '${input.table}' exists, but it is not a primary key.`, + ) + } + this.logger.info( + `Column '${input.newColumn}' of table '${input.table}' already exists, migration not necessary`, + ) + return true + } + + // Only for the UP step + async processMigrationInput(input: MigrationInput): Promise { + this.logger.debug(`Inferring primary key name for table '${input.table}'`) + const oldPrimaryKeyConstraint = + await this.getPrimaryKeyConstraintName(input) + this.logger.debug( + `Table '${input.table}' existing primary key name is '${oldPrimaryKeyConstraint}'`, + ) + const newPrimaryKeyConstraint = + oldPrimaryKeyConstraint + MANUAL_CONSTRAINT_NAME_FRAGMENT + this.logger.debug( + `Table '${input.table}' updated primary key name will be '${newPrimaryKeyConstraint}'`, + ) + return { + ...input, + oldPrimaryKeyConstraint, + newPrimaryKeyConstraint, + } + } + // Only for the DOWN step + async processMigrationInputDown( + input: MigrationInput, + ): Promise { + const currentPrimaryKeyConstraint = + await this.getPrimaryKeyConstraintName(input) + let previousPrimaryKeyConstraint + if (currentPrimaryKeyConstraint.endsWith(MANUAL_CONSTRAINT_NAME_FRAGMENT)) { + previousPrimaryKeyConstraint = currentPrimaryKeyConstraint.replace( + MANUAL_CONSTRAINT_NAME_FRAGMENT, + '', + ) + } else { + previousPrimaryKeyConstraint = `{input.table}_pkey` + } + + return { + ...input, + newPrimaryKeyConstraint: currentPrimaryKeyConstraint, + oldPrimaryKeyConstraint: previousPrimaryKeyConstraint, + } + } + + async getPrimaryKeyConstraintName(target: MigrationInput): Promise { + const result: null | { constraint?: string } = + await this.queryInterface.sequelize.query( + ` +SELECT + con.conname as constraint +FROM + pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace +WHERE + nsp.nspname = 'public' + AND rel.relname = :table + AND con.contype = 'p'; +`, + { + type: QueryTypes.SELECT, + replacements: { table: target.table }, + plain: true, + raw: true, + }, + ) + + if (!result || !result.constraint) { + throw new Error( + `Failed to infer primary key constraint name for table '${target.table}'`, + ) + } + return result.constraint + } + + async addColumn(target: MigrationTarget): Promise { + this.logger.info( + `Add '${target.newColumn}' column to ${target.table} table`, + ) + + // Note: we set it to be nullable, but it is only for this migration. At the end we + // set it back to not null. + await this.queryInterface.addColumn(target.table, target.newColumn, { + type: DataTypes.STRING, + allowNull: true, + }) + } + + async dropColumn(target: MigrationTarget): Promise { + const tables = await this.queryInterface.showAllTables() + if (tables.includes(target.table)) { + this.logger.info( + `Drop '${target.newColumn}' column from ${target.table} table`, + ) + await this.queryInterface.removeColumn(target.table, target.newColumn) + } + } + + async updateTable(target: MigrationTarget, value: string) { + const values = { [target.newColumn]: value } + const where = { [target.newColumn]: null } + this.logger.info( + `Set '${target.table}' table '${target.newColumn}' column to '${value}'`, + ) + await this.queryInterface.bulkUpdate(target.table, values, where) + } + + async alterColumn(target: MigrationTarget) { + this.logger.info( + `Altering ${target.table} table ${target.newColumn} to be non-nullable`, + ) + await this.queryInterface.changeColumn(target.table, target.newColumn, { + type: DataTypes.STRING, + allowNull: false, + }) + } + + async removeConstraint(target: MigrationTarget) { + this.logger.info( + `Temporarily removing primary key constraints from ${target.table}`, + ) + const sql = `ALTER TABLE "${target.table}" DROP CONSTRAINT "${target.oldPrimaryKeyConstraint}" CASCADE` + await this.queryInterface.sequelize.query(sql) + } + + async restorePrimaryKeyConstraint(target: MigrationTarget) { + this.logger.info(`Restoring primary key constraints from ${target.table}`) + await this.queryInterface.addConstraint(target.table, { + fields: [...target.oldPrimaryKeyColumns, target.newColumn], + type: 'primary key', + name: target.newPrimaryKeyConstraint, + }) + } + + async restoreBrokenForeignKeys(target: MigrationTarget) { + if (!target.referencedBy || target.referencedBy.length === 0) { + return + } + this.logger.info( + `Restoring broken foreign keys for tables that depend on table '${target.table}'`, + ) + for (const dependent of target.referencedBy) { + this.logger.debug( + `Restoring foreing key between tables '${dependent.table}' and '${target.table}'`, + ) + const constraintName = `${dependent.table}_${target.table}_fkey` + + const createConstraintSql = ` + ALTER TABLE "${dependent.table}" + ADD CONSTRAINT "${constraintName}" FOREIGN KEY ("${dependent.columnName}", "${target.newColumn}") + REFERENCES "${target.table}" ("${dependent.refColumnName}", "${target.newColumn}") + ON UPDATE CASCADE ON DELETE CASCADE NOT VALID; +` + // PostgreSQL docs suggests doing this in two steps to avoid unecessary locks + const validateConstraintSql = `ALTER TABLE "${dependent.table}" VALIDATE CONSTRAINT "${constraintName}";` + + await this.queryInterface.sequelize.query(createConstraintSql) + await this.queryInterface.sequelize.query(validateConstraintSql) + } + } + + // Only for the DOWN step + async removeNewConstraint(target: MigrationTarget) { + await this.queryInterface.removeConstraint( + target.table, + target.newPrimaryKeyConstraint, + ) + } + + // Only for the DOWN step + async deleteRowsFromOtherNetworks( + target: MigrationTarget, + networkChainId: string, + ) { + await this.queryInterface.bulkDelete(target.table, { + [target.newColumn]: { + [Op.ne]: networkChainId, + }, + }) + } + + // Only for the DOWN step + async restoreOldPrimaryKeyConstraint(target: MigrationTarget) { + await this.queryInterface.addConstraint(target.table, { + fields: target.oldPrimaryKeyColumns, + type: 'primary key', + name: target.oldPrimaryKeyConstraint, + }) + } + + // Checks if a table has at least one row + async tableHaveRows(table: string): Promise { + if (!(await this.checkTableExists({ table }))) { + this.logger.trace(`Table '${table}' does not exist, ignoring row check.`) + return false + } + const result: null | { count?: number } = + await this.queryInterface.sequelize.query( + `SELECT COUNT(*) AS count FROM "${table}"`, + { + type: QueryTypes.SELECT, + plain: true, + raw: true, + }, + ) + if (!result || !result.count) { + throw new Error(`Invalid query result: ${result}`) + } + this.logger.debug(`Table '${table}' has ${result.count} row(s)`) + return result.count > 0 + } + + // Checks if input tables have at least one row + async hasExistingRows(targets: MigrationInput[]): Promise { + return ( + await Promise.all(targets.map(t => this.tableHaveRows(t.table))) + ).some(Boolean) + } +} diff --git a/packages/indexer-agent/src/db/migrations/11-update-deployment-names.ts b/packages/indexer-agent/src/db/migrations/11-update-deployment-names.ts deleted file mode 100644 index 58dab038f..000000000 --- a/packages/indexer-agent/src/db/migrations/11-update-deployment-names.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { Logger } from '@graphprotocol/common-ts' -import { - formatDeploymentName, - indexerError, - IndexerErrorCode, - IndexingStatusResolver, - NetworkMonitor, - SubgraphDeploymentAssignment, -} from '@graphprotocol/indexer-common' -import { Client } from 'jayson/promise' -import pMap from 'p-map' - -interface MigrationContext { - logger: Logger - indexingStatusResolver: IndexingStatusResolver - graphNodeAdminEndpoint: string - networkMonitor: NetworkMonitor -} - -interface Context { - context: MigrationContext -} - -interface SubgraphRedeployment { - newName: string - ipfsHash: string - nodeId: string -} - -export async function up({ context }: Context): Promise { - const { logger, networkMonitor: networkMonitor } = context - - const clientConstructor = context.graphNodeAdminEndpoint.startsWith('https') - ? Client.https - : Client.http - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const rpc = clientConstructor(context.graphNodeAdminEndpoint as any) - - // Fetch active deployments. - const subgraphDeploymentAssignments = - await context.indexingStatusResolver.subgraphDeploymentsAssignments() - - // Maps assignments to redeployments - const mapper = async (assignment: SubgraphDeploymentAssignment) => - await processAssignment(assignment, networkMonitor, logger) - - // Produces the Redeployments. - const subgraphRedeployments: SubgraphRedeployment[] = ( - await pMap(subgraphDeploymentAssignments, mapper) - ).filter((item): item is SubgraphRedeployment => Boolean(item)) - - // Execute Redeployments over Graph-Node's RPC endpoint - await pMap(subgraphRedeployments, async subgraphRedeployment => - redeploy(rpc, subgraphRedeployment, logger), - ) -} - -export async function down(): Promise { - // Nothing to do here. The old subgraph names should still exist in Graph Node's database. -} - -// Performs redeployment in Graph-Node -async function redeploy( - client: Client, - subgraphRedeployment: SubgraphRedeployment, - logger: Logger, -): Promise { - logger = logger.child({ - ...subgraphRedeployment, - }) - try { - logger.info(`Redeploying subgraph with adjusted name`) - logger.debug(`Sending subgraph_create request`) - const create_response = await client.request('subgraph_create', { - name: subgraphRedeployment.newName, - }) - if (create_response.error) { - throw create_response.error - } - logger.debug(`Sending subgraph_deploy request`) - const deploy_response = await client.request('subgraph_deploy', { - name: subgraphRedeployment.newName, - ipfs_hash: subgraphRedeployment.ipfsHash, - node_id: subgraphRedeployment.nodeId, - }) - if (deploy_response.error) { - throw deploy_response.error - } - logger.info(`Successfully redeployed subgraph with a fixed name`) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE026, error) - logger.error(`Failed to redeploy subgraph with a fixed name`, { err }) - throw err - } -} - -// Tentatively converts a `SubgraphDeploymentAssignment` into a `SubgraphRedeployment` -async function processAssignment( - assignment: SubgraphDeploymentAssignment, - networkMonitor: NetworkMonitor, - logger: Logger, -): Promise { - logger.debug( - `Querying the Network Subgraph for more details on subgraph deployment ${assignment.id}`, - ) - const deployment = await networkMonitor.subgraphDeployment( - assignment.id.ipfsHash, - ) - if (!deployment) { - logger.info( - `Subgraph deployment ${assignment.id} was not found in Network Subgraph. Skipping its redeployment`, - ) - return undefined - } - return { - newName: formatDeploymentName(deployment), - ipfsHash: assignment.id.ipfsHash, - nodeId: assignment.node, - } -} diff --git a/packages/indexer-agent/src/index.ts b/packages/indexer-agent/src/index.ts index 0e1b07810..ebe961689 100644 --- a/packages/indexer-agent/src/index.ts +++ b/packages/indexer-agent/src/index.ts @@ -1,22 +1,73 @@ +import { createLogger } from '@graphprotocol/common-ts' import * as yargs from 'yargs' +import { + start, + createNetworkSpecification, + reviewArgumentsForWarnings, + AgentOptions, + run, +} from './commands/start' +import { + startMultiNetwork, + parseNetworkSpecifications, +} from './commands/start-multi-network' -import start from './commands/start' +const MULTINETWORK_MODE: boolean = + !!process.env.INDEXER_AGENT_MULTINETWORK_MODE && + process.env.INDEXER_AGENT_MULTINETWORK_MODE.toLowerCase() !== 'false' -yargs - .scriptName('indexer-agent') - .env('INDEXER_AGENT') - .command(start) - .fail(function (msg, err, yargs) { - if (err) { - console.error(err) - } else { - console.error(msg) - console.error(` -Usage help... -`) - console.error(yargs.help()) - } - process.exit(1) +function parseArguments(): AgentOptions { + let builder = yargs.scriptName('indexer-agent').env('INDEXER_AGENT') + + // Dynamic argument parser construction based on network mode + if (MULTINETWORK_MODE) { + console.log('Starting the Indexer Agent in multi-network mode') + builder = builder.command(startMultiNetwork) + } else { + console.log('Starting the Indexer Agent in single-network mode') + builder = builder.command(start) + } + + return ( + builder + // eslint-disable-next-line @typescript-eslint/no-unused-vars + .fail(function (msg, err, _yargs) { + console.error('The Indexer Agent command has failed.') + if (err) { + console.error(err) + } else { + console.error(msg) + } + process.exit(1) + }) + .demandCommand( + 1, + 'You need at least one command before continuing.' + + " See 'indexer-agent --help' for usage instructions.", + ) + .help().argv + ) +} + +async function processArgumentsAndRun(args: AgentOptions): Promise { + const logger = createLogger({ + name: 'IndexerAgent', + async: false, + level: args.logLevel, }) - .demandCommand(1, 'Choose a command from the above list') - .help().argv + if (MULTINETWORK_MODE) { + const specifications = parseNetworkSpecifications(args, logger) + await run(args, specifications, logger) + } else { + reviewArgumentsForWarnings(args, logger) + const specification = await createNetworkSpecification(args, logger) + await run(args, [specification], logger) + } +} + +async function main(): Promise { + const args = parseArguments() + await processArgumentsAndRun(args) +} + +void main() diff --git a/packages/indexer-agent/src/indexer.ts b/packages/indexer-agent/src/indexer.ts deleted file mode 100644 index 8c7acaec0..000000000 --- a/packages/indexer-agent/src/indexer.ts +++ /dev/null @@ -1,938 +0,0 @@ -import gql from 'graphql-tag' -import jayson, { Client as RpcClient } from 'jayson/promise' -import { BigNumber, utils } from 'ethers' -import { - formatGRT, - Logger, - SubgraphDeploymentID, -} from '@graphprotocol/common-ts' -import { - IndexingRuleAttributes, - IndexerManagementClient, - INDEXING_RULE_GLOBAL, - indexerError, - IndexerErrorCode, - POIDisputeAttributes, - IndexingStatusResolver, - IndexingStatus, - SubgraphIdentifierType, - parseGraphQLIndexingStatus, - CostModelAttributes, - ActionResult, - ActionItem, - Action, - ActionStatus, - AllocationManagementMode, - ActionInput, - ActionType, - Allocation, - AllocationDecision, - ActionFilter, -} from '@graphprotocol/indexer-common' -import { CombinedError } from '@urql/core' -import pMap from 'p-map' -import yaml from 'yaml' - -const POI_DISPUTES_CONVERTERS_FROM_GRAPHQL: Record< - keyof POIDisputeAttributes, - (x: never) => string | BigNumber | number | null -> = { - allocationID: x => x, - subgraphDeploymentID: x => x, - allocationIndexer: x => x, - allocationAmount: x => x, - allocationProof: x => x, - closedEpoch: x => +x, - closedEpochStartBlockHash: x => x, - closedEpochStartBlockNumber: x => +x, - closedEpochReferenceProof: x => x, - previousEpochStartBlockHash: x => x, - previousEpochStartBlockNumber: x => +x, - previousEpochReferenceProof: x => x, - status: x => x, -} - -/** - * Parses a POI dispute returned from the indexer management GraphQL - * API into normalized form. - */ -const disputeFromGraphQL = ( - dispute: Partial, -): POIDisputeAttributes => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const obj = {} as any - for (const [key, value] of Object.entries(dispute)) { - if (key === '__typename') { - continue - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - obj[key] = (POI_DISPUTES_CONVERTERS_FROM_GRAPHQL as any)[key](value) - } - return obj as POIDisputeAttributes -} - -interface indexNode { - id: string - deployments: string[] -} - -export class Indexer { - statusResolver: IndexingStatusResolver - rpc: RpcClient - indexerManagement: IndexerManagementClient - logger: Logger - indexNodeIDs: string[] - defaultAllocationAmount: BigNumber - indexerAddress: string - allocationManagementMode: AllocationManagementMode - ipfsEndpoint: string - autoGraftResolverLimit: number - - constructor( - logger: Logger, - adminEndpoint: string, - statusResolver: IndexingStatusResolver, - indexerManagement: IndexerManagementClient, - indexNodeIDs: string[], - defaultAllocationAmount: BigNumber, - indexerAddress: string, - allocationManagementMode: AllocationManagementMode, - ipfsUrl: string, - autoGraftResolverLimit: number, - ) { - this.indexerManagement = indexerManagement - this.statusResolver = statusResolver - this.logger = logger - this.indexerAddress = indexerAddress - this.allocationManagementMode = allocationManagementMode - this.autoGraftResolverLimit = autoGraftResolverLimit - this.ipfsEndpoint = ipfsUrl + '/api/v0/cat?arg=' - - if (adminEndpoint.startsWith('https')) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - this.rpc = jayson.Client.https(adminEndpoint as any) - } else { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - this.rpc = jayson.Client.http(adminEndpoint as any) - } - this.indexNodeIDs = indexNodeIDs - this.defaultAllocationAmount = defaultAllocationAmount - } - - async connect(): Promise { - try { - this.logger.info(`Check if indexing status API is available`) - const currentDeployments = await this.subgraphDeployments() - this.logger.info(`Successfully connected to indexing status API`, { - currentDeployments: currentDeployments.map( - deployment => deployment.display, - ), - }) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE024, error) - this.logger.error(`Failed to connect to indexing status API`, { - err, - }) - throw err - } - } - - async subgraphDeployments(): Promise { - return await this.statusResolver.subgraphDeployments() - } - - async indexNodes(): Promise { - try { - const result = await this.statusResolver.statuses - .query( - gql` - { - indexingStatuses { - subgraphDeployment: subgraph - node - } - } - `, - ) - .toPromise() - - if (result.error) { - throw result.error - } - - const indexNodes: indexNode[] = [] - result.data.indexingStatuses.map( - (status: { subgraphDeployment: string; node: string }) => { - const node = indexNodes.find(node => node.id === status.node) - node - ? node.deployments.push(status.subgraphDeployment) - : indexNodes.push({ - id: status.node, - deployments: [status.subgraphDeployment], - }) - }, - ) - - this.logger.trace(`Queried index nodes`, { - indexNodes, - }) - return indexNodes - } catch (error) { - const err = indexerError(IndexerErrorCode.IE018, error) - this.logger.error( - `Failed to query index nodes API (Should get a different IE?)`, - { err }, - ) - throw err - } - } - - async indexingRules(merged: boolean): Promise { - try { - const result = await this.indexerManagement - .query( - gql` - query indexingRules($merged: Boolean!) { - indexingRules(merged: $merged) { - identifier - identifierType - allocationAmount - allocationLifetime - autoRenewal - parallelAllocations - maxAllocationPercentage - minSignal - maxSignal - minStake - minAverageQueryFees - custom - decisionBasis - requireSupported - } - } - `, - { merged }, - ) - .toPromise() - - if (result.error) { - throw result.error - } - this.logger.trace('Fetched indexing rules', { - count: result.data.indexingRules.length, - rules: result.data.indexingRules.map((rule: IndexingRuleAttributes) => { - return { - identifier: rule.identifier, - identifierType: rule.identifierType, - decisionBasis: rule.decisionBasis, - } - }), - }) - return result.data.indexingRules - } catch (error) { - const err = indexerError(IndexerErrorCode.IE025, error) - this.logger.error(`Failed to query indexer management API`, { err }) - throw err - } - } - - async ensureGlobalIndexingRule(): Promise { - try { - const globalRule = await this.indexerManagement - .query( - gql` - query indexingRule($identifier: String!) { - indexingRule(identifier: $identifier, merged: false) { - identifier - identifierType - allocationAmount - decisionBasis - requireSupported - } - } - `, - { identifier: INDEXING_RULE_GLOBAL }, - ) - .toPromise() - - if (!globalRule.data.indexingRule) { - this.logger.info(`Creating default "global" indexing rule`) - - const defaults = { - identifier: INDEXING_RULE_GLOBAL, - identifierType: SubgraphIdentifierType.GROUP, - allocationAmount: this.defaultAllocationAmount.toString(), - parallelAllocations: 1, - decisionBasis: 'rules', - requireSupported: true, - safety: true, - } - - const defaultGlobalRule = await this.indexerManagement - .mutation( - gql` - mutation setIndexingRule($rule: IndexingRuleInput!) { - setIndexingRule(rule: $rule) { - identifier - identifierType - allocationAmount - allocationLifetime - autoRenewal - parallelAllocations - maxAllocationPercentage - minSignal - maxSignal - minStake - minAverageQueryFees - custom - decisionBasis - requireSupported - safety - } - } - `, - { rule: defaults }, - ) - .toPromise() - - if (defaultGlobalRule.error) { - throw defaultGlobalRule.error - } - - this.logger.info(`Created default "global" indexing rule`, { - rule: defaultGlobalRule.data.setIndexingRule, - }) - } - } catch (error) { - const err = indexerError(IndexerErrorCode.IE017, error) - this.logger.warn('Failed to ensure default "global" indexing rule', { - err, - }) - throw err - } - } - - async costModels( - deployments: SubgraphDeploymentID[], - ): Promise { - try { - const result = await this.indexerManagement - .query( - gql` - query costModels($deployments: [String!]!) { - costModels(deployments: $deployments) { - deployment - model - variables - } - } - `, - { - deployments: deployments.map(deployment => deployment.bytes32), - }, - ) - .toPromise() - - if (result.error) { - throw result.error - } - return result.data.costModels - } catch (error) { - this.logger.warn(`Failed to query cost models`, { error }) - throw error - } - } - - async storePoiDisputes( - disputes: POIDisputeAttributes[], - ): Promise { - try { - const result = await this.indexerManagement - .mutation( - gql` - mutation storeDisputes($disputes: [POIDisputeInput!]!) { - storeDisputes(disputes: $disputes) { - allocationID - subgraphDeploymentID - allocationIndexer - allocationAmount - allocationProof - closedEpoch - closedEpochStartBlockHash - closedEpochStartBlockNumber - closedEpochReferenceProof - previousEpochStartBlockHash - previousEpochStartBlockNumber - previousEpochReferenceProof - status - } - } - `, - { disputes: disputes }, - ) - .toPromise() - - if (result.error) { - throw result.error - } - - return result.data.storeDisputes.map( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (dispute: Record) => { - return disputeFromGraphQL(dispute) - }, - ) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE039, error) - this.logger.error('Failed to store potential POI disputes', { - err, - }) - throw err - } - } - - async fetchPOIDisputes( - status: string, - minClosedEpoch: number, - ): Promise { - try { - const result = await this.indexerManagement - .query( - gql` - query disputes($status: String!, $minClosedEpoch: Int!) { - disputes(status: $status, minClosedEpoch: $minClosedEpoch) { - allocationID - subgraphDeploymentID - allocationIndexer - allocationAmount - allocationProof - closedEpoch - closedEpochStartBlockHash - closedEpochStartBlockNumber - closedEpochReferenceProof - previousEpochStartBlockHash - previousEpochStartBlockNumber - previousEpochReferenceProof - status - } - } - `, - { - status, - minClosedEpoch, - }, - ) - .toPromise() - - if (result.error) { - throw result.error - } - - return result.data.disputes.map( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (dispute: Record) => { - return disputeFromGraphQL(dispute) - }, - ) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE039, error) - this.logger.error('Failed to store potential POI disputes', { - err, - }) - throw err - } - } - - async indexingStatus( - deployment: SubgraphDeploymentID, - ): Promise { - try { - const result = await this.statusResolver.statuses - .query( - gql` - query indexingStatus($deployments: [String!]!) { - indexingStatuses(subgraphs: $deployments) { - subgraphDeployment: subgraph - synced - health - fatalError { - handler - message - } - chains { - network - ... on EthereumIndexingStatus { - latestBlock { - number - hash - } - chainHeadBlock { - number - hash - } - } - } - } - } - `, - { deployments: [deployment.ipfsHash] }, - ) - .toPromise() - this.logger.debug(`Query indexing status`, { - deployment, - statuses: result.data, - }) - return ( - result.data.indexingStatuses - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .map((status: any) => parseGraphQLIndexingStatus(status)) - .pop() - ) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE018, error) - this.logger.error(`Failed to query indexing status API`, { - err, - }) - throw err - } - } - - async fetchActions(actionFilter: ActionFilter): Promise { - const result = await this.indexerManagement - .query( - gql` - query actions($filter: ActionFilter!) { - actions(filter: $filter) { - id - type - allocationID - deploymentID - amount - poi - force - source - reason - priority - transaction - status - failureReason - } - } - `, - { filter: actionFilter }, - ) - .toPromise() - - if (result.error) { - throw result.error - } - - return result.data.actions - } - - async queueAction(action: ActionItem): Promise { - let status = ActionStatus.QUEUED - switch (this.allocationManagementMode) { - case AllocationManagementMode.MANUAL: - throw Error( - `Cannot queue actions when AllocationManagementMode = 'MANUAL'`, - ) - case AllocationManagementMode.AUTO: - status = ActionStatus.APPROVED - break - case AllocationManagementMode.OVERSIGHT: - status = ActionStatus.QUEUED - } - - const actionInput = { - ...action.params, - status, - type: action.type, - source: 'indexerAgent', - reason: action.reason, - priority: 0, - } as ActionInput - - const actionResult = await this.indexerManagement - .mutation( - gql` - mutation queueActions($actions: [ActionInput!]!) { - queueActions(actions: $actions) { - id - type - deploymentID - source - reason - priority - status - } - } - `, - { actions: [actionInput] }, - ) - .toPromise() - - if (actionResult.error) { - if ( - actionResult.error instanceof CombinedError && - actionResult.error.message.includes('Duplicate') - ) { - this.logger.warn( - `Action not queued: Already a queued action targeting ${actionInput.deploymentID} from another source`, - { action }, - ) - return [] - } - throw actionResult.error - } - - if (actionResult.data.queueActions.length > 0) { - this.logger.info(`Queued ${action.type} action for execution`, { - queuedAction: actionResult.data.queueActions, - }) - } - - return actionResult.data.queueActions - } - - async createAllocation( - logger: Logger, - deploymentAllocationDecision: AllocationDecision, - mostRecentlyClosedAllocation: Allocation, - ): Promise { - const desiredAllocationAmount = deploymentAllocationDecision.ruleMatch.rule - ?.allocationAmount - ? BigNumber.from( - deploymentAllocationDecision.ruleMatch.rule.allocationAmount, - ) - : this.defaultAllocationAmount - - logger.info(`No active allocation for deployment, creating one now`, { - allocationAmount: formatGRT(desiredAllocationAmount), - }) - - // Skip allocating if the previous allocation for this deployment was closed with 0x00 POI but rules set to un-safe - if ( - deploymentAllocationDecision.ruleMatch.rule?.safety && - mostRecentlyClosedAllocation && - mostRecentlyClosedAllocation.poi === utils.hexlify(Array(32).fill(0)) - ) { - logger.warn( - `Skipping allocation to this deployment as the last allocation to it was closed with a zero POI`, - { - notSafe: !deploymentAllocationDecision.ruleMatch.rule?.safety, - deployment: deploymentAllocationDecision.deployment, - closedAllocation: mostRecentlyClosedAllocation.id, - }, - ) - return - } - - // Send AllocateAction to the queue - await this.queueAction({ - params: { - deploymentID: deploymentAllocationDecision.deployment.ipfsHash, - amount: formatGRT(desiredAllocationAmount), - }, - type: ActionType.ALLOCATE, - reason: deploymentAllocationDecision.reasonString(), - }) - - return - } - - async closeEligibleAllocations( - logger: Logger, - deploymentAllocationDecision: AllocationDecision, - activeDeploymentAllocations: Allocation[], - epoch: number, - ): Promise { - const activeDeploymentAllocationsEligibleForClose = - activeDeploymentAllocations - .filter(allocation => allocation.createdAtEpoch < epoch) - .map(allocation => allocation.id) - // Make sure to close all active allocations on the way out - if (activeDeploymentAllocationsEligibleForClose.length > 0) { - logger.info( - `Deployment is not (or no longer) worth allocating towards, close allocation`, - { - eligibleForClose: activeDeploymentAllocationsEligibleForClose, - }, - ) - await pMap( - // We can only close allocations from a previous epoch; - // try the others again later - activeDeploymentAllocationsEligibleForClose, - async allocation => { - // Send unallocate action to the queue - await this.queueAction({ - params: { - allocationID: allocation, - deploymentID: deploymentAllocationDecision.deployment.ipfsHash, - poi: undefined, - force: false, - }, - type: ActionType.UNALLOCATE, - reason: deploymentAllocationDecision.reasonString(), - } as ActionItem) - }, - { concurrency: 1 }, - ) - } - } - - async refreshExpiredAllocations( - logger: Logger, - deploymentAllocationDecision: AllocationDecision, - expiredAllocations: Allocation[], - ): Promise { - if (deploymentAllocationDecision.ruleMatch.rule?.autoRenewal) { - logger.info(`Reallocating expired allocations`, { - number: expiredAllocations.length, - expiredAllocations: expiredAllocations.map(allocation => allocation.id), - }) - - const desiredAllocationAmount = deploymentAllocationDecision.ruleMatch - .rule?.allocationAmount - ? BigNumber.from( - deploymentAllocationDecision.ruleMatch.rule.allocationAmount, - ) - : this.defaultAllocationAmount - - // Queue reallocate actions to be picked up by the worker - await pMap(expiredAllocations, async allocation => { - await this.queueAction({ - params: { - allocationID: allocation.id, - deploymentID: deploymentAllocationDecision.deployment.ipfsHash, - amount: formatGRT(desiredAllocationAmount), - }, - type: ActionType.REALLOCATE, - reason: `${deploymentAllocationDecision.reasonString()}:allocationExpiring`, // Need to update to include 'ExpiringSoon' - }) - }) - } else { - logger.info( - `Skipping reallocating expired allocation since the corresponding rule has 'autoRenewal' = False`, - { - number: expiredAllocations.length, - expiredAllocations: expiredAllocations.map( - allocation => allocation.id, - ), - }, - ) - } - return - } - - async create(name: string): Promise { - try { - this.logger.info(`Create subgraph name`, { name }) - const response = await this.rpc.request('subgraph_create', { name }) - if (response.error) { - throw response.error - } - this.logger.info(`Successfully created subgraph name`, { name }) - } catch (error) { - if (error.message.includes('already exists')) { - this.logger.debug(`Subgraph name already exists`, { name }) - return - } - throw error - } - } - - // Simple fetch for subgraph manifest - async subgraphManifest(targetDeployment: SubgraphDeploymentID) { - const ipfsFile = await fetch( - this.ipfsEndpoint + targetDeployment.ipfsHash, - { - method: 'POST', - redirect: 'follow', - }, - ) - return yaml.parse(await ipfsFile.text()) - } - - // Recursive function for targetDeployment resolve grafting, add depth until reached to resolverDepth - async resolveGrafting( - name: string, - targetDeployment: SubgraphDeploymentID, - depth: number, - ): Promise { - // Matches "/depth-" followed by one or more digits - const depthRegex = /\/depth-\d+/ - const manifest = await this.subgraphManifest(targetDeployment) - - // No grafting dependency - if (!manifest.features || !manifest.features.includes('grafting')) { - // Ensure sync if at root of dependency - if (depth) { - await this.ensure(name, targetDeployment) - } - return - } - - // Default autoGraftResolverLimit is 0, essentially disabling auto-resolve - if (depth >= this.autoGraftResolverLimit) { - throw indexerError( - IndexerErrorCode.IE074, - `Grafting depth reached limit for auto resolve`, - ) - } - - try { - const baseDeployment = new SubgraphDeploymentID(manifest.graft.base) - let baseName = name.replace(depthRegex, `/depth-${depth}`) - if (baseName === name) { - // add depth suffix if didn't have one from targetDeployment - baseName += `/depth-${depth}` - } - await this.resolveGrafting(baseName, baseDeployment, depth + 1) - - // If base deployment has synced upto the graft block, then ensure the target deployment - // Otherwise just log to come back later - const graftStatus = await this.statusResolver.indexingStatus([ - baseDeployment, - ]) - // If base deployment synced to required block, try to sync the target and - // turn off syncing for the base deployment - if ( - graftStatus[0].chains[0].latestBlock && - graftStatus[0].chains[0].latestBlock.number >= manifest.graft.block - ) { - await this.ensure(name, targetDeployment) - } else { - this.logger.debug( - `Graft base deployment has yet to reach the graft block, try again later`, - ) - } - } catch { - throw indexerError( - IndexerErrorCode.IE074, - `Base deployment hasn't synced to the graft block, try again later`, - ) - } - } - async deploy( - name: string, - deployment: SubgraphDeploymentID, - node_id: string, - ): Promise { - try { - this.logger.info(`Deploy subgraph deployment`, { - name, - deployment: deployment.display, - }) - const response = await this.rpc.request('subgraph_deploy', { - name, - ipfs_hash: deployment.ipfsHash, - node_id: node_id, - }) - if (response.error) { - throw indexerError(IndexerErrorCode.IE026, response.error) - } - this.logger.info(`Successfully deployed subgraph deployment`, { - name, - deployment: deployment.display, - }) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE026, error) - this.logger.error(`Failed to deploy subgraph deployment`, { - name, - deployment: deployment.display, - err, - }) - throw err - } - } - - async remove(deployment: SubgraphDeploymentID): Promise { - try { - this.logger.info(`Remove subgraph deployment`, { - deployment: deployment.display, - }) - const response = await this.rpc.request('subgraph_reassign', { - node_id: 'removed', - ipfs_hash: deployment.ipfsHash, - }) - if (response.error) { - throw response.error - } - this.logger.info(`Successfully removed subgraph deployment`, { - deployment: deployment.display, - }) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE027, error) - this.logger.error(`Failed to remove subgraph deployment`, { - deployment: deployment.display, - err, - }) - } - } - - async reassign( - deployment: SubgraphDeploymentID, - node: string, - ): Promise { - try { - this.logger.info(`Reassign subgraph deployment`, { - deployment: deployment.display, - node, - }) - const response = await this.rpc.request('subgraph_reassign', { - node_id: node, - ipfs_hash: deployment.ipfsHash, - }) - if (response.error) { - throw response.error - } - } catch (error) { - if (error.message.includes('unchanged')) { - this.logger.debug(`Subgraph deployment assignment unchanged`, { - deployment: deployment.display, - node, - }) - return - } - const err = indexerError(IndexerErrorCode.IE028, error) - this.logger.error(`Failed to reassign subgraph deployment`, { - deployment: deployment.display, - err, - }) - throw err - } - } - - async ensure(name: string, deployment: SubgraphDeploymentID): Promise { - try { - // Randomly assign to unused nodes if they exist, - // otherwise use the node with lowest deployments assigned - const indexNodes = (await this.indexNodes()).filter( - (node: { id: string; deployments: Array }) => { - return node.id && node.id !== 'removed' - }, - ) - const usedIndexNodeIDs = indexNodes.map(node => node.id) - const unusedNodes = this.indexNodeIDs.filter( - nodeID => !(nodeID in usedIndexNodeIDs), - ) - - const targetNode = unusedNodes - ? unusedNodes[Math.floor(Math.random() * unusedNodes.length)] - : indexNodes.sort((nodeA, nodeB) => { - return nodeA.deployments.length - nodeB.deployments.length - })[0].id - await this.create(name) - await this.deploy(name, deployment, targetNode) - await this.reassign(deployment, targetNode) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE020, error) - this.logger.error(`Failed to ensure subgraph deployment is indexing`, { - name, - deployment: deployment.display, - err, - }) - } - } -} diff --git a/packages/indexer-agent/src/syncing-server.ts b/packages/indexer-agent/src/syncing-server.ts index 94ed04b0e..4be36ef10 100644 --- a/packages/indexer-agent/src/syncing-server.ts +++ b/packages/indexer-agent/src/syncing-server.ts @@ -4,17 +4,22 @@ import cors from 'cors' import bodyParser from 'body-parser' import morgan from 'morgan' import { Logger } from '@graphprotocol/common-ts' -import { NetworkSubgraph } from '@graphprotocol/indexer-common' +import { parse } from 'graphql' +import { + NetworkMapped, + NetworkSubgraph, + resolveChainId, +} from '@graphprotocol/indexer-common' export interface CreateSyncingServerOptions { logger: Logger - networkSubgraph: NetworkSubgraph + networkSubgraphs: NetworkMapped port: number } export const createSyncingServer = async ({ logger, - networkSubgraph, + networkSubgraphs, port, }: CreateSyncingServerOptions): Promise => { logger = logger.child({ component: 'SyncingServer' }) @@ -38,21 +43,58 @@ export const createSyncingServer = async ({ }) // Network subgraph endpoint - server.post('/network', bodyParser.json(), async (req, res) => { - const { query, variables } = req.body + server.post( + '/network/:networkIdentifier', + bodyParser.json(), + // eslint-disable-next-line @typescript-eslint/no-misused-promises + async (req, res) => { + const { query, variables } = req.body + const { networkIdentifier: unvalidatedNetworkIdentifier } = req.params - if (query.startsWith('mutation') || query.startsWith('subscription')) { - return res.status(405).send('Only queries are supported') - } + if (query.startsWith('mutation') || query.startsWith('subscription')) { + return res.status(405).send('Only queries are supported') + } - const result = await networkSubgraph.query(query, variables) + let networkIdentifier + try { + networkIdentifier = resolveChainId(unvalidatedNetworkIdentifier) + } catch (e) { + return res + .status(404) + .send(`Unknown network identifier: '${unvalidatedNetworkIdentifier}'`) + } - res.status(200).send({ - data: result.data, - errors: result.error ? result.error.graphQLErrors : null, - extensions: result.extensions, - }) - }) + const networkSubgraph = networkSubgraphs[networkIdentifier] + if (!networkSubgraph) { + return res + .status(404) + .send( + `Indexer Agent not configured for network '${networkIdentifier}'`, + ) + } + + let parsedQuery + try { + parsedQuery = parse(query) + } catch (e) { + return res.status(400).send('Malformed GraphQL query') + } + + let result + try { + result = await networkSubgraph.checkedQuery(parsedQuery, variables) + } catch (err) { + logger.error(err) + return res.status(400).send({ error: err.message }) + } + + return res.status(200).send({ + data: result.data, + errors: result.error ? result.error.graphQLErrors : null, + extensions: result.extensions, + }) + }, + ) server.listen(port, () => { logger.debug(`Listening on port ${port}`) diff --git a/packages/indexer-agent/src/types.ts b/packages/indexer-agent/src/types.ts index 62d39d35a..be21b3327 100644 --- a/packages/indexer-agent/src/types.ts +++ b/packages/indexer-agent/src/types.ts @@ -3,14 +3,14 @@ import { Network, NetworkSubgraph, ReceiptCollector, + GraphNode, } from '@graphprotocol/indexer-common' -import { Indexer } from './indexer' import { NetworkMonitor } from '@graphprotocol/indexer-common' export interface AgentConfig { logger: Logger metrics: Metrics - indexer: Indexer + indexer: GraphNode network: Network networkMonitor: NetworkMonitor networkSubgraph: NetworkSubgraph diff --git a/packages/indexer-agent/src/utils/balance-monitor.ts b/packages/indexer-agent/src/utils/balance-monitor.ts deleted file mode 100644 index 6cef0f41a..000000000 --- a/packages/indexer-agent/src/utils/balance-monitor.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Wallet, utils } from 'ethers' - -import { Logger, Metrics, timer } from '@graphprotocol/common-ts' -import { indexerError, IndexerErrorCode } from '@graphprotocol/indexer-common' - -const registerMetrics = (metrics: Metrics) => ({ - operatorEthBalance: new metrics.client.Gauge({ - name: 'indexer_agent_operator_eth_balance', - help: 'Amount of ETH in the operator wallet; a low amount could cause transactions to fail', - registers: [metrics.registry], - }), -}) - -export async function monitorEthBalance( - logger: Logger, - wallet: Wallet, - metrics: Metrics, -): Promise { - logger = logger.child({ component: 'ETHBalanceMonitor' }) - - logger.info('Monitor operator ETH balance (refreshes every 120s)') - - const balanceMetrics = registerMetrics(metrics) - - timer(120_000).pipe(async () => { - try { - const balance = await wallet.getBalance() - const eth = parseFloat(utils.formatEther(balance)) - balanceMetrics.operatorEthBalance.set(eth) - logger.info('Current operator ETH balance', { - balance: eth, - }) - } catch (error) { - logger.warn(`Failed to check latest ETH balance`, { - err: indexerError(IndexerErrorCode.IE059), - }) - } - }) -} diff --git a/packages/indexer-agent/src/utils/index.ts b/packages/indexer-agent/src/utils/index.ts deleted file mode 100644 index ad983d80f..000000000 --- a/packages/indexer-agent/src/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './balance-monitor' diff --git a/packages/indexer-cli/CHANGELOG.md b/packages/indexer-cli/CHANGELOG.md index a93f224e1..f5256fb06 100644 --- a/packages/indexer-cli/CHANGELOG.md +++ b/packages/indexer-cli/CHANGELOG.md @@ -6,6 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.20.23] - 2023-09-29 +### Changed +- Require a `--network` option to approve actions + +## [0.20.21] - 2023-08-24 +### Changed +- Upgraded `common-ts` dependency to v2.0.3 + +## [0.20.18] - 2023-08-11 +### Added +- Most CLI commands now require the protocol network to be identified using the option `--network`. + ## [0.20.12] - 2023-02-19 ### Added - Add `--safety` option to add extra POI correctness safety checks @@ -197,7 +209,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Update @graphprotocol/common-ts to 0.2.2 -[Unreleased]: https://github.com/graphprotocol/indexer/compare/v0.20.12...HEAD +[Unreleased]: https://github.com/graphprotocol/indexer/compare/v0.20.23-rc.0...HEAD +[0.20.23]: https://github.com/graphprotocol/indexer/compare/v0.20.21...v0.20.23 +[0.20.21]: https://github.com/graphprotocol/indexer/compare/v0.20.18...v0.20.21 +[0.20.18]: https://github.com/graphprotocol/indexer/compare/v0.20.12...v0.20.18 [0.20.12]: https://github.com/graphprotocol/indexer/compare/v0.20.8...v0.20.12 [0.20.8]: https://github.com/graphprotocol/indexer/compare/v0.20.6...v0.20.8 [0.20.6]: https://github.com/graphprotocol/indexer/compare/v0.20.3...v0.20.6 diff --git a/packages/indexer-cli/README.md b/packages/indexer-cli/README.md index 98fcfd1f9..7edce8f1e 100644 --- a/packages/indexer-cli/README.md +++ b/packages/indexer-cli/README.md @@ -19,35 +19,38 @@ $ graph indexer --help Manage indexer configuration - indexer status Check the status of an indexer - indexer rules Configure indexing rules - indexer rules stop (never) Never index a deployment (and stop indexing it if necessary) - indexer rules start (always) Always index a deployment (and start indexing it if necessary) - indexer rules prepare (offchain) Offchain index a deployment (good practice to prepare indexing) - indexer rules set Set one or more indexing rules - indexer rules maybe Index a deployment based on rules - indexer rules get Get one or more indexing rules - indexer rules delete Remove one or many indexing rules - indexer rules clear (reset) Clear one or more indexing rules - indexer disputes POI monitoring - indexer disputes get Cross-check POIs submitted in the network - indexer cost Manage costing for subgraphs - indexer cost set variables Update cost model variables - indexer cost set model Update a cost model - indexer cost get Get cost models and/or variables for one or all subgraphs - indexer connect Connect to indexer management API - indexer allocations reallocate Reallocate to subgraph deployment - indexer allocations get List one or more allocations - indexer allocations create Create an allocation - indexer allocations close Close an allocation - indexer allocations Manage indexer allocations - indexer actions queue Queue an action item - indexer actions get List one or more actions - indexer actions execute Execute approved items in the action queue - indexer actions cancel Cancel an item in the queue - indexer actions approve Approve an action item - indexer actions Manage indexer actions - indexer Manage indexer configuration + indexer Manage indexer configuration + indexer status Check the status of an indexer + indexer rules Configure indexing rules + indexer rules clear (reset) Clear one or more indexing rules + indexer rules delete Remove one or many indexing rules + indexer rules get Get one or more indexing rules + indexer rules maybe Index a deployment based on rules + indexer rules prepare (offchain) Offchain index a deployment (and start indexing it if necessary) + indexer rules set Set one or more indexing rules + indexer rules start (always) Always index a deployment (and start indexing it if necessary) + indexer rules stop (never) Never index a deployment (and stop indexing it if necessary) + indexer disputes Configure allocation POI monitoring + indexer disputes get Cross-check POIs submitted in the network + indexer cost Manage costing for subgraphs + indexer cost set model Update a cost model + indexer cost set variables Update cost model variables + indexer cost delete Remove one or many cost models + indexer cost get Get cost models and/or variables for one or all subgraphs + indexer connect Connect to indexer management API + indexer allocations Manage indexer allocations + indexer allocations close Close an allocation + indexer allocations create Create an allocation + indexer allocations get List one or more allocations + indexer allocations reallocate Reallocate to subgraph deployment + indexer actions Manage indexer actions + indexer actions approve Approve an action item + indexer actions cancel Cancel an item in the queue + indexer actions delete Delete one or many actions in the queue + indexer actions execute Execute approved items in the action queue + indexer actions get List one or more actions + indexer actions queue Queue an action item + indexer actions update Update one or more actions ``` Connecting to an indexer management API: diff --git a/packages/indexer-cli/jest.config.js b/packages/indexer-cli/jest.config.js index c1d71ddff..a21637a25 100644 --- a/packages/indexer-cli/jest.config.js +++ b/packages/indexer-cli/jest.config.js @@ -1,10 +1,11 @@ -const bail = (s) => { +const bail = s => { throw new Error(s) } module.exports = { collectCoverage: false, preset: 'ts-jest', + forceExit: true, testEnvironment: 'node', // The glob patterns Jest uses to detect test files testMatch: ['**/?(*.)+(spec|test).ts?(x)'], @@ -15,15 +16,15 @@ module.exports = { host: process.env.POSTGRES_TEST_HOST || bail('POSTGRES_TEST_HOST is not defined'), port: parseInt(process.env.POSTGRES_TEST_PORT || '5432'), username: - process.env.POSTGRES_TEST_USERNAME || - bail('POSTGRES_TEST_USERNAME is not defined'), + process.env.POSTGRES_TEST_USERNAME || + bail('POSTGRES_TEST_USERNAME is not defined'), password: - process.env.POSTGRES_TEST_PASSWORD || - bail('POSTGRES_TEST_PASSWORD is not defined'), + process.env.POSTGRES_TEST_PASSWORD || + bail('POSTGRES_TEST_PASSWORD is not defined'), database: - process.env.POSTGRES_TEST_DATABASE || - bail('POSTGRES_TEST_DATABASE is not defined'), + process.env.POSTGRES_TEST_DATABASE || + bail('POSTGRES_TEST_DATABASE is not defined'), }, - __LOG_LEVEL__: process.env.LOG_LEVEL || 'info' + __LOG_LEVEL__: process.env.LOG_LEVEL || 'info', }, } diff --git a/packages/indexer-cli/package.json b/packages/indexer-cli/package.json index ebe8173d8..9dd822274 100644 --- a/packages/indexer-cli/package.json +++ b/packages/indexer-cli/package.json @@ -1,6 +1,6 @@ { "name": "@graphprotocol/indexer-cli", - "version": "0.20.14", + "version": "0.20.23", "description": "Indexer CLI for The Graph Network", "main": "./dist/cli.js", "files": [ @@ -22,12 +22,12 @@ "clean": "rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo", "test": "jest --colors --verbose --runInBand --detectOpenHandles", "test:ci": "LOG_LEVEL=error jest --verbose --runInBand --ci", - "test:debug": "LOG_LEVEL=debug jest --runInBand --detectOpenHandles --verbose --forceExit", + "test:debug": "LOG_LEVEL=debug jest --runInBand --detectOpenHandles --verbose", "test:watch": "jest --watch --detectOpenHandles --verbose" }, "dependencies": { - "@graphprotocol/common-ts": "2.0.1", - "@graphprotocol/indexer-common": "^0.20.14", + "@graphprotocol/common-ts": "2.0.7", + "@graphprotocol/indexer-common": "^0.20.23", "@iarna/toml": "2.2.5", "@thi.ng/iterators": "5.1.74", "@urql/core": "2.4.4", @@ -42,17 +42,19 @@ }, "devDependencies": { "@types/isomorphic-fetch": "0.0.36", - "@typescript-eslint/eslint-plugin": "5.19.0", - "@typescript-eslint/parser": "5.19.0", - "eslint": "8.13.0", + "@types/lodash.clonedeep": "^4.5.7", + "@typescript-eslint/eslint-plugin": "6.7.0", + "@typescript-eslint/parser": "6.7.0", + "eslint": "8.49.0", "eslint-config-prettier": "8.5.0", - "prettier": "2.6.2", - "ts-jest": "27.1.4", - "typescript": "4.6.3" + "lodash.clonedeep": "^4.5.0", + "prettier": "3.0.3", + "ts-jest": "29.1.1", + "typescript": "5.2.2" }, "resolutions": { "ethers": "5.7.0", - "sequelize": "6.19.0" + "sequelize": "6.33.0" }, "gitHead": "972ab96774007b2aee15b1da169d2ff4be9f9d27" } diff --git a/packages/indexer-cli/src/__tests__/indexer/actions.test.ts b/packages/indexer-cli/src/__tests__/indexer/actions.test.ts index 516b0da1c..e18627697 100644 --- a/packages/indexer-cli/src/__tests__/indexer/actions.test.ts +++ b/packages/indexer-cli/src/__tests__/indexer/actions.test.ts @@ -86,6 +86,7 @@ async function createTestAction() { deploymentID: 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', source: 'test', reason: 'test', + protocolNetwork: 'eip155:5', }) await Action.create({ type: ActionType.UNALLOCATE, @@ -93,6 +94,7 @@ async function createTestAction() { deploymentID: 'QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66', source: 'test', reason: 'test', + protocolNetwork: 'eip155:5', }) } diff --git a/packages/indexer-cli/src/__tests__/indexer/cost.test.ts b/packages/indexer-cli/src/__tests__/indexer/cost.test.ts index 215a1fffb..08fdbee5f 100644 --- a/packages/indexer-cli/src/__tests__/indexer/cost.test.ts +++ b/packages/indexer-cli/src/__tests__/indexer/cost.test.ts @@ -1,11 +1,14 @@ -import { cliTest, setup, teardown } from '../util' +import { cliTest, setup, seed, teardown } from '../util' import path from 'path' const baseDir = path.join(__dirname, '..') describe('Indexer cost tests', () => { describe('With indexer management server', () => { - beforeEach(setup) + beforeEach(async () => { + await setup() + await seed() + }) afterEach(teardown) describe('Cost help', () => { cliTest('Indexer cost', ['indexer', 'cost'], 'references/indexer-cost', { diff --git a/packages/indexer-cli/src/__tests__/indexer/rules.test.ts b/packages/indexer-cli/src/__tests__/indexer/rules.test.ts index 139ef14a9..94989dcf5 100644 --- a/packages/indexer-cli/src/__tests__/indexer/rules.test.ts +++ b/packages/indexer-cli/src/__tests__/indexer/rules.test.ts @@ -1,18 +1,27 @@ -import { cliTest, setup, teardown } from '../util' +import { cliTest, setup, seed, teardown, deleteFromAllTables } from '../util' import path from 'path' const baseDir = path.join(__dirname, '..') describe('Indexer rules tests', () => { describe('With indexer management server', () => { - beforeEach(setup) - afterEach(teardown) + beforeAll(setup) + beforeEach(async () => { + await deleteFromAllTables() + await seed() + }) + afterAll(teardown) describe('Rules help', () => { - cliTest('Indexer rules', ['indexer', 'rules'], 'references/indexer-rules', { - expectedExitCode: 255, - cwd: baseDir, - timeout: 10000, - }) + cliTest( + 'Indexer rules', + ['indexer', 'rules', '--network', 'goerli'], + 'references/indexer-rules', + { + expectedExitCode: 255, + cwd: baseDir, + timeout: 10000, + }, + ) cliTest( 'Indexer rules help', ['indexer', 'rules', '--help'], @@ -28,7 +37,14 @@ describe('Indexer rules tests', () => { describe('Rules start...', () => { cliTest( 'Indexer rules start - success', - ['indexer', 'rules', 'start', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'start', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-rule-deployment-always', { expectedExitCode: 0, @@ -37,9 +53,19 @@ describe('Indexer rules tests', () => { }, ) cliTest( - 'Indexer rules start - no args', + 'Indexer rules start - no network', ['indexer', 'rules', 'start'], - 'references/indexer-rules-command-no-args', + 'references/indexer-rules-no-network', + { + expectedExitCode: 1, + cwd: baseDir, + timeout: 10000, + }, + ) + cliTest( + 'Indexer rules start - no identifier', + ['indexer', 'rules', 'start', '--network', 'goerli'], + 'references/indexer-rules-no-identifier', { expectedExitCode: 1, cwd: baseDir, @@ -48,7 +74,7 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules start - invalid deployment ID ', - ['indexer', 'rules', 'start', 'Qmemememememe'], + ['indexer', 'rules', 'start', '--network', 'goerli', 'Qmemememememe'], 'references/indexer-rules-invalid-identifier-arg', { expectedExitCode: 1, @@ -61,7 +87,14 @@ describe('Indexer rules tests', () => { describe('Rules prepare...', () => { cliTest( 'Indexer rules prepare - success', - ['indexer', 'rules', 'prepare', 'QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK'], + [ + 'indexer', + 'rules', + 'prepare', + '--network', + 'goerli', + 'QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK', + ], 'references/indexer-rule-deployment-offchain', { expectedExitCode: 0, @@ -75,6 +108,8 @@ describe('Indexer rules tests', () => { 'indexer', 'rules', 'offchain', + '--network', + 'goerli', 'QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK', ], 'references/indexer-rule-deployment-offchain', @@ -85,9 +120,19 @@ describe('Indexer rules tests', () => { }, ) cliTest( - 'Indexer rules prepare - no args', + 'Indexer rules prepare - no network', ['indexer', 'rules', 'prepare'], - 'references/indexer-rules-command-no-args', + 'references/indexer-rules-no-network', + { + expectedExitCode: 1, + cwd: baseDir, + timeout: 10000, + }, + ) + cliTest( + 'Indexer rules prepare - no identifier', + ['indexer', 'rules', 'prepare', '--network', 'goerli'], + 'references/indexer-rules-no-identifier', { expectedExitCode: 1, cwd: baseDir, @@ -96,7 +141,7 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules prepare - invalid deployment ID ', - ['indexer', 'rules', 'prepare', 'Qmemememememe'], + ['indexer', 'rules', 'prepare', '--network', 'goerli', 'Qmemememememe'], 'references/indexer-rules-invalid-identifier-arg', { expectedExitCode: 1, @@ -109,7 +154,14 @@ describe('Indexer rules tests', () => { describe('Rules stop...', () => { cliTest( 'Indexer rules stop - success', - ['indexer', 'rules', 'stop', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'stop', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-rule-deployment-never', { expectedExitCode: 0, @@ -118,9 +170,19 @@ describe('Indexer rules tests', () => { }, ) cliTest( - 'Indexer rules stop - no args', + 'Indexer rules stop - no network', ['indexer', 'rules', 'stop'], - 'references/indexer-rules-command-no-args', + 'references/indexer-rules-no-network', + { + expectedExitCode: 1, + cwd: baseDir, + timeout: 10000, + }, + ) + cliTest( + 'Indexer rules stop - no identifier', + ['indexer', 'rules', 'stop', '--network', 'goerli'], + 'references/indexer-rules-no-identifier', { expectedExitCode: 1, cwd: baseDir, @@ -129,7 +191,7 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules stop - invalid deployment ID', - ['indexer', 'rules', 'stop', 'Qmemememememe'], + ['indexer', 'rules', 'stop', '--network', 'goerli', 'Qmemememememe'], 'references/indexer-rules-invalid-identifier-arg', { expectedExitCode: 1, @@ -142,7 +204,14 @@ describe('Indexer rules tests', () => { describe('Rules maybe...', () => { cliTest( 'Indexer rules maybe - success', - ['indexer', 'rules', 'maybe', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'maybe', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-rule-deployment-rules', { expectedExitCode: 0, @@ -151,9 +220,19 @@ describe('Indexer rules tests', () => { }, ) cliTest( - 'Indexer rules maybe - no args', + 'Indexer rules maybe - no network', ['indexer', 'rules', 'maybe'], - 'references/indexer-rules-command-no-args', + 'references/indexer-rules-no-network', + { + expectedExitCode: 1, + cwd: baseDir, + timeout: 10000, + }, + ) + cliTest( + 'Indexer rules maybe - no identifier', + ['indexer', 'rules', 'maybe', '--network', 'goerli'], + 'references/indexer-rules-no-identifier', { expectedExitCode: 1, cwd: baseDir, @@ -162,7 +241,7 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules maybe - invalid deployment ID ', - ['indexer', 'rules', 'maybe', 'Qmemememememe'], + ['indexer', 'rules', 'maybe', '--network', 'goerli', 'Qmemememememe'], 'references/indexer-rules-invalid-identifier-arg', { expectedExitCode: 1, @@ -175,7 +254,14 @@ describe('Indexer rules tests', () => { describe('Rules clear...', () => { cliTest( 'Indexer rules clear - success', - ['indexer', 'rules', 'clear', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'clear', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-rule-deployment-rules', { expectedExitCode: 0, @@ -184,9 +270,19 @@ describe('Indexer rules tests', () => { }, ) cliTest( - 'Indexer rules clear - no args', + 'Indexer rules clear - no network', ['indexer', 'rules', 'clear'], - 'references/indexer-rules-command-no-args', + 'references/indexer-rules-no-network', + { + expectedExitCode: 1, + cwd: baseDir, + timeout: 10000, + }, + ) + cliTest( + 'Indexer rules clear - no identifier', + ['indexer', 'rules', 'clear', '--network', 'goerli'], + 'references/indexer-rules-no-identifier', { expectedExitCode: 1, cwd: baseDir, @@ -195,7 +291,7 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules clear - invalid deployment ID ', - ['indexer', 'rules', 'clear', 'Qmemememememe'], + ['indexer', 'rules', 'clear', '--network', 'goerli', 'Qmemememememe'], 'references/indexer-rules-invalid-identifier-arg', { expectedExitCode: 1, @@ -208,7 +304,14 @@ describe('Indexer rules tests', () => { describe('Rules delete...', () => { cliTest( 'Indexer rules delete - success', - ['indexer', 'rules', 'delete', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'delete', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-rule-deployment-deleted-success', { expectedExitCode: 0, @@ -218,7 +321,14 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules delete - success', - ['indexer', 'rules', 'delete', 'QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK'], + [ + 'indexer', + 'rules', + 'delete', + '--network', + 'goerli', + 'QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK', + ], 'references/indexer-rule-deployment-deleted-offchain-success', { expectedExitCode: 0, @@ -227,9 +337,19 @@ describe('Indexer rules tests', () => { }, ) cliTest( - 'Indexer rules delete - no args', + 'Indexer rules delete - no network', ['indexer', 'rules', 'delete'], - 'references/indexer-rules-command-no-args', + 'references/indexer-rules-no-network', + { + expectedExitCode: 1, + cwd: baseDir, + timeout: 10000, + }, + ) + cliTest( + 'Indexer rules delete - no identifier', + ['indexer', 'rules', 'delete', '--network', 'goerli'], + 'references/indexer-rules-no-identifier', { expectedExitCode: 1, cwd: baseDir, @@ -238,7 +358,7 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules delete - invalid deployment ID ', - ['indexer', 'rules', 'delete', 'Qmemememememe'], + ['indexer', 'rules', 'delete', '--network', 'goerli', 'Qmemememememe'], 'references/indexer-rules-invalid-identifier-arg', { expectedExitCode: 1, @@ -255,6 +375,8 @@ describe('Indexer rules tests', () => { 'indexer', 'rules', 'set', + '--network', + 'goerli', '0x0000000000000000000000000000000000000000-0', 'allocationAmount', '1000', @@ -272,6 +394,8 @@ describe('Indexer rules tests', () => { 'indexer', 'rules', 'set', + '--network', + 'goerli', '0x0000000000000000000000000000000000000000-1', 'allocationAmount', '1000', @@ -289,7 +413,14 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules set deployment id - success', - ['indexer', 'rules', 'set', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'set', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-rule-deployment-rules', { expectedExitCode: 0, @@ -303,6 +434,8 @@ describe('Indexer rules tests', () => { 'indexer', 'rules', 'set', + '--network', + 'goerli', 'QmVEV7RA2U6BJT9Ssjxcfyrk4YQUnVqSRNX4TvYagjzh9h', 'requireSupported', 'false', @@ -320,6 +453,8 @@ describe('Indexer rules tests', () => { 'indexer', 'rules', 'set', + '--network', + 'goerli', 'QmVEV7RA2U6BJT9Ssjxcfyrk4YQUnVqSRNX4TvYagjzh9h', 'safety', 'false', @@ -337,6 +472,8 @@ describe('Indexer rules tests', () => { 'indexer', 'rules', 'set', + '--network', + 'goerli', 'QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK', 'decisionBasis', 'offchain', @@ -358,6 +495,8 @@ describe('Indexer rules tests', () => { 'indexer', 'rules', 'set', + '--network', + 'goerli', 'global', 'minSignal', '500', @@ -374,7 +513,7 @@ describe('Indexer rules tests', () => { cliTest( 'Indexer rules set - no args', ['indexer', 'rules', 'set'], - 'references/indexer-rules-command-no-args', + 'references/indexer-rules-no-network', { expectedExitCode: 1, cwd: baseDir, @@ -383,7 +522,7 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules set - invalid deployment ID ', - ['indexer', 'rules', 'set', 'Qmemememememe'], + ['indexer', 'rules', 'set', '--network', 'goerli', 'Qmemememememe'], 'references/indexer-rules-invalid-identifier-arg', { expectedExitCode: 1, @@ -397,6 +536,8 @@ describe('Indexer rules tests', () => { 'indexer', 'rules', 'set', + '--network', + 'goerli', '0x0000000000000000000000000000000000000000-0', 'allocationAmoewt', '1000', @@ -423,7 +564,14 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules get deployment - success', - ['indexer', 'rules', 'get', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'get', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-rule-deployment-rules', { expectedExitCode: 0, @@ -433,7 +581,14 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules get deployment - success - offchain', - ['indexer', 'rules', 'get', 'QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK'], + [ + 'indexer', + 'rules', + 'get', + '--network', + 'goerli', + 'QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK', + ], 'references/indexer-rule-deployment-offchain', { expectedExitCode: 0, @@ -443,7 +598,14 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules get subgraph - success', - ['indexer', 'rules', 'get', '0x0000000000000000000000000000000000000000-0'], + [ + 'indexer', + 'rules', + 'get', + '--network', + 'goerli', + '0x0000000000000000000000000000000000000000-0', + ], 'references/indexer-rule-subgraph-rules', { expectedExitCode: 0, @@ -453,7 +615,14 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules get subgraph - success - options', - ['indexer', 'rules', 'get', '0x0000000000000000000000000000000000000000-2'], + [ + 'indexer', + 'rules', + 'get', + '--network', + 'goerli', + '0x0000000000000000000000000000000000000000-2', + ], 'references/indexer-rule-subgraph-options', { expectedExitCode: 0, @@ -467,6 +636,8 @@ describe('Indexer rules tests', () => { 'indexer', 'rules', 'get', + '--network', + 'goerli', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', '--output', 'yaml', @@ -480,7 +651,7 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules get global - success', - ['indexer', 'rules', 'get', 'global'], + ['indexer', 'rules', 'get', '--network', 'goerli', 'global'], 'references/indexer-rule-global-rules', { expectedExitCode: 0, @@ -493,14 +664,14 @@ describe('Indexer rules tests', () => { ['indexer', 'rules', 'get'], 'references/indexer-rules-command-no-args', { - expectedExitCode: 1, + expectedExitCode: 0, cwd: baseDir, timeout: 10000, }, ) cliTest( 'Indexer rules get - invalid deployment ID ', - ['indexer', 'rules', 'get', 'Qmemememememe'], + ['indexer', 'rules', 'get', '--network', 'goerli', 'Qmemememememe'], 'references/indexer-rules-invalid-identifier-arg', { expectedExitCode: 1, @@ -514,7 +685,14 @@ describe('Indexer rules tests', () => { describe('Without indexer management server', () => { cliTest( 'Indexer rules start - not connected', - ['indexer', 'rules', 'start', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'start', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-not-connected', { expectedExitCode: 1, @@ -524,7 +702,14 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules stop - not connected', - ['indexer', 'rules', 'stop', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'stop', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-not-connected', { expectedExitCode: 1, @@ -534,7 +719,14 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules maybe - not connected', - ['indexer', 'rules', 'maybe', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'maybe', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-not-connected', { expectedExitCode: 1, @@ -542,19 +734,16 @@ describe('Indexer rules tests', () => { timeout: 10000, }, ) - cliTest( - 'Indexer rules get - no args', - ['indexer', 'rules', 'get'], - 'references/indexer-rules-command-no-args', - { - expectedExitCode: 1, - cwd: baseDir, - timeout: 10000, - }, - ) cliTest( 'Indexer rules get - not connected', - ['indexer', 'rules', 'get', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'get', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-not-connected', { expectedExitCode: 1, @@ -564,7 +753,14 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules delete - not connected', - ['indexer', 'rules', 'delete', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'delete', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-not-connected', { expectedExitCode: 1, @@ -574,7 +770,14 @@ describe('Indexer rules tests', () => { ) cliTest( 'Indexer rules clear - not connected', - ['indexer', 'rules', 'clear', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], + [ + 'indexer', + 'rules', + 'clear', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], 'references/indexer-not-connected', { expectedExitCode: 1, diff --git a/packages/indexer-cli/src/__tests__/references/indexer-actions-get-first.stdout b/packages/indexer-cli/src/__tests__/references/indexer-actions-get-first.stdout index 773bca940..508c1a86b 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-actions-get-first.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-actions-get-first.stdout @@ -1,5 +1,5 @@ -┌────┬────────────┬────────────────────────────────────────────────┬──────────────┬────────┬──────┬───────┬──────────┬────────┬────────┬───────────────┬─────────────┬────────┐ -│ id │ type │ deploymentID │ allocationID │ amount │ poi │ force │ priority │ status │ source │ failureReason │ transaction │ reason │ -├────┼────────────┼────────────────────────────────────────────────┼──────────────┼────────┼──────┼───────┼──────────┼────────┼────────┼───────────────┼─────────────┼────────┤ -│ 2 │ unallocate │ QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 │ null │ null │ null │ null │ 0 │ failed │ test │ null │ null │ test │ -└────┴────────────┴────────────────────────────────────────────────┴──────────────┴────────┴──────┴───────┴──────────┴────────┴────────┴───────────────┴─────────────┴────────┘ +┌────┬─────────────────┬────────────┬────────────────────────────────────────────────┬──────────────┬────────┬──────┬───────┬──────────┬────────┬────────┬───────────────┬─────────────┬────────┐ +│ id │ protocolNetwork │ type │ deploymentID │ allocationID │ amount │ poi │ force │ priority │ status │ source │ failureReason │ transaction │ reason │ +├────┼─────────────────┼────────────┼────────────────────────────────────────────────┼──────────────┼────────┼──────┼───────┼──────────┼────────┼────────┼───────────────┼─────────────┼────────┤ +│ 2 │ goerli │ unallocate │ QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 │ null │ null │ null │ null │ 0 │ failed │ test │ null │ null │ test │ +└────┴─────────────────┴────────────┴────────────────────────────────────────────────┴──────────────┴────────┴──────┴───────┴──────────┴────────┴────────┴───────────────┴─────────────┴────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-actions-get.stdout b/packages/indexer-cli/src/__tests__/references/indexer-actions-get.stdout index 145603b61..6b00ddbbc 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-actions-get.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-actions-get.stdout @@ -1,7 +1,7 @@ -┌────┬────────────┬────────────────────────────────────────────────┬──────────────┬────────┬──────┬───────┬──────────┬─────────┬────────┬───────────────┬─────────────┬────────┐ -│ id │ type │ deploymentID │ allocationID │ amount │ poi │ force │ priority │ status │ source │ failureReason │ transaction │ reason │ -├────┼────────────┼────────────────────────────────────────────────┼──────────────┼────────┼──────┼───────┼──────────┼─────────┼────────┼───────────────┼─────────────┼────────┤ -│ 2 │ unallocate │ QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 │ null │ null │ null │ null │ 0 │ failed │ test │ null │ null │ test │ -├────┼────────────┼────────────────────────────────────────────────┼──────────────┼────────┼──────┼───────┼──────────┼─────────┼────────┼───────────────┼─────────────┼────────┤ -│ 1 │ allocate │ QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr │ null │ null │ null │ null │ 0 │ success │ test │ null │ null │ test │ -└────┴────────────┴────────────────────────────────────────────────┴──────────────┴────────┴──────┴───────┴──────────┴─────────┴────────┴───────────────┴─────────────┴────────┘ +┌────┬─────────────────┬────────────┬────────────────────────────────────────────────┬──────────────┬────────┬──────┬───────┬──────────┬─────────┬────────┬───────────────┬─────────────┬────────┐ +│ id │ protocolNetwork │ type │ deploymentID │ allocationID │ amount │ poi │ force │ priority │ status │ source │ failureReason │ transaction │ reason │ +├────┼─────────────────┼────────────┼────────────────────────────────────────────────┼──────────────┼────────┼──────┼───────┼──────────┼─────────┼────────┼───────────────┼─────────────┼────────┤ +│ 2 │ goerli │ unallocate │ QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 │ null │ null │ null │ null │ 0 │ failed │ test │ null │ null │ test │ +├────┼─────────────────┼────────────┼────────────────────────────────────────────────┼──────────────┼────────┼──────┼───────┼──────────┼─────────┼────────┼───────────────┼─────────────┼────────┤ +│ 1 │ goerli │ allocate │ QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr │ null │ null │ null │ null │ 0 │ success │ test │ null │ null │ test │ +└────┴─────────────────┴────────────┴────────────────────────────────────────────────┴──────────────┴────────┴──────┴───────┴──────────┴─────────┴────────┴───────────────┴─────────────┴────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-always.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-always.stdout index 37e299f4e..f1adb22f6 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-always.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-always.stdout @@ -1,5 +1,5 @@ -┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┐ -│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ -├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ -│ QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ always │ true │ true │ -└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┘ +┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┬─────────────────┐ +│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ protocolNetwork │ +├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┼─────────────────┤ +│ QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ always │ true │ true │ goerli │ +└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┴─────────────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-deleted-offchain-success.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-deleted-offchain-success.stdout index be659136c..01b15a29d 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-deleted-offchain-success.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-deleted-offchain-success.stdout @@ -1 +1 @@ -Deleted indexing rules for "QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK" (deployment) +Deleted indexing rules for "QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK" (deployment) on network: 'goerli' diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-deleted-success.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-deleted-success.stdout index 546cb83c6..062a6ba5c 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-deleted-success.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-deleted-success.stdout @@ -1 +1 @@ -Deleted indexing rules for "QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr" (deployment) +Deleted indexing rules for "QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr" (deployment) on network: 'goerli' diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-lifetime.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-lifetime.stdout index 5f5115f1c..0eb184ed9 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-lifetime.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-lifetime.stdout @@ -1,5 +1,5 @@ -┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┐ -│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ -├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ -│ QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK │ deployment │ null │ 21 │ false │ null │ null │ null │ null │ null │ null │ null │ offchain │ true │ true │ -└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┘ +┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┬─────────────────┐ +│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ protocolNetwork │ +├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┼─────────────────┤ +│ QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK │ deployment │ null │ 21 │ false │ null │ null │ null │ null │ null │ null │ null │ offchain │ true │ true │ goerli │ +└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┴─────────────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-never.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-never.stdout index 4fdd160b5..813c5736a 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-never.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-never.stdout @@ -1,5 +1,5 @@ -┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┐ -│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ -├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ -│ QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ never │ true │ true │ -└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┘ +┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┬─────────────────┐ +│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ protocolNetwork │ +├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┼─────────────────┤ +│ QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ never │ true │ true │ goerli │ +└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┴─────────────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-offchain.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-offchain.stdout index 5806df5c7..d44a372dc 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-offchain.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-offchain.stdout @@ -1,5 +1,5 @@ -┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┐ -│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ -├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ -│ QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ offchain │ true │ true │ -└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┘ +┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┬─────────────────┐ +│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ protocolNetwork │ +├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┼─────────────────┤ +│ QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ offchain │ true │ true │ goerli │ +└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┴─────────────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-rules.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-rules.stdout index 96f4174a9..3ea675fbb 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-rules.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-rules.stdout @@ -1,5 +1,5 @@ -┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┐ -│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ -├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ -│ QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ rules │ true │ true │ -└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┘ +┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┬─────────────────┐ +│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ protocolNetwork │ +├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┼─────────────────┤ +│ QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ rules │ true │ true │ goerli │ +└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┴─────────────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-safety.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-safety.stdout index d502b5c44..390ad6605 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-safety.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-safety.stdout @@ -1,5 +1,5 @@ -┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┐ -│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ -├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ -│ QmVEV7RA2U6BJT9Ssjxcfyrk4YQUnVqSRNX4TvYagjzh9h │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ rules │ true │ false │ -└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┘ +┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┬─────────────────┐ +│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ protocolNetwork │ +├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┼─────────────────┤ +│ QmVEV7RA2U6BJT9Ssjxcfyrk4YQUnVqSRNX4TvYagjzh9h │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ rules │ true │ false │ goerli │ +└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┴─────────────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-supported.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-supported.stdout index cd5f1a9a3..8f8dc475a 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-supported.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-supported.stdout @@ -1,5 +1,5 @@ -┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┐ -│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ -├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ -│ QmVEV7RA2U6BJT9Ssjxcfyrk4YQUnVqSRNX4TvYagjzh9h │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ rules │ false │ true │ -└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┘ +┌────────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┬─────────────────┐ +│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ protocolNetwork │ +├────────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┼─────────────────┤ +│ QmVEV7RA2U6BJT9Ssjxcfyrk4YQUnVqSRNX4TvYagjzh9h │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ rules │ false │ true │ goerli │ +└────────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┴─────────────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-yaml.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-yaml.stdout index 994546f99..bb4efe2aa 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-yaml.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-deployment-yaml.stdout @@ -13,3 +13,4 @@ custom: null decisionBasis: rules requireSupported: true safety: true +protocolNetwork: goerli diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-global-rules.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-global-rules.stdout index 3a1021afb..bfc937631 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-global-rules.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-global-rules.stdout @@ -1,5 +1,5 @@ -┌────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┐ -│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ -├────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ -│ global │ group │ 0.01 │ null │ true │ null │ null │ 500.0 │ null │ null │ null │ null │ rules │ true │ true │ -└────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┘ +┌────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┬─────────────────┐ +│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ protocolNetwork │ +├────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┼─────────────────┤ +│ global │ group │ 0.01 │ null │ true │ null │ null │ 500.0 │ null │ null │ null │ null │ rules │ true │ true │ goerli │ +└────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┴─────────────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-subgraph-offchain.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-subgraph-offchain.stdout index c948ce8fe..fff19583a 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-subgraph-offchain.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-subgraph-offchain.stdout @@ -1,5 +1,5 @@ -┌──────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┐ -│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ -├──────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ -│ 0x0000000000000000000000000000000000000000-1 │ subgraph │ 1,000.0 │ 12 │ true │ null │ null │ null │ null │ null │ null │ null │ offchain │ true │ true │ -└──────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┘ +┌──────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┬─────────────────┐ +│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ protocolNetwork │ +├──────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┼─────────────────┤ +│ 0x0000000000000000000000000000000000000000-1 │ subgraph │ 1,000.0 │ 12 │ true │ null │ null │ null │ null │ null │ null │ null │ offchain │ true │ true │ goerli │ +└──────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┴─────────────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-subgraph-options.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-subgraph-options.stdout index c21331585..0defef731 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-subgraph-options.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-subgraph-options.stdout @@ -1,5 +1,5 @@ -┌──────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┐ -│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ -├──────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ -│ 0x0000000000000000000000000000000000000000-2 │ subgraph │ 1,000.0 │ 12 │ true │ null │ null │ null │ null │ null │ null │ null │ offchain │ true │ true │ -└──────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┘ +┌──────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┬─────────────────┐ +│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ protocolNetwork │ +├──────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┼─────────────────┤ +│ 0x0000000000000000000000000000000000000000-2 │ subgraph │ 1,000.0 │ 12 │ true │ null │ null │ null │ null │ null │ null │ null │ offchain │ true │ true │ goerli │ +└──────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┴─────────────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rule-subgraph-rules.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rule-subgraph-rules.stdout index 795f870dd..e5339693d 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rule-subgraph-rules.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rule-subgraph-rules.stdout @@ -1,5 +1,5 @@ -┌──────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┐ -│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ -├──────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ -│ 0x0000000000000000000000000000000000000000-0 │ subgraph │ 1,000.0 │ null │ true │ null │ null │ null │ null │ null │ null │ null │ rules │ true │ true │ -└──────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┘ +┌──────────────────────────────────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┬─────────────────┐ +│ identifier │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ protocolNetwork │ +├──────────────────────────────────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┼─────────────────┤ +│ 0x0000000000000000000000000000000000000000-0 │ subgraph │ 1,000.0 │ null │ true │ null │ null │ null │ null │ null │ null │ null │ rules │ true │ true │ goerli │ +└──────────────────────────────────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┴─────────────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rules-command-no-args.stdout b/packages/indexer-cli/src/__tests__/references/indexer-rules-command-no-args.stdout index b2b9731e4..75048306b 100644 --- a/packages/indexer-cli/src/__tests__/references/indexer-rules-command-no-args.stdout +++ b/packages/indexer-cli/src/__tests__/references/indexer-rules-command-no-args.stdout @@ -1 +1,11 @@ -Error: Invalid subgraph identifier "undefined". Subgraph identifier should match 1 type of [deployment ID, subgraph ID, group identifier]. +┌────────────────────────────────────────────────┬─────────────────┬────────────────┬──────────────────┬────────────────────┬─────────────┬─────────────────────┬─────────────────────────┬───────────┬───────────┬──────────┬─────────────────────┬────────┬───────────────┬──────────────────┬────────┐ +│ identifier │ protocolNetwork │ identifierType │ allocationAmount │ allocationLifetime │ autoRenewal │ parallelAllocations │ maxAllocationPercentage │ minSignal │ maxSignal │ minStake │ minAverageQueryFees │ custom │ decisionBasis │ requireSupported │ safety │ +├────────────────────────────────────────────────┼─────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ +│ global │ goerli │ group │ 0.01 │ null │ true │ null │ null │ 500.0 │ null │ null │ null │ null │ rules │ true │ true │ +├────────────────────────────────────────────────┼─────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ +│ 0x0000000000000000000000000000000000000000-0 │ goerli │ subgraph │ 1,000.0 │ null │ true │ null │ null │ null │ null │ null │ null │ null │ rules │ true │ true │ +├────────────────────────────────────────────────┼─────────────────┼────────────────┼──────────────────┼────────────────────┼─────────────┼─────────────────────┼─────────────────────────┼───────────┼───────────┼──────────┼─────────────────────┼────────┼───────────────┼──────────────────┼────────┤ +│ QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr │ goerli │ deployment │ null │ null │ true │ null │ null │ null │ null │ null │ null │ null │ rules │ true │ true │ +└────────────────────────────────────────────────┴─────────────────┴────────────────┴──────────────────┴────────────────────┴─────────────┴─────────────────────┴─────────────────────────┴───────────┴───────────┴──────────┴─────────────────────┴────────┴───────────────┴──────────────────┴────────┘ +Offchain sync list +0x0000000000000000000000000000000000000000-1,0x0000000000000000000000000000000000000000-2,QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rules-no-identifier.stderr b/packages/indexer-cli/src/__tests__/references/indexer-rules-no-identifier.stderr new file mode 100644 index 000000000..b2b9731e4 --- /dev/null +++ b/packages/indexer-cli/src/__tests__/references/indexer-rules-no-identifier.stderr @@ -0,0 +1 @@ +Error: Invalid subgraph identifier "undefined". Subgraph identifier should match 1 type of [deployment ID, subgraph ID, group identifier]. diff --git a/packages/indexer-cli/src/__tests__/references/indexer-rules-no-network.stderr b/packages/indexer-cli/src/__tests__/references/indexer-rules-no-network.stderr new file mode 100644 index 000000000..0c57fdb91 --- /dev/null +++ b/packages/indexer-cli/src/__tests__/references/indexer-rules-no-network.stderr @@ -0,0 +1 @@ +Error: The option '--network' is required diff --git a/packages/indexer-cli/src/__tests__/util.ts b/packages/indexer-cli/src/__tests__/util.ts index 535d3c5d6..ace57beac 100644 --- a/packages/indexer-cli/src/__tests__/util.ts +++ b/packages/indexer-cli/src/__tests__/util.ts @@ -1,5 +1,4 @@ import { exec, ExecOptions } from 'child_process' -import { Wallet } from 'ethers' import fs from 'fs' import http from 'http' import { Socket } from 'net' @@ -13,18 +12,27 @@ import { defineIndexerManagementModels, IndexerManagementClient, IndexerManagementModels, - IndexingStatusResolver, - NetworkSubgraph, + GraphNode, + specification, + IndexerManagementDefaults, + Network, + MultiNetworks, + QueryFeeModels, + defineQueryFeeModels, } from '@graphprotocol/indexer-common' import { - connectContracts, + createMetrics, + Metrics, connectDatabase, createLogger, Logger, - NetworkContracts, parseGRT, - toAddress, } from '@graphprotocol/common-ts' +import cloneDeep from 'lodash.clonedeep' + +const INDEXER_SAVE_CLI_TEST_OUTPUT: boolean = + !!process.env.INDEXER_SAVE_CLI_TEST_OUTPUT && + process.env.INDEXER_SAVE_CLI_TEST_OUTPUT.toLowerCase() !== 'false' declare const __DATABASE__: never declare const __LOG_LEVEL__: never @@ -32,13 +40,51 @@ declare const __LOG_LEVEL__: never let defaultMaxEventListeners: number let sequelize: Sequelize let models: IndexerManagementModels -let wallet: Wallet -let address: string -let contracts: NetworkContracts +let queryFeeModels: QueryFeeModels let logger: Logger let indexerManagementClient: IndexerManagementClient let server: http.Server let sockets: Socket[] = [] +let metrics: Metrics + +const PUBLIC_JSON_RPC_ENDPOINT = 'https://ethereum-goerli.publicnode.com' + +const testProviderUrl = + process.env.INDEXER_TEST_JRPC_PROVIDER_URL ?? PUBLIC_JSON_RPC_ENDPOINT + +export const testNetworkSpecification = specification.NetworkSpecification.parse({ + networkIdentifier: 'goerli', + gateway: { + url: 'http://localhost:8030/', + }, + networkProvider: { + url: testProviderUrl, + }, + indexerOptions: { + address: '0xf56b5d582920E4527A818FBDd801C0D80A394CB8', + mnemonic: + 'famous aspect index polar tornado zero wedding electric floor chalk tenant junk', + url: 'http://test-indexer.xyz', + }, + subgraphs: { + networkSubgraph: { + url: 'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli', + }, + epochSubgraph: { + url: 'http://test-url.xyz', + }, + }, + transactionMonitoring: { + gasIncreaseTimeout: 240000, + gasIncreaseFactor: 1.2, + baseFeePerGasMax: 100 * 10 ** 9, + maxTransactionAttempts: 0, + }, + dai: { + contractAddress: '0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448', + inject: false, + }, +}) export const setup = async () => { logger = createLogger({ @@ -49,43 +95,55 @@ export const setup = async () => { sequelize = await connectDatabase(__DATABASE__) models = defineIndexerManagementModels(sequelize) - address = '0x3C17A4c7cD8929B83e4705e04020fA2B1bca2E55' - contracts = await connectContracts(wallet, 5) - await sequelize.sync({ force: true }) + queryFeeModels = defineQueryFeeModels(sequelize) + metrics = createMetrics() + // Clearing the registry prevents duplicate metric registration in the default registry. + metrics.registry.clear() - wallet = Wallet.createRandom() + sequelize = await sequelize.sync({ force: true }) const statusEndpoint = 'http://localhost:8030/graphql' - const indexingStatusResolver = new IndexingStatusResolver({ - logger: logger, + const indexNodeIDs = ['node_1'] + const graphNode = new GraphNode( + logger, + 'http://test-admin-endpoint.xyz', + 'https://test-query-endpoint.xyz', statusEndpoint, - }) + indexNodeIDs, + ) - const networkSubgraph = await NetworkSubgraph.create({ + const network = await Network.create( logger, - endpoint: - 'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-testnet', - deployment: undefined, - }) - const indexNodeIDs = ['node_1'] + testNetworkSpecification, + queryFeeModels, + graphNode, + metrics, + ) + + const fakeMainnetNetwork = cloneDeep(network) as Network + fakeMainnetNetwork.specification.networkIdentifier = 'eip155:1' + + const multiNetworks = new MultiNetworks( + [network, fakeMainnetNetwork], + (n: Network) => n.specification.networkIdentifier, + ) + + const defaults: IndexerManagementDefaults = { + globalIndexingRule: { + allocationAmount: parseGRT('100'), + parallelAllocations: 1, + requireSupported: true, + safety: true, + }, + } + indexerManagementClient = await createIndexerManagementClient({ models, - address: toAddress(address), - contracts: contracts, - indexingStatusResolver, + graphNode, indexNodeIDs, - deploymentManagementEndpoint: statusEndpoint, - networkSubgraph, logger, - defaults: { - globalIndexingRule: { - allocationAmount: parseGRT('1000'), - parallelAllocations: 1, - }, - }, - features: { - injectDai: false, - }, + defaults, + multiNetworks, }) server = await createIndexerManagementServer({ @@ -101,28 +159,66 @@ export const setup = async () => { defaultMaxEventListeners = process.getMaxListeners() process.setMaxListeners(100) - process.on('SIGTERM', await shutdownIndexerManagementServer) - process.on('SIGINT', await shutdownIndexerManagementServer) + process.on('SIGTERM', shutdownIndexerManagementServer) + process.on('SIGINT', shutdownIndexerManagementServer) +} - // Set global, deployment, and subgraph based test rules and cost model +// Set global, deployment, and subgraph based test rules and cost model +export const seed = async () => { const commands: string[][] = [ ['indexer', 'connect', 'http://localhost:18000'], - ['indexer', 'rules', 'set', 'global', 'minSignal', '500', 'allocationAmount', '.01'], - ['indexer', 'rules', 'set', 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr'], - ['indexer', 'rules', 'prepare', 'QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK'], [ 'indexer', 'rules', 'set', + '--network', + 'goerli', + 'global', + 'minSignal', + '500', + 'allocationAmount', + '.01', + ], + [ + 'indexer', + 'rules', + 'set', + '--network', + 'goerli', + 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + ], + [ + 'indexer', + 'rules', + 'prepare', + '--network', + 'goerli', + 'QmZfeJYR86UARzp9HiXbURWunYgC9ywvPvoePNbuaATrEK', + ], + [ + 'indexer', + 'rules', + 'set', + '--network', + 'goerli', '0x0000000000000000000000000000000000000000-0', 'allocationAmount', '1000', ], - ['indexer', 'rules', 'offchain', '0x0000000000000000000000000000000000000000-1'], + [ + 'indexer', + 'rules', + 'offchain', + '--network', + 'goerli', + '0x0000000000000000000000000000000000000000-1', + ], [ 'indexer', 'rules', 'set', + '--network', + 'goerli', '0x0000000000000000000000000000000000000000-2', 'allocationAmount', '1000', @@ -150,8 +246,10 @@ export const setup = async () => { ], ] for (const command of commands) { - const { exitCode } = await runIndexerCli(command, process.cwd()) + const { exitCode, stderr, stdout } = await runIndexerCli(command, process.cwd()) if (exitCode == 1) { + console.error(stderr) + console.log(stdout) throw Error(`Setup failed: indexer rules or cost set command failed: ${command}`) } } @@ -175,6 +273,12 @@ export const teardown = async () => { await dropSequelizeModels() } +export const deleteFromAllTables = async () => { + const queryInterface = sequelize.getQueryInterface() + const allTables = await queryInterface.showAllTables() + await Promise.all(allTables.map(tableName => queryInterface.bulkDelete(tableName, {}))) +} + export interface CommandResult { exitCode: number | null stdout: string | undefined @@ -224,12 +328,31 @@ export const cliTest = ( `Make sure there is least one expected output located at the defined 'outputReferencePath', '${outputReferencePath}'`, ) } + + if (INDEXER_SAVE_CLI_TEST_OUTPUT) { + // To aid debugging, persist the output of CLI test commands for reviewing potential issues when tests fail. + // Requires setting the environment variable INDEXER_SAVE_CLI_TEST_OUTPUT. + const outfile = outputReferencePath.replace('references/', '') + const prefix = `/tmp/indexer-cli-test` + if (stdout) { + fs.writeFile(`${prefix}-${outfile}.stdout`, stripAnsi(stdout), 'utf8', err => { + err ? console.error('test: %s, error: %s', outfile, err) : null + }) + } + if (stderr) { + fs.writeFile(`${prefix}-${outfile}.stderr`, stripAnsi(stderr), 'utf8', err => { + err ? console.error('test: %s, error: %s', outfile, err) : null + }) + } + } + if (expectedExitCode !== undefined) { if (exitCode == undefined) { throw new Error('Expected exitCode (found undefined)') } expect(exitCode).toBe(expectedExitCode) } + if (expectedStderr) { if (stderr == undefined) { throw new Error('Expected stderr (found undefined)') diff --git a/packages/indexer-cli/src/actions.ts b/packages/indexer-cli/src/actions.ts index c4b9a944f..b3815ebc0 100644 --- a/packages/indexer-cli/src/actions.ts +++ b/packages/indexer-cli/src/actions.ts @@ -10,6 +10,7 @@ import { nullPassThrough, OrderDirection, parseBoolean, + validateNetworkIdentifier, } from '@graphprotocol/indexer-common' import { validatePOI, validateRequiredParams } from './command-helpers' import gql from 'graphql-tag' @@ -32,6 +33,7 @@ export async function buildActionInput( reason: string, status: ActionStatus, priority: number, + protocolNetwork: string, ): Promise { await validateActionInput(type, actionParams) switch (type) { @@ -44,6 +46,7 @@ export async function buildActionInput( reason, status, priority, + protocolNetwork, } case ActionType.UNALLOCATE: { let poi = actionParams.param2 @@ -60,6 +63,7 @@ export async function buildActionInput( reason, status, priority, + protocolNetwork, } } case ActionType.REALLOCATE: { @@ -78,6 +82,7 @@ export async function buildActionInput( reason, status, priority, + protocolNetwork, } } } @@ -182,6 +187,7 @@ export async function queueActions( reason priority status + protocolNetwork } } `, @@ -206,6 +212,7 @@ const ACTION_PARAMS_PARSERS: Record any> type: x => validateActionType(x), status: x => validateActionStatus(x), reason: nullPassThrough, + protocolNetwork: x => validateNetworkIdentifier(x), } /** @@ -229,26 +236,25 @@ export async function executeApprovedActions( client: IndexerManagementClient, ): Promise { const result = await client - .mutation( - gql` - mutation executeApprovedActions { - executeApprovedActions { - id - status - type - deploymentID - allocationID - amount - poi - force - source - reason - transaction - failureReason - } + .mutation(gql` + mutation executeApprovedActions { + executeApprovedActions { + id + protocolNetwork + status + type + deploymentID + allocationID + amount + poi + force + source + reason + transaction + failureReason } - `, - ) + } + `) .toPromise() if (result.error) { @@ -279,6 +285,7 @@ export async function approveActions( priority transaction status + protocolNetwork } } `, @@ -303,6 +310,7 @@ export async function cancelActions( mutation cancelActions($actionIDs: [Int!]!) { cancelActions(actionIDs: $actionIDs) { id + protocolNetwork type allocationID deploymentID @@ -338,6 +346,7 @@ export async function fetchAction( query action($actionID: Int!) { action(actionID: $actionID) { id + protocolNetwork type allocationID deploymentID @@ -386,6 +395,7 @@ export async function fetchActions( first: $first ) { id + protocolNetwork type allocationID deploymentID @@ -422,6 +432,7 @@ export async function deleteActions( mutation deleteActions($actionIDs: [Int!]!) { deleteActions(actionIDs: $actionIDs) { id + protocolNetwork type allocationID deploymentID @@ -471,6 +482,7 @@ export async function updateActions( transaction status failureReason + protocolNetwork } } `, diff --git a/packages/indexer-cli/src/allocations.ts b/packages/indexer-cli/src/allocations.ts index a0bc6660c..1133746a8 100644 --- a/packages/indexer-cli/src/allocations.ts +++ b/packages/indexer-cli/src/allocations.ts @@ -10,6 +10,7 @@ import { CloseAllocationResult, CreateAllocationResult, ReallocateAllocationResult, + resolveChainAlias, } from '@graphprotocol/indexer-common' export interface IndexerAllocation { @@ -28,6 +29,7 @@ export interface IndexerAllocation { indexingRewards: BigNumber queryFeesCollected: BigNumber status: string + protocolNetwork: string } const ALLOCATION_CONVERTERS_FROM_GRAPHQL: Record< @@ -51,6 +53,7 @@ const ALLOCATION_CONVERTERS_FROM_GRAPHQL: Record< indexingRewards: nullPassThrough((x: string) => BigNumber.from(x)), queryFeesCollected: nullPassThrough((x: string) => BigNumber.from(x)), status: x => x, + protocolNetwork: x => x, } const ALLOCATION_FORMATTERS: Record< @@ -73,6 +76,7 @@ const ALLOCATION_FORMATTERS: Record< indexingRewards: x => utils.commify(formatGRT(x)), queryFeesCollected: x => utils.commify(formatGRT(x)), status: x => x, + protocolNetwork: resolveChainAlias, } /** @@ -171,6 +175,7 @@ export const createAllocation = async ( deployment: string, amount: BigNumber, indexNode: string | undefined, + protocolNetwork: string, ): Promise => { const result = await client .mutation( @@ -178,16 +183,19 @@ export const createAllocation = async ( mutation createAllocation( $deployment: String! $amount: String! + $protocolNetwork: String! $indexNode: String ) { createAllocation( deployment: $deployment amount: $amount + protocolNetwork: $protocolNetwork indexNode: $indexNode ) { allocation deployment allocatedTokens + protocolNetwork } } `, @@ -195,6 +203,7 @@ export const createAllocation = async ( deployment, amount: amount.toString(), indexNode, + protocolNetwork, }, ) .toPromise() @@ -211,23 +220,36 @@ export const closeAllocation = async ( allocationID: string, poi: string | undefined, force: boolean, + protocolNetwork: string, ): Promise => { const result = await client .mutation( gql` - mutation closeAllocation($allocation: String!, $poi: String, $force: Boolean) { - closeAllocation(allocation: $allocation, poi: $poi, force: $force) { + mutation closeAllocation( + $allocation: String! + $poi: String + $force: Boolean + $protocolNetwork: String! + ) { + closeAllocation( + allocation: $allocation + poi: $poi + force: $force + protocolNetwork: $protocolNetwork + ) { allocation allocatedTokens indexingRewards receiptsWorthCollecting + protocolNetwork } } `, { allocation: allocationID, - poi: poi, - force: force, + poi, + force, + protocolNetwork, }, ) .toPromise() @@ -245,6 +267,7 @@ export const reallocateAllocation = async ( poi: string | undefined, amount: BigNumber, force: boolean, + protocolNetwork: string, ): Promise => { const result = await client .mutation( @@ -254,26 +277,30 @@ export const reallocateAllocation = async ( $poi: String $amount: String! $force: Boolean + $protocolNetwork: String! ) { reallocateAllocation( allocation: $allocation poi: $poi amount: $amount force: $force + protocolNetwork: $protocolNetwork ) { closedAllocation indexingRewardsCollected receiptsWorthCollecting createdAllocation createdAllocationStake + protocolNetwork } } `, { allocation: allocationID, - poi: poi, + poi, amount: amount.toString(), - force: force, + force, + protocolNetwork, }, ) .toPromise() diff --git a/packages/indexer-cli/src/command-helpers.ts b/packages/indexer-cli/src/command-helpers.ts index 65995927d..2d8b1aef8 100644 --- a/packages/indexer-cli/src/command-helpers.ts +++ b/packages/indexer-cli/src/command-helpers.ts @@ -36,6 +36,7 @@ export enum OutputFormat { import yaml from 'yaml' import { GluegunParameters, GluegunPrint } from 'gluegun' import { utils } from 'ethers' +import { validateNetworkIdentifier } from '@graphprotocol/indexer-common' export const fixParameters = ( parameters: GluegunParameters, @@ -206,3 +207,47 @@ export function suggestCommands( ) return suggestions.length > 0 ? suggestions : supported_commands } + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function extractProtocolNetworkOption( + options: { + [key: string]: any + }, + required = false, +): string | undefined { + const { n, network } = options + + // Tries to extract the --network option from Gluegun options. + // Throws if required is set to true and the option is not found. + if (!n && !network) { + if (required) { + throw new Error("The option '--network' is required") + } else { + return undefined + } + } + + // Check for invalid usage + const allowedUsages = + (n === undefined && typeof network === 'string') || + (network === undefined && typeof n === 'string') + if (!allowedUsages) { + throw new Error("Invalid usage of the option '--network'") + } + const input = (network ?? n) as string + + try { + return validateNetworkIdentifier(input) + } catch (parseError) { + throw new Error(`Invalid value for the option '--network'. ${parseError}`) + } +} + +// Same as `extractProtocolNetworkOption`, but always require the --network option to be set +export function requireProtocolNetworkOption(options: { [key: string]: any }): string { + const protocolNetwork = extractProtocolNetworkOption(options, true) + if (!protocolNetwork) { + throw new Error("The option '--network' is required") + } + return protocolNetwork +} diff --git a/packages/indexer-cli/src/commands/indexer/actions/approve.ts b/packages/indexer-cli/src/commands/indexer/actions/approve.ts index 315a9e5c4..e17a5b54c 100644 --- a/packages/indexer-cli/src/commands/indexer/actions/approve.ts +++ b/packages/indexer-cli/src/commands/indexer/actions/approve.ts @@ -7,18 +7,20 @@ import { fixParameters, printObjectOrArray, parseOutputFormat, + extractProtocolNetworkOption, } from '../../../command-helpers' import { approveActions, fetchActions } from '../../../actions' -import { ActionStatus } from '@graphprotocol/indexer-common' +import { ActionStatus, resolveChainAlias } from '@graphprotocol/indexer-common' const HELP = ` ${chalk.bold('graph indexer actions approve')} [options] [ ...] -${chalk.bold('graph indexer actions approve')} [options] queued +${chalk.bold('graph indexer actions approve')} [options] queued --network ${chalk.dim('Options:')} -h, --help Show usage information - -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML + -n, --network Filter action selection by their protocol network (mainnet, arbitrum-one, goerli, arbitrum-goerli) + -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML ` module.exports = { @@ -45,6 +47,7 @@ module.exports = { return } + const protocolNetwork = extractProtocolNetworkOption(parameters.options) let numericActionIDs: number[] const config = loadValidatedConfig() @@ -56,17 +59,35 @@ module.exports = { // If actionIDs is 'queued', then populate actionIDs with actions that are queued if (actionIDs.join() == 'queued') { + if (!protocolNetwork) { + throw new Error( + `Missing required option for approving queued actions: --network`, + ) + } const queuedActions = await fetchActions(client, { status: ActionStatus.QUEUED, + protocolNetwork, }) + numericActionIDs = queuedActions.map(action => action.id) if (numericActionIDs.length === 0) { - throw Error(`No 'queued' actions found`) + throw Error(`No 'queued' actions found for network '${protocolNetwork}'`) } } else { numericActionIDs = actionIDs.map(action => +action) } + // Ensure all provided actionIDs are positive numbers + const invalidActionIDs: string[] = [] + numericActionIDs.forEach((id, index) => { + if (isNaN(id) || id < 1) { + invalidActionIDs.push(actionIDs[index]) + } + }) + if (invalidActionIDs.length > 0) { + throw Error(`Invaild action IDs: ${invalidActionIDs}`) + } + inputSpinner.succeed('Processed input parameters') } catch (error) { inputSpinner.fail(error.toString()) @@ -79,10 +100,16 @@ module.exports = { try { const queuedAction = await approveActions(client, numericActionIDs) + // Format Actions 'protocolNetwork' field to display human-friendly chain aliases instead of CAIP2-IDs + queuedAction.forEach( + action => (action.protocolNetwork = resolveChainAlias(action.protocolNetwork)), + ) + actionSpinner.succeed(`Actions approved`) printObjectOrArray(print, outputFormat, queuedAction, [ 'id', 'type', + 'protocolNetwork', 'deploymentID', 'allocationID', 'amount', diff --git a/packages/indexer-cli/src/commands/indexer/actions/cancel.ts b/packages/indexer-cli/src/commands/indexer/actions/cancel.ts index ea75b22a7..d5b74701c 100644 --- a/packages/indexer-cli/src/commands/indexer/actions/cancel.ts +++ b/packages/indexer-cli/src/commands/indexer/actions/cancel.ts @@ -1,6 +1,6 @@ import { GluegunToolbox } from 'gluegun' import chalk from 'chalk' - +import { resolveChainAlias } from '@graphprotocol/indexer-common' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' import { fixParameters, printObjectOrArray } from '../../../command-helpers' @@ -12,7 +12,7 @@ ${chalk.bold('graph indexer actions cancel')} [options] [ ...] ${chalk.dim('Options:')} -h, --help Show usage information - -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML + -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML ` module.exports = { @@ -66,8 +66,15 @@ module.exports = { const queuedAction = await cancelActions(client, numericActionIDs) actionSpinner.succeed(`Actions canceled`) + + // Format Actions 'protocolNetwork' field to display human-friendly chain aliases instead of CAIP2-IDs + queuedAction.forEach( + action => (action.protocolNetwork = resolveChainAlias(action.protocolNetwork)), + ) + printObjectOrArray(print, outputFormat, queuedAction, [ 'id', + 'protocolNetwork', 'type', 'deploymentID', 'allocationID', diff --git a/packages/indexer-cli/src/commands/indexer/actions/execute.ts b/packages/indexer-cli/src/commands/indexer/actions/execute.ts index d8e27c85b..a742f21cd 100644 --- a/packages/indexer-cli/src/commands/indexer/actions/execute.ts +++ b/packages/indexer-cli/src/commands/indexer/actions/execute.ts @@ -1,6 +1,6 @@ import { GluegunToolbox } from 'gluegun' import chalk from 'chalk' - +import { resolveChainAlias } from '@graphprotocol/indexer-common' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' import { fixParameters, printObjectOrArray } from '../../../command-helpers' @@ -12,7 +12,7 @@ ${chalk.bold('graph indexer actions execute approved')} [options] ${chalk.dim('Options:')} -h, --help Show usage information - -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML + -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML ` module.exports = { @@ -60,8 +60,14 @@ module.exports = { spinner.succeed(`Executed approved actions`) + // Format Actions 'protocolNetwork' field to display human-friendly chain aliases instead of CAIP2-IDs + executedActions.forEach( + action => (action.protocolNetwork = resolveChainAlias(action.protocolNetwork)), + ) + printObjectOrArray(print, outputFormat, executedActions, [ 'id', + 'protocolNetwork', 'status', 'type', 'deploymentID', diff --git a/packages/indexer-cli/src/commands/indexer/actions/get.ts b/packages/indexer-cli/src/commands/indexer/actions/get.ts index dc805ca34..3c603ba41 100644 --- a/packages/indexer-cli/src/commands/indexer/actions/get.ts +++ b/packages/indexer-cli/src/commands/indexer/actions/get.ts @@ -6,20 +6,23 @@ import { ActionParams, ActionResult, OrderDirection, + resolveChainAlias, } from '@graphprotocol/indexer-common' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' -import { fixParameters, printObjectOrArray } from '../../../command-helpers' +import { + fixParameters, + printObjectOrArray, + extractProtocolNetworkOption, +} from '../../../command-helpers' import { fetchAction, fetchActions } from '../../../actions' const HELP = ` ${chalk.bold('graph indexer actions get')} [options] -${chalk.bold('graph indexer allocations get')} [options] -${chalk.bold('graph indexer allocations get')} [options] all - ${chalk.dim('Options:')} -h, --help Show usage information + -n, --network Filter by protocol network (mainnet, arbitrum-one, goerli, arbitrum-goerli) --type allocate|unallocate|reallocate|collect Filter by type --status queued|approved|pending|success|failed|canceled Filter by status --source Fetch only actions queued by a specific source @@ -33,6 +36,7 @@ ${chalk.dim('Options:')} const actionFields: (keyof Action)[] = [ 'id', + 'protocolNetwork', 'type', 'deploymentID', 'allocationID', @@ -92,6 +96,10 @@ module.exports = { let orderDirectionValue = OrderDirection.DESC const outputFormat = o || output || 'table' + const protocolNetwork: string | undefined = extractProtocolNetworkOption( + parameters.options, + ) + if (help || h) { inputSpinner.stopAndPersist({ symbol: '💁', text: HELP }) return @@ -125,7 +133,7 @@ module.exports = { if (action == 'all') { if (type || status || source || reason) { throw Error( - `Invalid query, cannot specify '--type', '--status', '--source' or '--reason' filters in addition to 'action = all'`, + `Invalid query, cannot specify '--type', '--status', '--source', or '--reason' filters in addition to 'action = all'`, ) } } @@ -186,6 +194,7 @@ module.exports = { status, source, reason, + protocolNetwork, }, first, orderByParam, @@ -202,6 +211,12 @@ module.exports = { const displayProperties = actionFields.filter(field => selectedFields.includes(field), ) + + // Format Actions 'protocolNetwork' field to display human-friendly chain aliases instead of CAIP2-IDs + actions.forEach( + action => (action.protocolNetwork = resolveChainAlias(action.protocolNetwork)), + ) + printObjectOrArray(print, outputFormat, actions, displayProperties) } catch (error) { actionSpinner.fail(error.toString()) diff --git a/packages/indexer-cli/src/commands/indexer/actions/queue.ts b/packages/indexer-cli/src/commands/indexer/actions/queue.ts index 76ff9f07d..c05d3707d 100644 --- a/packages/indexer-cli/src/commands/indexer/actions/queue.ts +++ b/packages/indexer-cli/src/commands/indexer/actions/queue.ts @@ -3,9 +3,16 @@ import chalk from 'chalk' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' -import { printObjectOrArray } from '../../../command-helpers' +import { + requireProtocolNetworkOption, + printObjectOrArray, +} from '../../../command-helpers' import { buildActionInput, queueActions, validateActionType } from '../../../actions' -import { ActionInput, ActionStatus } from '@graphprotocol/indexer-common' +import { + ActionInput, + ActionStatus, + resolveChainAlias, +} from '@graphprotocol/indexer-common' const HELP = ` ${chalk.bold( @@ -21,8 +28,9 @@ ${chalk.bold( ${chalk.dim('Options:')} - -h, --help Show usage information - -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML + -h, --help Show usage informatio + -n, --network [Required] The protocol network for this action (mainnet, arbitrum-one, goerli, arbitrum-goerli) + -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML -s, --source Specify the source of the action decision -r, --reason Specify the reason for the action to be taken -p, --priority Define a priority order for the action @@ -65,6 +73,8 @@ module.exports = { ) } + const networkIdentifier = requireProtocolNetworkOption(parameters.options) + actionInputParams = await buildActionInput( validateActionType(type), { targetDeployment, param1, param2, param3, param4 }, @@ -72,6 +82,7 @@ module.exports = { decisionReason, ActionStatus.QUEUED, executionPriority, + networkIdentifier, ) inputSpinner.succeed(`Processed input parameters`) @@ -93,8 +104,14 @@ module.exports = { actionSpinner.succeed(`${type} action added to queue`) + // Format Actions 'protocolNetwork' field to display human-friendly chain aliases instead of CAIP2-IDs + queuedAction.forEach( + action => (action.protocolNetwork = resolveChainAlias(action.protocolNetwork)), + ) + printObjectOrArray(print, outputFormat, queuedAction, [ 'id', + 'protocolNetwork', 'type', 'deploymentID', 'allocationID', diff --git a/packages/indexer-cli/src/commands/indexer/actions/update.ts b/packages/indexer-cli/src/commands/indexer/actions/update.ts index 4beaaaaf2..6ea470cac 100644 --- a/packages/indexer-cli/src/commands/indexer/actions/update.ts +++ b/packages/indexer-cli/src/commands/indexer/actions/update.ts @@ -1,7 +1,12 @@ import { GluegunToolbox } from 'gluegun' import chalk from 'chalk' -import { Action, ActionFilter, ActionUpdateInput } from '@graphprotocol/indexer-common' +import { + Action, + ActionFilter, + ActionUpdateInput, + resolveChainAlias, +} from '@graphprotocol/indexer-common' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' import { fixParameters, printObjectOrArray } from '../../../command-helpers' @@ -98,6 +103,7 @@ module.exports = { const displayProperties: (keyof Action)[] = [ 'id', 'type', + 'protocolNetwork', 'deploymentID', 'allocationID', 'amount', @@ -111,6 +117,11 @@ module.exports = { 'reason', ] + // Format Actions 'protocolNetwork' field to display human-friendly chain aliases instead of CAIP2-IDs + actionsUpdated.forEach( + action => (action.protocolNetwork = resolveChainAlias(action.protocolNetwork)), + ) + printObjectOrArray(print, outputFormat, actionsUpdated, displayProperties) } catch (error) { actionSpinner.fail(error.toString()) diff --git a/packages/indexer-cli/src/commands/indexer/allocations/close.ts b/packages/indexer-cli/src/commands/indexer/allocations/close.ts index 652082b5c..7976eb1ae 100644 --- a/packages/indexer-cli/src/commands/indexer/allocations/close.ts +++ b/packages/indexer-cli/src/commands/indexer/allocations/close.ts @@ -3,18 +3,21 @@ import chalk from 'chalk' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' -import { utils } from 'ethers' import { closeAllocation } from '../../../allocations' -import { printObjectOrArray } from '../../../command-helpers' +import { validatePOI, printObjectOrArray } from '../../../command-helpers' +import { validateNetworkIdentifier } from '@graphprotocol/indexer-common' const HELP = ` -${chalk.bold('graph indexer allocations close')} [options] +${chalk.bold('graph indexer allocations close')} [options] ${chalk.dim('Options:')} -h, --help Show usage information -f, --force Bypass POIaccuracy checks and submit transaction with provided data - -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML + -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML + +${chalk.dim('Networks:')} + mainnet, arbitrum-one, goerli or arbitrum goerli ` module.exports = { @@ -43,8 +46,7 @@ module.exports = { return } - const [id] = parameters.array || [] - let [, poi] = parameters.array || [] + const [network, id, unformattedPoi] = parameters.array || [] if (id === undefined) { spinner.fail(`Missing required argument: 'id'`) @@ -53,28 +55,36 @@ module.exports = { return } - if (poi !== undefined) { - if (typeof poi == 'number' && poi == 0) { - poi = utils.hexlify(Array(32).fill(0)) - } + let protocolNetwork: string + if (!network) { + spinner.fail(`Missing required argument: 'network'`) + print.info(HELP) + process.exitCode = 1 + return + } else { try { - // Ensure user provided POI is formatted properly - '0x...' (32 bytes) - const isHex = utils.isHexString(poi, 32) - if (!isHex) { - throw new Error('Must be a 32 byte length hex string') - } + protocolNetwork = validateNetworkIdentifier(network) } catch (error) { - spinner.fail(`Invalid POI provided, '${poi}'. ` + error.toString()) + spinner.fail(`Invalid value for argument 'network': '${network}' `) process.exitCode = 1 return } } + let poi: string | undefined + try { + poi = validatePOI(unformattedPoi) + } catch (error) { + spinner.fail(`Invalid POI provided, '${unformattedPoi}'. ` + error.message) + process.exitCode = 1 + return + } + spinner.text = `Closing allocation '${id}` try { const config = loadValidatedConfig() const client = await createIndexerManagementClient({ url: config.api }) - const closeResult = await closeAllocation(client, id, poi, toForce) + const closeResult = await closeAllocation(client, id, poi, toForce, protocolNetwork) spinner.succeed('Allocation closed') printObjectOrArray(print, outputFormat, closeResult, [ diff --git a/packages/indexer-cli/src/commands/indexer/allocations/create.ts b/packages/indexer-cli/src/commands/indexer/allocations/create.ts index 64c49a450..166cb61b8 100644 --- a/packages/indexer-cli/src/commands/indexer/allocations/create.ts +++ b/packages/indexer-cli/src/commands/indexer/allocations/create.ts @@ -5,19 +5,26 @@ import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' import { BigNumber } from 'ethers' import { createAllocation } from '../../../allocations' -import { processIdentifier, SubgraphIdentifierType } from '@graphprotocol/indexer-common' +import { + processIdentifier, + SubgraphIdentifierType, + validateNetworkIdentifier, +} from '@graphprotocol/indexer-common' import { printObjectOrArray } from '../../../command-helpers' const HELP = ` ${chalk.bold( 'graph indexer allocations create', -)} [options] +)} [options] ${chalk.dim('Options:')} -h, --help Show usage information -f, --force Bypass POI accuracy checks and submit transaction with provided data - -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML + -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML + +${chalk.dim('Networks:')} + mainnet, arbitrum-one, goerli or arbitrum goerli ` module.exports = { @@ -45,14 +52,23 @@ module.exports = { return } - const [deploymentID, amount, indexNode] = parameters.array || [] + const [deploymentID, protocolNetwork, amount, indexNode] = parameters.array || [] try { - if (!deploymentID || !amount) { + if (!deploymentID || !amount || !protocolNetwork) { throw new Error( - `Must provide a deployment ID and allocation amount (deploymentID: '${deploymentID}', allocationAmount: '${amount}'`, + 'Must provide a deployment ID, a network identifier and allocation amount' + + `(deploymentID: '${deploymentID}', network: '${protocolNetwork}' allocationAmount: '${amount}')`, ) } + + // This nested try block is necessary to complement the parsing error with the 'network' field. + try { + validateNetworkIdentifier(protocolNetwork) + } catch (parsingError) { + throw new Error(`Invalid 'network' provided. ${parsingError}`) + } + const [deploymentString, type] = await processIdentifier(deploymentID, { all: false, global: false, @@ -73,6 +89,7 @@ module.exports = { deploymentString, allocationAmount, indexNode, + protocolNetwork, ) spinner.succeed('Allocation created') @@ -80,6 +97,7 @@ module.exports = { 'allocation', 'deployment', 'allocatedTokens', + 'protocolNetwork', ]) } catch (error) { spinner.fail(error.toString()) diff --git a/packages/indexer-cli/src/commands/indexer/allocations/get.ts b/packages/indexer-cli/src/commands/indexer/allocations/get.ts index 39b924c47..72e17ebb7 100644 --- a/packages/indexer-cli/src/commands/indexer/allocations/get.ts +++ b/packages/indexer-cli/src/commands/indexer/allocations/get.ts @@ -3,7 +3,7 @@ import chalk from 'chalk' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' -import { fixParameters } from '../../../command-helpers' +import { extractProtocolNetworkOption, fixParameters } from '../../../command-helpers' import gql from 'graphql-tag' import { SubgraphDeploymentID } from '@graphprotocol/common-ts' import { processIdentifier, SubgraphIdentifierType } from '@graphprotocol/indexer-common' @@ -18,7 +18,8 @@ ${chalk.bold('graph indexer allocations get')} [options] all ${chalk.dim('Options:')} -h, --help Show usage information - --status active|closed|claimable Filter by status + -n, --network Filter allocations by their protocol network (mainnet, arbitrum-one, goerli, arbitrum-goerli) + --status active|closed|claimable Filter by status --deployment Fetch only allocations for a specific subgraph deployment -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML ` @@ -43,6 +44,8 @@ module.exports = { } try { + const protocolNetwork = extractProtocolNetworkOption(parameters.options) + if (!['json', 'yaml', 'table'].includes(outputFormat)) { throw Error( `Invalid output format "${outputFormat}" must be one of 'json', 'yaml' or 'table'`, @@ -96,6 +99,7 @@ module.exports = { query allocations($filter: AllocationFilter!) { allocations(filter: $filter) { id + protocolNetwork indexer subgraphDeployment allocatedTokens @@ -117,6 +121,7 @@ module.exports = { filter: { status: status ? status : null, allocation: allocation ? allocation : null, + protocolNetwork, }, }, ) @@ -140,6 +145,7 @@ module.exports = { let displayProperties: (keyof IndexerAllocation)[] = [ 'id', + 'protocolNetwork', 'indexer', 'subgraphDeployment', 'allocatedTokens', diff --git a/packages/indexer-cli/src/commands/indexer/allocations/reallocate.ts b/packages/indexer-cli/src/commands/indexer/allocations/reallocate.ts index 3f1c5c579..f5a8b3d03 100644 --- a/packages/indexer-cli/src/commands/indexer/allocations/reallocate.ts +++ b/packages/indexer-cli/src/commands/indexer/allocations/reallocate.ts @@ -8,12 +8,17 @@ import { reallocateAllocation } from '../../../allocations' import { printObjectOrArray, validatePOI } from '../../../command-helpers' const HELP = ` -${chalk.bold('graph indexer allocations reallocate')} [options] +${chalk.bold( + 'graph indexer allocations reallocate', +)} [options] ${chalk.dim('Options:')} -h, --help Show usage information - -f, --force Bypass POI accuracy checks and submit transaction with provided data + -f, --force Bypass POI accuracy checks and submit transaction with provided data + +${chalk.dim('Networks:')} + mainnet, arbitrum-one, goerli or arbitrum goerli ` module.exports = { @@ -43,7 +48,14 @@ module.exports = { } // eslint-disable-next-line prefer-const - let [id, amount, poi] = parameters.array || [] + let [network, id, amount, poi] = parameters.array || [] + + if (network === undefined) { + spinner.fail(`Missing required argument: 'network'`) + print.info(HELP) + process.exitCode = 1 + return + } if (id === undefined) { spinner.fail(`Missing required argument: 'id'`) @@ -60,7 +72,7 @@ module.exports = { } try { - await validatePOI(poi) + validatePOI(poi) const allocationAmount = BigNumber.from(amount) const config = loadValidatedConfig() const client = await createIndexerManagementClient({ url: config.api }) @@ -72,6 +84,7 @@ module.exports = { poi, allocationAmount, toForce, + network, ) spinner.succeed('Reallocated') diff --git a/packages/indexer-cli/src/commands/indexer/disputes/get.ts b/packages/indexer-cli/src/commands/indexer/disputes/get.ts index 4dd46cd17..51029d12e 100644 --- a/packages/indexer-cli/src/commands/indexer/disputes/get.ts +++ b/packages/indexer-cli/src/commands/indexer/disputes/get.ts @@ -4,15 +4,16 @@ import chalk from 'chalk' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' import { disputes, printDisputes } from '../../../disputes' -import { parseOutputFormat } from '../../../command-helpers' +import { parseOutputFormat, extractProtocolNetworkOption } from '../../../command-helpers' const HELP = ` ${chalk.bold( 'graph indexer disputes get', )} [options] potential - + ${chalk.dim('Options:')} + -n, --network Filter by protocol network (mainnet, arbitrum-one, goerli, arbitrum-goerli) -h, --help Show usage information -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML ` @@ -54,8 +55,13 @@ module.exports = { // Create indexer API client const client = await createIndexerManagementClient({ url: config.api }) try { - const storedDisputes = await disputes(client, status, +minAllocationClosedEpoch) - + const protocolNetwork = extractProtocolNetworkOption(parameters.options) + const storedDisputes = await disputes( + client, + status, + +minAllocationClosedEpoch, + protocolNetwork, + ) printDisputes(print, outputFormat, storedDisputes) } catch (error) { print.error(error.toString()) diff --git a/packages/indexer-cli/src/commands/indexer/rules/clear.ts b/packages/indexer-cli/src/commands/indexer/rules/clear.ts index 44a010748..421dc0505 100644 --- a/packages/indexer-cli/src/commands/indexer/rules/clear.ts +++ b/packages/indexer-cli/src/commands/indexer/rules/clear.ts @@ -4,7 +4,11 @@ import { partition } from '@thi.ng/iterators' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' -import { fixParameters, parseOutputFormat } from '../../../command-helpers' +import { + requireProtocolNetworkOption, + fixParameters, + parseOutputFormat, +} from '../../../command-helpers' import { setIndexingRule, displayRules, parseIndexingRule } from '../../../rules' import { processIdentifier } from '@graphprotocol/indexer-common' @@ -17,6 +21,7 @@ ${chalk.bold('graph indexer rules reset')} [options] [ ${chalk.dim('Options:')} -h, --help Show usage information + -n, --network [Required] the rule's protocol network (mainnet, arbitrum-one, goerli, arbitrum-goerli) -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML ` @@ -23,7 +28,6 @@ module.exports = { description: 'Remove one or many indexing rules', run: async (toolbox: GluegunToolbox) => { const { print, parameters } = toolbox - const { h, help, o, output } = parameters.options const [id] = fixParameters(parameters, { h, help }) || [] const outputFormat = parseOutputFormat(print, o || output || 'table') @@ -40,6 +44,7 @@ module.exports = { const config = loadValidatedConfig() try { + const protocolNetwork = requireProtocolNetworkOption(parameters.options) const [identifier, identifierType] = await processIdentifier(id, { all: true, global: true, @@ -48,28 +53,65 @@ module.exports = { const client = await createIndexerManagementClient({ url: config.api }) if (identifier === 'all') { - const rules = await indexingRules(client, false) + const rules = await indexingRules(client, false, protocolNetwork) - /* eslint-disable @typescript-eslint/no-non-null-assertion */ - await deleteIndexingRules( - client, - await Promise.all( - rules.map( - async rule => - ( - await processIdentifier(rule.identifier!, { all: true, global: true }) - )[0], - ), - ), + const rulesIdentifiers = await Promise.all( + rules.map(async function (rule) { + const identifier = ( + await processIdentifier( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + rule.identifier!, + { + all: true, + global: true, + }, + ) + )[0] + + // All rules returned from `indexingRules` are have a `protocolNetwork` field, so we + // don't expect to see this error. + if (!rule.protocolNetwork) { + throw Error( + `Indexing Rule is missing a 'protocolNetwork' attribute: ${JSON.stringify( + rule, + )}`, + ) + } + + return { + identifier, + protocolNetwork: rule.protocolNetwork, + } + }), ) + + /* eslint-disable @typescript-eslint/no-non-null-assertion */ + await deleteIndexingRules(client, rulesIdentifiers) /* eslint-enable @typescript-eslint/no-non-null-assertion */ - print.success(`Deleted all indexing rules`) - } else if (identifier === 'global') { - await deleteIndexingRules(client, ['global']) - print.warning(`Reset global indexing rules (the global rules cannot be deleted)`) + print.success('Deleted all indexing rules') + return + } + + // Since we are not deleting all rules, we must require a protocol network from the user + if (!protocolNetwork) { + throw Error( + 'The --netowrk option must be used when deleting a glboal Indexing Rule', + ) + } + const chainAlias = resolveChainAlias(protocolNetwork) + + if (identifier === 'global') { + const globalIdentifier = { identifier, protocolNetwork } + await deleteIndexingRules(client, [globalIdentifier]) + print.warning( + `Reset global indexing rules for network '${chainAlias}' (global rules cannot be deleted)`, + ) } else { - await deleteIndexingRules(client, [identifier]) - print.success(`Deleted indexing rules for "${identifier}" (${identifierType})`) + const ruleIdentifier = { identifier, protocolNetwork } + await deleteIndexingRules(client, [ruleIdentifier]) + print.success( + `Deleted indexing rules for "${identifier}" (${identifierType}) on network: '${chainAlias}'`, + ) } } catch (error) { print.error(error.toString()) diff --git a/packages/indexer-cli/src/commands/indexer/rules/get.ts b/packages/indexer-cli/src/commands/indexer/rules/get.ts index eb99ba80a..c8c3d2b1f 100644 --- a/packages/indexer-cli/src/commands/indexer/rules/get.ts +++ b/packages/indexer-cli/src/commands/indexer/rules/get.ts @@ -3,7 +3,11 @@ import chalk from 'chalk' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' -import { fixParameters, parseOutputFormat } from '../../../command-helpers' +import { + extractProtocolNetworkOption, + fixParameters, + parseOutputFormat, +} from '../../../command-helpers' import { indexingRule, indexingRules, displayRules } from '../../../rules' import { IndexingRuleAttributes, processIdentifier } from '@graphprotocol/indexer-common' @@ -15,6 +19,7 @@ ${chalk.bold('graph indexer rules get')} [options] [ ...] ${chalk.dim('Options:')} -h, --help Show usage information + -n, --network Filter by the rule's protocol network (mainnet, arbitrum-one, goerli, arbitrum-goerli) --merged Shows the deployment rules and global rules merged -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML ` @@ -40,8 +45,8 @@ module.exports = { } try { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [identifier, identifierType] = await processIdentifier(id, { + const protocolNetwork = extractProtocolNetworkOption(parameters.options) + const [identifier] = await processIdentifier(id ?? 'all', { all: true, global: true, }) @@ -51,10 +56,18 @@ module.exports = { // Create indexer API client const client = await createIndexerManagementClient({ url: config.api }) - const ruleOrRules = - identifier === 'all' - ? await indexingRules(client, !!merged) - : await indexingRule(client, identifier, !!merged) + let ruleOrRules + if (identifier === 'all') { + ruleOrRules = await indexingRules(client, !!merged, protocolNetwork) + } else { + if (!protocolNetwork) { + throw Error( + 'The --netowrk option must be used when quering for a single Indexing Rule', + ) + } + const ruleIdentifier = { identifier, protocolNetwork } + ruleOrRules = await indexingRule(client, ruleIdentifier, !!merged) + } print.info( displayRules( diff --git a/packages/indexer-cli/src/commands/indexer/rules/maybe.ts b/packages/indexer-cli/src/commands/indexer/rules/maybe.ts index 95cf3566a..603c39117 100644 --- a/packages/indexer-cli/src/commands/indexer/rules/maybe.ts +++ b/packages/indexer-cli/src/commands/indexer/rules/maybe.ts @@ -3,7 +3,11 @@ import chalk from 'chalk' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' -import { fixParameters, parseOutputFormat } from '../../../command-helpers' +import { + requireProtocolNetworkOption, + fixParameters, + parseOutputFormat, +} from '../../../command-helpers' import { IndexingDecisionBasis, processIdentifier } from '@graphprotocol/indexer-common' import { setIndexingRule, displayRules, parseIndexingRule } from '../../../rules' @@ -14,6 +18,7 @@ ${chalk.bold('graph indexer rules maybe')} [options] ${chalk.dim('Options:')} -h, --help Show usage information + -n, --network [Required] the rule's protocol network (mainnet, arbitrum-one, goerli, arbitrum-goerli) -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML ` @@ -40,6 +45,7 @@ module.exports = { const config = loadValidatedConfig() try { + const protocolNetwork = requireProtocolNetworkOption(parameters.options) const [identifier, identifierType] = await processIdentifier(id, { all: false, global: true, @@ -49,6 +55,7 @@ module.exports = { identifier, identifierType, decisionBasis: IndexingDecisionBasis.RULES, + protocolNetwork, }) const client = await createIndexerManagementClient({ url: config.api }) diff --git a/packages/indexer-cli/src/commands/indexer/rules/offchain.ts b/packages/indexer-cli/src/commands/indexer/rules/offchain.ts index 7109c3306..0c43b3166 100644 --- a/packages/indexer-cli/src/commands/indexer/rules/offchain.ts +++ b/packages/indexer-cli/src/commands/indexer/rules/offchain.ts @@ -3,7 +3,11 @@ import chalk from 'chalk' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' -import { fixParameters, parseOutputFormat } from '../../../command-helpers' +import { + requireProtocolNetworkOption, + fixParameters, + parseOutputFormat, +} from '../../../command-helpers' import { IndexingDecisionBasis, processIdentifier } from '@graphprotocol/indexer-common' import { setIndexingRule, displayRules, parseIndexingRule } from '../../../rules' @@ -15,7 +19,7 @@ ${chalk.bold('graph indexer rules prepare')} [options] ${chalk.dim('Options:')} - -h, --help Show usage information +, -h, --help Show usage information -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML ` @@ -42,6 +46,7 @@ module.exports = { const config = loadValidatedConfig() try { + const protocolNetwork = requireProtocolNetworkOption(parameters.options) const [identifier, identifierType] = await processIdentifier(id, { all: false, global: true, @@ -50,6 +55,7 @@ module.exports = { const inputRule = parseIndexingRule({ identifier, identifierType, + protocolNetwork, decisionBasis: IndexingDecisionBasis.OFFCHAIN, }) diff --git a/packages/indexer-cli/src/commands/indexer/rules/set.ts b/packages/indexer-cli/src/commands/indexer/rules/set.ts index 165212427..ad0f15094 100644 --- a/packages/indexer-cli/src/commands/indexer/rules/set.ts +++ b/packages/indexer-cli/src/commands/indexer/rules/set.ts @@ -5,6 +5,7 @@ import { partition } from '@thi.ng/iterators' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' import { + requireProtocolNetworkOption, fixParameters, parseOutputFormat, suggestCommands, @@ -25,6 +26,7 @@ ${chalk.bold('graph indexer rules set')} [options] = 2) { print.error( @@ -88,7 +92,8 @@ module.exports = { } catch (error) { // Failed to parse input, make suggestions // Generate a instance of indexing rules for valid attributes - const tmpRules = await indexingRule(client, 'global', false) + const globalRule = { identifier: 'global', protocolNetwork } + const tmpRules = await indexingRule(client, globalRule, false) if (!tmpRules) { throw new Error( `Global indexing rules missing, try again after the agent ensures global rule`, diff --git a/packages/indexer-cli/src/commands/indexer/rules/start.ts b/packages/indexer-cli/src/commands/indexer/rules/start.ts index 044eb693e..3183c9b34 100644 --- a/packages/indexer-cli/src/commands/indexer/rules/start.ts +++ b/packages/indexer-cli/src/commands/indexer/rules/start.ts @@ -3,7 +3,11 @@ import chalk from 'chalk' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' -import { fixParameters, parseOutputFormat } from '../../../command-helpers' +import { + requireProtocolNetworkOption, + fixParameters, + parseOutputFormat, +} from '../../../command-helpers' import { IndexingDecisionBasis, processIdentifier } from '@graphprotocol/indexer-common' import { setIndexingRule, displayRules, parseIndexingRule } from '../../../rules' @@ -16,6 +20,7 @@ ${chalk.bold('graph indexer rules always')} [options] ${chalk.dim('Options:')} -h, --help Show usage information + -n, --network [Required] the rule's protocol network (mainnet, arbitrum-one, goerli, arbitrum-goerli) -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML ` @@ -42,6 +47,7 @@ module.exports = { const config = loadValidatedConfig() try { + const protocolNetwork = requireProtocolNetworkOption(parameters.options) const [identifier, identifierType] = await processIdentifier(id, { all: false, global: true, @@ -51,6 +57,7 @@ module.exports = { identifier, identifierType, decisionBasis: IndexingDecisionBasis.ALWAYS, + protocolNetwork, }) // Update the indexing rule according to the key/value pairs diff --git a/packages/indexer-cli/src/commands/indexer/rules/stop.ts b/packages/indexer-cli/src/commands/indexer/rules/stop.ts index da7abe76a..8856cf086 100644 --- a/packages/indexer-cli/src/commands/indexer/rules/stop.ts +++ b/packages/indexer-cli/src/commands/indexer/rules/stop.ts @@ -3,7 +3,11 @@ import chalk from 'chalk' import { loadValidatedConfig } from '../../../config' import { createIndexerManagementClient } from '../../../client' -import { fixParameters, parseOutputFormat } from '../../../command-helpers' +import { + requireProtocolNetworkOption, + fixParameters, + parseOutputFormat, +} from '../../../command-helpers' import { IndexingDecisionBasis, processIdentifier } from '@graphprotocol/indexer-common' import { setIndexingRule, displayRules, parseIndexingRule } from '../../../rules' @@ -16,6 +20,7 @@ ${chalk.bold('graph indexer rules never')} [options] ${chalk.dim('Options:')} -h, --help Show usage information + -n, --network [Required] the rule's protocol network (mainnet, arbitrum-one, goerli, arbitrum-goerli) -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML ` @@ -42,6 +47,7 @@ module.exports = { const config = loadValidatedConfig() try { + const protocolNetwork = requireProtocolNetworkOption(parameters.options) const [identifier, identifierType] = await processIdentifier(id, { all: false, global: true, @@ -51,6 +57,7 @@ module.exports = { identifier, identifierType, decisionBasis: IndexingDecisionBasis.NEVER, + protocolNetwork, }) const client = await createIndexerManagementClient({ url: config.api }) diff --git a/packages/indexer-cli/src/commands/indexer/status.ts b/packages/indexer-cli/src/commands/indexer/status.ts index d06cafbf4..7ca1c35cb 100644 --- a/packages/indexer-cli/src/commands/indexer/status.ts +++ b/packages/indexer-cli/src/commands/indexer/status.ts @@ -7,7 +7,13 @@ import { createIndexerManagementClient } from '../../client' import gql from 'graphql-tag' import { displayRules, indexingRuleFromGraphQL } from '../../rules' import { printIndexerAllocations, indexerAllocationFromGraphQL } from '../../allocations' -import { formatData, parseOutputFormat, pickFields } from '../../command-helpers' +import { + requireProtocolNetworkOption, + formatData, + parseOutputFormat, + pickFields, +} from '../../command-helpers' +import { resolveChainAlias } from '@graphprotocol/indexer-common' const HELP = ` ${chalk.bold('graph indexer status')} @@ -15,8 +21,21 @@ ${chalk.bold('graph indexer status')} ${chalk.dim('Options:')} -h, --help Show usage information + -n, --network [Required] the rule's protocol network (mainnet, arbitrum-one, goerli, arbitrum-goerli) ` +interface Endpoint { + url: string | null + healthy: boolean + protocolNetwork: string + tests: any[] +} + +interface Endpoints { + service: Endpoint + status: Endpoint +} + module.exports = { name: 'status', alias: [], @@ -44,13 +63,18 @@ module.exports = { // Query status information let result: any | undefined try { + // TODO:L2: Consider making Protocol Network optional, showing status for all + // networks, combined. + const protocolNetwork = requireProtocolNetworkOption(toolbox.parameters.options) + result = await client .query( gql` - query { - indexerRegistration { + query ($protocolNetwork: String!) { + indexerRegistration(protocolNetwork: $protocolNetwork) { url address + protocolNetwork registered location { latitude @@ -81,8 +105,9 @@ module.exports = { } } - indexerAllocations { + indexerAllocations(protocolNetwork: $protocolNetwork) { id + protocolNetwork allocatedTokens createdAtEpoch closedAtEpoch @@ -91,10 +116,11 @@ module.exports = { stakedTokens } - indexerEndpoints { + indexerEndpoints(protocolNetwork: $protocolNetwork) { service { url healthy + protocolNetwork tests { test error @@ -104,6 +130,7 @@ module.exports = { status { url healthy + protocolNetwork tests { test error @@ -112,8 +139,9 @@ module.exports = { } } - indexingRules(merged: true) { + indexingRules(merged: true, protocolNetwork: $protocolNetwork) { identifier + protocolNetwork identifierType allocationAmount allocationLifetime @@ -131,7 +159,7 @@ module.exports = { } } `, - {}, + { protocolNetwork }, ) .toPromise() } catch (error) { @@ -167,24 +195,25 @@ module.exports = { } if (result.data.indexerEndpoints) { - const keys = Object.keys(pickFields(result.data.indexerEndpoints, [])) - keys.sort() - - const statusUp = outputFormat == 'table' ? chalk.green('up') : 'up' - const statusDown = outputFormat == 'table' ? chalk.red('down') : 'down' - - data.endpoints = keys.reduce( - (out, key) => [ - ...out, + data.endpoints = result.data.indexerEndpoints.flatMap((endpoints: Endpoints) => { + const { service, status } = endpoints + return [ { - name: key, - url: result.data.indexerEndpoints[key].url, - status: result.data.indexerEndpoints[key].healthy ? statusUp : statusDown, - tests: result.data.indexerEndpoints[key].tests, + name: 'service', + url: service.url, + tests: service.tests, + protocolNetwork: resolveChainAlias(service.protocolNetwork), + status: formatStatus(outputFormat, service.healthy), }, - ], - [] as any, - ) + { + name: 'status', + url: status.url, + tests: status.tests, + protocolNetwork: resolveChainAlias(status.protocolNetwork), + status: formatStatus(outputFormat, status.healthy), + }, + ] + }) } else { data.endpoints = { error: @@ -228,15 +257,17 @@ module.exports = { print.info( formatData( data.endpoints.map((endpoint: any) => - pickFields(endpoint, ['name', 'url', 'status']), + pickFields(endpoint, ['name', 'protocolNetwork', 'url', 'status']), ), outputFormat, ), ) if ( - data.endpoints.find((endpoint: any) => - endpoint.tests.find((test: any) => test.error !== null), - ) + data.endpoints.find((endpoint: any) => { + if (endpoint.tests) { + return endpoint.tests.find((test: any) => test.error !== null) + } + }) ) { print.error('The following endpoint tests failed:\n') for (const endpoint of data.endpoints) { @@ -300,3 +331,13 @@ module.exports = { } }, } + +function formatStatus(outputFormat: string, status: boolean) { + return outputFormat === 'table' + ? status + ? chalk.green('up') + : chalk.red('down') + : status + ? 'up' + : 'down' +} diff --git a/packages/indexer-cli/src/cost.ts b/packages/indexer-cli/src/cost.ts index 6c3b5a4d6..af2fc51d8 100644 --- a/packages/indexer-cli/src/cost.ts +++ b/packages/indexer-cli/src/cost.ts @@ -193,17 +193,15 @@ export const costModels = async ( client: IndexerManagementClient, ): Promise[]> => { const result = await client - .query( - gql` - { - costModels { - deployment - model - variables - } + .query(gql` + { + costModels { + deployment + model + variables } - `, - ) + } + `) .toPromise() if (result.error) { diff --git a/packages/indexer-cli/src/disputes.ts b/packages/indexer-cli/src/disputes.ts index 9eab92fbd..e3fa12bb4 100644 --- a/packages/indexer-cli/src/disputes.ts +++ b/packages/indexer-cli/src/disputes.ts @@ -3,12 +3,13 @@ import { POIDisputeAttributes, indexerError, IndexerErrorCode, + resolveChainAlias, } from '@graphprotocol/indexer-common' import gql from 'graphql-tag' import yaml from 'yaml' import { GluegunPrint } from 'gluegun' import { table, getBorderCharacters } from 'table' -import { parseOutputFormat, OutputFormat } from './command-helpers' +import { OutputFormat } from './command-helpers' const DISPUTE_FORMATTERS: Record string> = { allocationID: x => x, @@ -24,6 +25,7 @@ const DISPUTE_FORMATTERS: Record strin previousEpochStartBlockHash: x => x, previousEpochStartBlockNumber: x => x, status: x => x, + protocolNetwork: resolveChainAlias, } const DISPUTES_CONVERTERS_FROM_GRAPHQL: Record< @@ -43,6 +45,7 @@ const DISPUTES_CONVERTERS_FROM_GRAPHQL: Record< previousEpochStartBlockHash: x => x, previousEpochStartBlockNumber: x => +x, status: x => x, + protocolNetwork: x => x, } /** @@ -129,13 +132,22 @@ export const disputes = async ( client: IndexerManagementClient, status: string, minClosedEpoch: number, + protocolNetwork: string | undefined, ): Promise[]> => { try { const result = await client .query( gql` - query disputes($status: String!, $minClosedEpoch: Int!) { - disputes(status: $status, minClosedEpoch: $minClosedEpoch) { + query disputes( + $status: String! + $minClosedEpoch: Int! + $protocolNetwork: String + ) { + disputes( + status: $status + minClosedEpoch: $minClosedEpoch + protocolNetwork: $protocolNetwork + ) { allocationID allocationIndexer allocationAmount @@ -148,12 +160,14 @@ export const disputes = async ( previousEpochStartBlockNumber previousEpochReferenceProof status + protocolNetwork } } `, { status, minClosedEpoch, + protocolNetwork, }, ) .toPromise() diff --git a/packages/indexer-cli/src/rules.ts b/packages/indexer-cli/src/rules.ts index f37cd74c1..e50027849 100644 --- a/packages/indexer-cli/src/rules.ts +++ b/packages/indexer-cli/src/rules.ts @@ -6,6 +6,8 @@ import { IndexerManagementClient, IndexingRuleAttributes, IndexingDecisionBasis, + IndexingRuleIdentifier, + resolveChainAlias, } from '@graphprotocol/indexer-common' import gql from 'graphql-tag' import yaml from 'yaml' @@ -34,6 +36,7 @@ const INDEXING_RULE_PARSERS: Record custom: nullPassThrough(JSON.parse), requireSupported: x => parseBoolean(x), safety: x => parseBoolean(x), + protocolNetwork: x => x, } const INDEXING_RULE_FORMATTERS: Record< @@ -57,6 +60,7 @@ const INDEXING_RULE_FORMATTERS: Record< custom: nullPassThrough(JSON.stringify), requireSupported: x => x, safety: x => x, + protocolNetwork: resolveChainAlias, } const INDEXING_RULE_CONVERTERS_FROM_GRAPHQL: Record< @@ -80,6 +84,7 @@ const INDEXING_RULE_CONVERTERS_FROM_GRAPHQL: Record< custom: nullPassThrough(JSON.stringify), requireSupported: x => x, safety: x => x, + protocolNetwork: x => x, } const INDEXING_RULE_CONVERTERS_TO_GRAPHQL: Record< @@ -103,6 +108,7 @@ const INDEXING_RULE_CONVERTERS_TO_GRAPHQL: Record< custom: nullPassThrough(JSON.stringify), requireSupported: x => x, safety: x => x, + protocolNetwork: x => x, } /** @@ -238,13 +244,15 @@ export const displayRules = ( export const indexingRules = async ( client: IndexerManagementClient, merged: boolean, + protocolNetwork?: string, ): Promise[]> => { const result = await client .query( gql` - query indexingRules($merged: Boolean!) { - indexingRules(merged: $merged) { + query indexingRules($merged: Boolean!, $protocolNetwork: String) { + indexingRules(merged: $merged, protocolNetwork: $protocolNetwork) { identifier + protocolNetwork identifierType allocationAmount allocationLifetime @@ -262,7 +270,7 @@ export const indexingRules = async ( } } `, - { merged: !!merged }, + { merged: !!merged, protocolNetwork }, ) .toPromise() @@ -275,13 +283,13 @@ export const indexingRules = async ( export const indexingRule = async ( client: IndexerManagementClient, - identifier: string, + identifier: IndexingRuleIdentifier, merged: boolean, ): Promise | null> => { const result = await client .query( gql` - query indexingRule($identifier: String!, $merged: Boolean!) { + query indexingRule($identifier: IndexingRuleIdentifier!, $merged: Boolean!) { indexingRule(identifier: $identifier, merged: $merged) { identifier identifierType @@ -298,6 +306,7 @@ export const indexingRule = async ( decisionBasis requireSupported safety + protocolNetwork } } `, @@ -340,6 +349,7 @@ export const setIndexingRule = async ( decisionBasis requireSupported safety + protocolNetwork } } `, @@ -356,16 +366,16 @@ export const setIndexingRule = async ( export const deleteIndexingRules = async ( client: IndexerManagementClient, - identifiers: string[], + deployments: IndexingRuleIdentifier[], ): Promise => { const result = await client .mutation( gql` - mutation deleteIndexingRules($deployments: [String!]!) { + mutation deleteIndexingRules($deployments: [IndexingRuleIdentifier!]!) { deleteIndexingRules(identifiers: $deployments) } `, - { deployments: identifiers.map(identifier => identifier.toString()) }, + { deployments }, ) .toPromise() diff --git a/packages/indexer-common/CHANGELOG.md b/packages/indexer-common/CHANGELOG.md index 847e80d11..aa29b2baa 100644 --- a/packages/indexer-common/CHANGELOG.md +++ b/packages/indexer-common/CHANGELOG.md @@ -6,6 +6,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.20.23] - 2023-09-29 +### Fixed +- Reference SQL conflict targets when upserting `POIDisputes`. + +## [0.20.21] - 2023-08-24 +### Changed +- Upgraded `common-ts` dependency to v2.0.3 + +## [0.20.20] - 2023-08-21 +### Fixed +- Ensure some database operations run inside their transactions +- More helpful error message when parsing network specification files +- Protocol network missing when creating offchain rules and allocations via the GraphQL interface + +## [0.20.18] - 2023-08-11 +### Added +- The `Network` type now holds references to all network-specific components, such as the `NetworkMonitor` and `Contracts` classes. +- Introduced `GraphNode` class to replace `Indexer`, `Subgraph`, and `IndexingStatus` classes. +- New general purpose `parser` module, used for input validation. +- New `NetworkSpecification` type, which holds all information required to represent a protocol network. + +### Changed +- Added `protocolNetwork` field to most types. + +## [0.20.17] - 2023-06-19 +### Changed +- Check action batch feasibility at batch level only, improve batche efficiency +- Improved logging detail when queueing allocation receipts for collecting +- Update network alias value, polygon -> matic +- Update partial-vouchers encoding scheme + ## [0.20.12] - 2023-02-19 ### Added - New `ReceiptMetrics` metric for allocation receipt collector @@ -247,7 +278,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Move indexing rule management here from `@graphprotocol/common-ts` -[Unreleased]: https://github.com/graphprotocol/indexer/compare/v0.20.12...HEAD +[Unreleased]: https://github.com/graphprotocol/indexer/compare/v0.20.23-rc.0...HEAD +[0.20.23]: https://github.com/graphprotocol/indexer/compare/v0.20.21...v0.20.23 +[0.20.21]: https://github.com/graphprotocol/indexer/compare/v0.20.20...v0.20.21 +[0.20.20]: https://github.com/graphprotocol/indexer/compare/v0.20.18...v0.20.20 +[0.20.18]: https://github.com/graphprotocol/indexer/compare/v0.20.17...v0.20.18 +[0.20.17]: https://github.com/graphprotocol/indexer/compare/v0.20.12...v0.20.17 [0.20.12]: https://github.com/graphprotocol/indexer/compare/v0.20.11...v0.20.12 [0.20.11]: https://github.com/graphprotocol/indexer/compare/v0.20.10...v0.20.11 [0.20.10]: https://github.com/graphprotocol/indexer/compare/v0.20.9...v0.20.10 diff --git a/packages/indexer-common/jest.config.js b/packages/indexer-common/jest.config.js index 76c857d61..489c309cc 100644 --- a/packages/indexer-common/jest.config.js +++ b/packages/indexer-common/jest.config.js @@ -5,8 +5,9 @@ const bail = (s) => { module.exports = { collectCoverage: true, preset: 'ts-jest', + forceExit: true, testEnvironment: 'node', - testPathIgnorePatterns: ['/node_modules/', '/dist/', '/.yalc'], + testPathIgnorePatterns: ['/node_modules/', '/dist/', '/.yalc', 'util.ts'], globals: { __DATABASE__: { host: process.env.POSTGRES_TEST_HOST || bail('POSTGRES_TEST_HOST is not defined'), @@ -21,6 +22,6 @@ module.exports = { process.env.POSTGRES_TEST_DATABASE || bail('POSTGRES_TEST_DATABASE is not defined'), }, - __LOG_LEVEL__: process.env.LOG_LEVEL || 'info' + __LOG_LEVEL__: process.env.LOG_LEVEL || 'info', }, } diff --git a/packages/indexer-common/package.json b/packages/indexer-common/package.json index 02299ad4a..ce2d9ea5c 100644 --- a/packages/indexer-common/package.json +++ b/packages/indexer-common/package.json @@ -1,6 +1,6 @@ { "name": "@graphprotocol/indexer-common", - "version": "0.20.14", + "version": "0.20.23", "description": "Common library for Graph Protocol indexer components", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -15,28 +15,35 @@ "lint": "eslint . --ext .ts,.tsx --fix", "compile": "tsc", "prepare": "yarn format && yarn lint && yarn compile", - "test": "LOG_LEVEL=info jest --colors --verbose --forceExit --runInBand", - "test:ci": "LOG_LEVEL=error jest --verbose --runInBand --forceExit --ci", - "test:debug": "LOG_LEVEL=debug jest --runInBand --detectOpenHandles --verbose --forceExit", + "test": "LOG_LEVEL=info jest --colors --verbose --runInBand --detectOpenHandles", + "test:ci": "LOG_LEVEL=info jest --verbose --maxWorkers=1 --ci", + "test:debug": "LOG_LEVEL=debug jest --runInBand --detectOpenHandles --verbose", "test:watch": "jest --runInBand --detectOpenHandles --watch --passWithNoTests --verbose", "clean": "rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo" }, "dependencies": { - "@graphprotocol/common-ts": "2.0.1", + "@graphprotocol/common-ts": "2.0.7", "@graphprotocol/cost-model": "0.1.16", "@thi.ng/heaps": "1.2.38", + "@types/lodash.clonedeep": "^4.5.7", + "@types/lodash.intersection": "^4.4.7", + "@types/lodash.xor": "^4.5.7", "@urql/core": "2.4.4", "@urql/exchange-execute": "1.2.2", "axios": "0.26.1", - "body-parser": "1.19.1", + "body-parser": "1.20.2", "cors": "2.8.5", "ethers": "5.7.0", "evt": "1.10.1", - "express": "4.17.3", + "express": "4.18.2", "fastify": "3.25.0", - "graphql": "16.3.0", + "graphql": "16.8.0", "graphql-tag": "2.12.6", "jayson": "3.6.6", + "lodash.clonedeep": "^4.5.0", + "lodash.groupby": "^4.6.0", + "lodash.isequal": "4.5.0", + "lodash.xor": "^4.5.0", "morgan": "1.10.0", "ngeohash": "0.6.3", "p-filter": "2.1.0", @@ -44,28 +51,33 @@ "p-reduce": "2.1.0", "p-retry": "4.6.1", "p-timeout": "4.1.0", - "sequelize": "6.19.0", - "ts-custom-error": "^3.2.0" + "parsimmon": "^1.18.1", + "sequelize": "6.33.0", + "ts-custom-error": "^3.2.0", + "zod": "^3.21.4" }, "devDependencies": { - "@types/cors": "2.8.12", - "@types/express": "4.17.13", - "@types/jest": "27.4.1", - "@types/morgan": "1.9.2", + "@types/cors": "2.8.14", + "@types/express": "4.17.17", + "@types/jest": "29.5.4", + "@types/lodash.groupby": "^4.6.7", + "@types/lodash.isequal": "4.5.6", + "@types/morgan": "1.9.5", "@types/ngeohash": "0.6.4", - "@types/node": "17.0.23", - "@typescript-eslint/eslint-plugin": "5.22.0", - "@typescript-eslint/parser": "5.22.0", - "eslint": "8.14.0", + "@types/node": "20.6.1", + "@types/parsimmon": "^1.10.6", + "@typescript-eslint/eslint-plugin": "6.7.0", + "@typescript-eslint/parser": "6.7.0", + "eslint": "8.49.0", "eslint-config-prettier": "8.5.0", - "jest": "27.5.1", - "prettier": "2.6.2", - "ts-jest": "27.1.4", - "typescript": "4.6.3" + "jest": "<30.0.0-0", + "prettier": "3.0.3", + "ts-jest": "29.1.1", + "typescript": "5.2.2" }, "resolutions": { "ethers": "5.7.0", - "sequelize": "6.19.0", + "sequelize": "6.33.0", "@ethersproject/bignumber": "5.7.0", "@urql/exchange-execute/@urql/core": "2.4.4" }, diff --git a/packages/indexer-common/src/__tests__/network-specification-files/invalid-address.yml b/packages/indexer-common/src/__tests__/network-specification-files/invalid-address.yml new file mode 100644 index 000000000..819c8fd32 --- /dev/null +++ b/packages/indexer-common/src/__tests__/network-specification-files/invalid-address.yml @@ -0,0 +1,34 @@ +networkIdentifier: mainnet +gateway: + url: http://gateway +indexerOptions: + address: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" + mnemonic: word ivory whale diesel slab pelican voyage oxygen chat find tobacco sport + url: http://indexer + geoCoordinates: [25.1, -71.2] + restakeRewards: true + rebateClaimThreshold: 400 + rebateClaimBatchThreshold: 5000 + rebateClaimMaxBatchSize: 10 + poiDisputeMonitoring: false + poiDisputableEpochs: 5 + defaultAllocationAmount: 0.05 + voucherRedemptionThreshold: 2 + voucherRedemptionBatchThreshold: 2000 + voucherRedemptionMaxBatchSize: 15 + allocationManagementMode: "auto" + autoAllocationMinBatchSize: 20 +transactionMonitoring: + gasIncreaseTimeout: 10 + gasIncreaseFactor: 10 + baseFeePerGasMax: 10 + maxTransactionAttempts: 10 +subgraphs: + networkSubgraph: + deployment: QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB + epochSubgraph: + url: http://subgraph +networkProvider: + url: http://provider +dai: + contractAddress: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448 " diff --git a/packages/indexer-common/src/__tests__/network-specification-files/invalid-base58.yml b/packages/indexer-common/src/__tests__/network-specification-files/invalid-base58.yml new file mode 100644 index 000000000..9252faabb --- /dev/null +++ b/packages/indexer-common/src/__tests__/network-specification-files/invalid-base58.yml @@ -0,0 +1,34 @@ +networkIdentifier: mainnet +gateway: + url: http://gateway +indexerOptions: + address: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" + mnemonic: word ivory whale diesel slab pelican voyage oxygen chat find tobacco sport + url: http://indexer + geoCoordinates: [25.1, -71.2] + restakeRewards: true + rebateClaimThreshold: 400 + rebateClaimBatchThreshold: 5000 + rebateClaimMaxBatchSize: 10 + poiDisputeMonitoring: false + poiDisputableEpochs: 5 + defaultAllocationAmount: 0.05 + voucherRedemptionThreshold: 2 + voucherRedemptionBatchThreshold: 2000 + voucherRedemptionMaxBatchSize: 15 + allocationManagementMode: "auto" + autoAllocationMinBatchSize: 20 +transactionMonitoring: + gasIncreaseTimeout: 10 + gasIncreaseFactor: 10 + baseFeePerGasMax: 10 + maxTransactionAttempts: 10 +subgraphs: + networkSubgraph: + deployment: abcdefg # <-- invalid base58 field + epochSubgraph: + url: http://subgraph +networkProvider: + url: http://provider +dai: + contractAddress: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" diff --git a/packages/indexer-common/src/__tests__/network-specification-files/invalid-epoch-subgraph.yml b/packages/indexer-common/src/__tests__/network-specification-files/invalid-epoch-subgraph.yml new file mode 100644 index 000000000..8120d9563 --- /dev/null +++ b/packages/indexer-common/src/__tests__/network-specification-files/invalid-epoch-subgraph.yml @@ -0,0 +1,35 @@ +networkIdentifier: mainnet +gateway: + url: http://gateway +indexerOptions: + address: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" + mnemonic: word ivory whale diesel slab pelican voyage oxygen chat find tobacco sport + url: http://indexer + geoCoordinates: [25.1, -71.2] + restakeRewards: true + rebateClaimThreshold: 400 + rebateClaimBatchThreshold: 5000 + rebateClaimMaxBatchSize: 10 + poiDisputeMonitoring: false + poiDisputableEpochs: 5 + defaultAllocationAmount: 0.05 + voucherRedemptionThreshold: 2 + voucherRedemptionBatchThreshold: 2000 + voucherRedemptionMaxBatchSize: 15 + allocationManagementMode: "auto" + autoAllocationMinBatchSize: 20 +transactionMonitoring: + gasIncreaseTimeout: 10 + gasIncreaseFactor: 10 + baseFeePerGasMax: 10 + maxTransactionAttempts: 10 +subgraphs: + networkSubgraph: + url: http://subgraph + epochSubgraph: + deployment: QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB + # <-- Missing `url` field +networkProvider: + url: http://provider +dai: + contractAddress: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" diff --git a/packages/indexer-common/src/__tests__/network-specification-files/invalid-extra-field.yml b/packages/indexer-common/src/__tests__/network-specification-files/invalid-extra-field.yml new file mode 100644 index 000000000..cbdf0ae1d --- /dev/null +++ b/packages/indexer-common/src/__tests__/network-specification-files/invalid-extra-field.yml @@ -0,0 +1,36 @@ +networkIdentifier: mainnet +gateway: + url: http://gateway +indexerOptions: + address: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" + mnemonic: word ivory whale diesel slab pelican voyage oxygen chat find tobacco sport + url: http://indexer + geoCoordinates: [25.1, -71.2] + restakeRewards: true + rebateClaimThreshold: 400 + rebateClaimBatchThreshold: 5000 + rebateClaimMaxBatchSize: 10 + poiDisputeMonitoring: false + poiDisputableEpochs: 5 + defaultAllocationAmount: 0.05 + voucherRedemptionThreshold: 2 + voucherRedemptionBatchThreshold: 2000 + voucherRedemptionMaxBatchSize: 15 + allocationManagementMode: "auto" + autoAllocationMinBatchSize: 20 + invalidExtraField: abcd # <-- invalid extra field +transactionMonitoring: + gasIncreaseTimeout: 10 + gasIncreaseFactor: 10 + baseFeePerGasMax: 10 + maxTransactionAttempts: 10 +subgraphs: + networkSubgraph: + url: http://subgraph + deployment: QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB + epochSubgraph: + url: http://subgraph +networkProvider: + url: http://provider +dai: + contractAddress: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" diff --git a/packages/indexer-common/src/__tests__/network-specification-files/invalid-missing-field.yml b/packages/indexer-common/src/__tests__/network-specification-files/invalid-missing-field.yml new file mode 100644 index 000000000..435558a5c --- /dev/null +++ b/packages/indexer-common/src/__tests__/network-specification-files/invalid-missing-field.yml @@ -0,0 +1,35 @@ +networkIdentifier: mainnet +gateway: + url: http://gateway +indexerOptions: + # missing indexer address field + mnemonic: word ivory whale diesel slab pelican voyage oxygen chat find tobacco sport + url: http://indexer + geoCoordinates: [25.1, -71.2] + restakeRewards: true + rebateClaimThreshold: 400 + rebateClaimBatchThreshold: 5000 + rebateClaimMaxBatchSize: 10 + poiDisputeMonitoring: false + poiDisputableEpochs: 5 + defaultAllocationAmount: 0.05 + voucherRedemptionThreshold: 2 + voucherRedemptionBatchThreshold: 2000 + voucherRedemptionMaxBatchSize: 15 + allocationManagementMode: "auto" + autoAllocationMinBatchSize: 20 +transactionMonitoring: + gasIncreaseTimeout: 10 + gasIncreaseFactor: 10 + baseFeePerGasMax: 10 + maxTransactionAttempts: 10 +subgraphs: + networkSubgraph: + url: http://subgraph + deployment: QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB + epochSubgraph: + url: http://subgraph +networkProvider: + url: http://provider +dai: + contractAddress: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" diff --git a/packages/indexer-common/src/__tests__/network-specification-files/invalid-negative-max-block-distance.yml b/packages/indexer-common/src/__tests__/network-specification-files/invalid-negative-max-block-distance.yml new file mode 100644 index 000000000..7cc09d111 --- /dev/null +++ b/packages/indexer-common/src/__tests__/network-specification-files/invalid-negative-max-block-distance.yml @@ -0,0 +1,35 @@ +networkIdentifier: mainnet +gateway: + url: http://gateway +indexerOptions: + address: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" + mnemonic: word ivory whale diesel slab pelican voyage oxygen chat find tobacco sport + url: http://indexer + geoCoordinates: [25.1, -71.2] + restakeRewards: true + rebateClaimThreshold: 400 + rebateClaimBatchThreshold: 5000 + rebateClaimMaxBatchSize: 10 + poiDisputeMonitoring: false + poiDisputableEpochs: 5 + defaultAllocationAmount: 0.05 + voucherRedemptionThreshold: 2 + voucherRedemptionBatchThreshold: 2000 + voucherRedemptionMaxBatchSize: 15 + allocationManagementMode: "auto" + autoAllocationMinBatchSize: 20 +transactionMonitoring: + gasIncreaseTimeout: 10 + gasIncreaseFactor: 10 + baseFeePerGasMax: 10 + maxTransactionAttempts: 10 +subgraphs: + maxBlockDistance: -10 + networkSubgraph: + deployment: QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB + epochSubgraph: + url: http://subgraph +networkProvider: + url: http://provider +dai: + contractAddress: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" diff --git a/packages/indexer-common/src/__tests__/network-specification-files/invalid-network-identifier.yml b/packages/indexer-common/src/__tests__/network-specification-files/invalid-network-identifier.yml new file mode 100644 index 000000000..74eca8c09 --- /dev/null +++ b/packages/indexer-common/src/__tests__/network-specification-files/invalid-network-identifier.yml @@ -0,0 +1,35 @@ +networkIdentifier: invalid # <-- invalid network identifier +gateway: + url: http://gateway +indexerOptions: + address: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" + mnemonic: word ivory whale diesel slab pelican voyage oxygen chat find tobacco sport + url: http://indexer + geoCoordinates: [25.1, -71.2] + restakeRewards: true + rebateClaimThreshold: 400 + rebateClaimBatchThreshold: 5000 + rebateClaimMaxBatchSize: 10 + poiDisputeMonitoring: false + poiDisputableEpochs: 5 + defaultAllocationAmount: 0.05 + voucherRedemptionThreshold: 2 + voucherRedemptionBatchThreshold: 2000 + voucherRedemptionMaxBatchSize: 15 + allocationManagementMode: "auto" + autoAllocationMinBatchSize: 20 +transactionMonitoring: + gasIncreaseTimeout: 10 + gasIncreaseFactor: 10 + baseFeePerGasMax: 10 + maxTransactionAttempts: 10 +subgraphs: + networkSubgraph: + url: http://subgraph + deployment: QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB + epochSubgraph: + url: http://subgraph +networkProvider: + url: http://provider +dai: + contractAddress: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" diff --git a/packages/indexer-common/src/__tests__/network-specification-files/valid-missing.yml b/packages/indexer-common/src/__tests__/network-specification-files/valid-missing.yml new file mode 100644 index 000000000..6092351c3 --- /dev/null +++ b/packages/indexer-common/src/__tests__/network-specification-files/valid-missing.yml @@ -0,0 +1,30 @@ +# this file is missing the whole `transactionMonitoring` entry, and is still valid +networkIdentifier: mainnet +gateway: + url: http://gateway +indexerOptions: + address: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" + mnemonic: word ivory whale diesel slab pelican voyage oxygen chat find tobacco sport + url: http://indexer + geoCoordinates: [25.1, -71.2] + restakeRewards: true + rebateClaimThreshold: 400 + rebateClaimBatchThreshold: 5000 + rebateClaimMaxBatchSize: 10 + poiDisputeMonitoring: false + poiDisputableEpochs: 5 + defaultAllocationAmount: 0.05 + voucherRedemptionThreshold: 2 + voucherRedemptionBatchThreshold: 2000 + voucherRedemptionMaxBatchSize: 15 + allocationManagementMode: "auto" + autoAllocationMinBatchSize: 20 +subgraphs: + networkSubgraph: + deployment: QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB + epochSubgraph: + url: http://subgraph +networkProvider: + url: http://provider +dai: + contractAddress: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" diff --git a/packages/indexer-common/src/__tests__/network-specification-files/valid.yml b/packages/indexer-common/src/__tests__/network-specification-files/valid.yml new file mode 100644 index 000000000..756ac2f4e --- /dev/null +++ b/packages/indexer-common/src/__tests__/network-specification-files/valid.yml @@ -0,0 +1,34 @@ +networkIdentifier: mainnet +gateway: + url: http://gateway +indexerOptions: + address: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" + mnemonic: word ivory whale diesel slab pelican voyage oxygen chat find tobacco sport + url: http://indexer + geoCoordinates: [25.1, -71.2] + restakeRewards: true + rebateClaimThreshold: 400 + rebateClaimBatchThreshold: 5000 + rebateClaimMaxBatchSize: 10 + poiDisputeMonitoring: false + poiDisputableEpochs: 5 + defaultAllocationAmount: 0.05 + voucherRedemptionThreshold: 2 + voucherRedemptionBatchThreshold: 2000 + voucherRedemptionMaxBatchSize: 15 + allocationManagementMode: "auto" + autoAllocationMinBatchSize: 20 +transactionMonitoring: + gasIncreaseTimeout: 10 + gasIncreaseFactor: 10 + baseFeePerGasMax: 10 + maxTransactionAttempts: 10 +subgraphs: + networkSubgraph: + deployment: QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB + epochSubgraph: + url: http://subgraph +networkProvider: + url: http://provider +dai: + contractAddress: "0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448" diff --git a/packages/indexer-common/src/__tests__/network-specification.test.ts b/packages/indexer-common/src/__tests__/network-specification.test.ts new file mode 100644 index 000000000..303c38c0d --- /dev/null +++ b/packages/indexer-common/src/__tests__/network-specification.test.ts @@ -0,0 +1,106 @@ +import * as fs from 'fs' +import * as path from 'path' +import * as YAML from 'yaml' +import { + NetworkSpecification, + IndexerOptions, + TransactionMonitoring, +} from '../network-specification' + +function readYamlFile(p: string): string { + const filePath = path.join(__dirname, 'network-specification-files', p) + const text = fs.readFileSync(filePath, 'utf8') + return YAML.parse(text) +} + +describe('Network Specification deserialization', () => { + describe('Successful deserialization', () => { + test('Valid specification file', () => { + const validFile = readYamlFile('valid.yml') + NetworkSpecification.parse(validFile) + }) + }) + + describe('Successful deserialization with missing defaults', () => { + test('Valid specification file', () => { + const validFile = readYamlFile('valid-missing.yml') + const parsed = NetworkSpecification.parse(validFile) + const expectedDefaults = TransactionMonitoring.parse({}) + expect(expectedDefaults).not.toEqual({}) // Ensures default is not an empty object + expect(parsed.transactionMonitoring).toStrictEqual(expectedDefaults) + }) + }) +}) + +interface FailedDeserializationTest { + file: string + path: string[] + message: string +} + +describe('Failed deserialization', () => { + const failedTests: FailedDeserializationTest[] = [ + { + file: 'invalid-epoch-subgraph.yml', + path: ['subgraphs', 'epochSubgraph', 'url'], + message: 'Epoch Subgraph endpoint must be defined', + }, + { + file: 'invalid-missing-field.yml', + path: ['indexerOptions', 'address'], + message: 'Required', + }, + { + file: 'invalid-extra-field.yml', + path: ['indexerOptions'], + message: "Unrecognized key(s) in object: 'invalidExtraField'", + }, + { + file: 'invalid-network-identifier.yml', + path: ['networkIdentifier'], + message: 'Invalid network identifier', + }, + { + file: 'invalid-base58.yml', + path: ['subgraphs', 'networkSubgraph', 'deployment'], + message: 'Invalid IPFS hash', + }, + { + file: 'invalid-address.yml', + path: ['dai', 'contractAddress'], + message: 'Invalid contract address', + }, + { + file: 'invalid-negative-max-block-distance.yml', + path: ['subgraphs', 'maxBlockDistance'], + message: 'Number must be greater than or equal to 0', + }, + ] + + test.each(failedTests)( + 'Validation should fail for $file', + (t: FailedDeserializationTest) => { + const invalidFile = readYamlFile(t.file) + const result = NetworkSpecification.safeParse(invalidFile) + expect(result.success).toBe(false) + if (result.success === false) { + const issue = result.error.issues[0] + expect(issue.path).toStrictEqual(t.path) + expect(issue.message).toStrictEqual(t.message) + } else { + fail('This deserialization test should have failed') + } + }, + ) +}) + +describe('Specificaiton parts parsing', () => { + test('Valid Indexer Options should parse successfully', () => { + IndexerOptions.parse({ + address: '0xdf9CAc44924C21a6c874ee3C727b1c9Ccd5b58cc', + mnemonic: 'any valid string can work', + url: 'http://example.com', + geoCoordinates: [60.16952, 24.93545], // Must be numbers + }) + }) +}) diff --git a/packages/indexer-common/src/__tests__/subgraph.test.ts b/packages/indexer-common/src/__tests__/subgraph.test.ts new file mode 100644 index 000000000..fb85fcb19 --- /dev/null +++ b/packages/indexer-common/src/__tests__/subgraph.test.ts @@ -0,0 +1,258 @@ +import { DocumentNode, print } from 'graphql' +import { + SubgraphFreshnessChecker, + LoggerInterface, + ProviderInterface, + SubgraphQueryInterface, +} from '../subgraphs' +import { QueryResult } from '../network-subgraph' +import gql from 'graphql-tag' +import { mergeSelectionSets } from '../utils' + +/* eslint-disable @typescript-eslint/no-explicit-any */ +export const mockProvider: ProviderInterface & any = { + getBlockNumber: jest.fn(), +} + +export const mockLogger: LoggerInterface & any = { + trace: jest.fn(), + error: jest.fn(), + warn: jest.fn(), +} + +const mockSubgraph: SubgraphQueryInterface & any = { + query: jest.fn(), +} + +const testSubgraphQuery: DocumentNode = gql` + query TestQuery { + foo { + id + } + } +` + +function mockQueryResult(blockNumber: number): QueryResult & { + data: { _meta: { block: { number: number } } } +} { + return { + data: { + foo: { + id: 1, + }, + _meta: { + block: { + number: blockNumber, + }, + }, + }, + } +} +/* eslint-enable @typescript-eslint/no-explicit-any */ + +const blockNumberQuery = gql` + { + _meta { + block { + number + } + } + } +` + +describe('mergeSelectionSets function tests', () => { + it('can merge two GraphQL queries', () => { + const firstQuery = gql` + query Foo { + graphNetworks(first: 5) { + id + controller + graphToken + epochManager + } + graphAccounts(first: 5) { + id + names { + id + } + defaultName { + id + } + createdAt + } + } + ` + const expected = gql` + query Foo { + graphNetworks(first: 5) { + id + controller + graphToken + epochManager + } + graphAccounts(first: 5) { + id + names { + id + } + defaultName { + id + } + createdAt + } + _meta { + block { + number + } + } + } + ` + const result = mergeSelectionSets(firstQuery, blockNumberQuery) + expect(result.definitions).toStrictEqual(expected.definitions) + expect(print(result)).toEqual(print(expected)) + }) + + it("doesn't mutate its input", () => { + const expectedMergedQuery = gql` + query TestQuery { + foo { + id + } + _meta { + block { + number + } + } + } + ` + let result: DocumentNode + // Repetition required to test `mergeSelectionSets` doesn't mutate its input + for (let i = 0; i < 3; i++) { + result = mergeSelectionSets(testSubgraphQuery, blockNumberQuery) + } + expect(result!.definitions).toStrictEqual(expectedMergedQuery.definitions) + expect(print(result!)).toEqual(print(expectedMergedQuery)) + }) +}) + +describe('SubgraphFreshnessChecker', () => { + beforeEach(jest.resetAllMocks) + + describe('checkedQuery method', () => { + beforeEach(jest.resetAllMocks) + + it('should throw an error if max retries reached', async () => { + const checker = new SubgraphFreshnessChecker( + 'Test Subgraph', + mockProvider, + 10, + 10, + mockLogger, + 1, + ) + + // Mocks never change value in this test, so the network will always be 100 blocks ahead and + // the checked query will timeout.f + mockProvider.getBlockNumber.mockResolvedValue(242) + mockSubgraph.query.mockResolvedValue(mockQueryResult(100)) + + await expect(checker.checkedQuery(mockSubgraph, testSubgraphQuery)).rejects.toThrow( + 'Max retries reached for Test Subgraph freshness check', + ) + + expect(mockLogger.trace).toHaveBeenCalledWith( + expect.stringContaining('Performing subgraph freshness check'), + { + blockDistance: 142, + freshnessThreshold: 10, + latestIndexedBlock: 100, + latestNetworkBlock: 242, + retriesLeft: 1, + subgraph: 'Test Subgraph', + }, + ) + }) + + it('should return query result if the subgraph is fresh', async () => { + const checker = new SubgraphFreshnessChecker( + 'Test Subgraph', + mockProvider, + 10, + 10, + mockLogger, + 1, + ) + + mockProvider.getBlockNumber.mockResolvedValue(105) + mockSubgraph.query.mockResolvedValue(mockQueryResult(100)) + + await expect( + checker.checkedQuery(mockSubgraph, testSubgraphQuery), + ).resolves.toEqual(mockQueryResult(100)) + + expect(mockLogger.trace).toHaveBeenCalledWith( + expect.stringContaining('Performing subgraph freshness check'), + { + blockDistance: 5, + freshnessThreshold: 10, + latestIndexedBlock: 100, + latestNetworkBlock: 105, + retriesLeft: 1, + subgraph: 'Test Subgraph', + }, + ) + }) + + it('should return query result if the subgraph becomes fresh after retries', async () => { + const checker = new SubgraphFreshnessChecker( + 'Test Subgraph', + mockProvider, + 10, + 100, + mockLogger, + 2, + ) + + // Advance the network by ten blocks between calls + mockProvider.getBlockNumber.mockResolvedValueOnce(150).mockResolvedValueOnce(160) + + // Advance the subgraph by 20 blocks between calls + // The first call should trigger a retry, which then shuld succeed + mockSubgraph.query + .mockResolvedValueOnce(mockQueryResult(130)) + .mockResolvedValueOnce(mockQueryResult(150)) + + const result = await checker.checkedQuery(mockSubgraph, testSubgraphQuery) + expect(result).toEqual(mockQueryResult(150)) + + // It should log this on retry + expect(mockLogger.warn).toHaveBeenCalledWith( + expect.stringContaining( + 'Test Subgraph is not fresh. Sleeping for 100 ms before retrying', + ), + { + blockDistance: 20, + freshnessThreshold: 10, + latestIndexedBlock: 130, + latestNetworkBlock: 150, + retriesLeft: 2, + subgraph: 'Test Subgraph', + }, + ) + // It should log this on success + expect(mockLogger.trace.mock.calls).toContainEqual( + expect.objectContaining([ + 'Test Subgraph is fresh', + { + blockDistance: 10, + freshnessThreshold: 10, + latestIndexedBlock: 150, + latestNetworkBlock: 160, + retriesLeft: 1, + subgraph: 'Test Subgraph', + }, + ]), + ) + }) + }) +}) diff --git a/packages/indexer-common/src/__tests__/subgraphs.ts b/packages/indexer-common/src/__tests__/subgraphs.ts deleted file mode 100644 index 5dcc9de1d..000000000 --- a/packages/indexer-common/src/__tests__/subgraphs.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { formatDeploymentName, cleanDeploymentName } from '../types' -import { BigNumber } from 'ethers' -import { SubgraphDeploymentID, toAddress } from '@graphprotocol/common-ts' - -describe('formatDeploymentName function tests', () => { - const creatorAddress = toAddress('0x6d2e03b7EfFEae98BD302A9F836D0d6Ab0002766') - const name = 'testSubgraphName' - const ipfsHash = 'Qmadj8x9km1YEyKmRnJ6EkC2zpJZFCfTyTZpuqC3j6e1QH' - const base = { - id: new SubgraphDeploymentID(ipfsHash), - deniedAt: 0, - stakedTokenns: BigNumber.from(0), - signalledTokens: BigNumber.from(0), - queryFeesAmount: BigNumber.from(0), - stakedTokens: BigNumber.from(0), - activeAllocations: 0, - } - - test('formatDeploymentName can handle existing subgraph and owner information', async () => { - const nameAndOwner = { - name, - creatorAddress, - ...base, - } - expect(formatDeploymentName(nameAndOwner)).toBe( - `${name}/${ipfsHash}/${creatorAddress}`, - ) - }) - - test('formatDeploymentName can handle missing owner name', async () => { - const noOwner = { - name, - ...base, - } - expect(formatDeploymentName(noOwner)).toBe(`${name}/${ipfsHash}/unknownCreator`) - }) - - test('formatDeploymentName can handle missing subgraph name', async () => { - const noName = { - creatorAddress, - ...base, - } - expect(formatDeploymentName(noName)).toBe( - `unknownSubgraph/${ipfsHash}/${creatorAddress}`, - ) - }) - - test('formatDeploymentName can handle missing subgraph and owner names', async () => { - expect(formatDeploymentName(base)).toBe(`unknownSubgraph/${ipfsHash}/unknownCreator`) - }) -}) - -describe('cleanDeploymentName function tests', () => { - test('can handle null input', () => { - expect(cleanDeploymentName(undefined)).toBe('unknownSubgraph') - }) - test('can remove invalid characters', () => { - expect(cleanDeploymentName('abc!@"#$%^&*()-def_123')).toBe('abc-def_123') - }) - test('can strip invalid charecters from start', () => { - expect(cleanDeploymentName('_abc')).toBe('abc') - expect(cleanDeploymentName('-abc')).toBe('abc') - }) - test('can strip invalid charecters from the end', () => { - expect(cleanDeploymentName('abc_')).toBe('abc') - expect(cleanDeploymentName('abc-')).toBe('abc') - }) - test('can strip invalid charecters from both ends', () => { - expect(cleanDeploymentName('_abc_')).toBe('abc') - expect(cleanDeploymentName('-abc-')).toBe('abc') - }) - test('can clean empty strings', () => { - expect(cleanDeploymentName('')).toBe('unknownSubgraph') - expect(cleanDeploymentName('--')).toBe('unknownSubgraph') - expect(cleanDeploymentName('_')).toBe('unknownSubgraph') - }) - test('can clean the special name "graphql"', () => { - expect(cleanDeploymentName('graphql')).toBe('graphql-subgraph') - expect(cleanDeploymentName('-graphql-')).toBe('graphql-subgraph') - expect(cleanDeploymentName('_graphql')).toBe('graphql-subgraph') - expect(cleanDeploymentName('graphql_')).toBe('graphql-subgraph') - }) - test('can chop the subgraph name to the adequate size', () => { - const reallyLongName = // 200 chars - 'y2feiw6y0eihrau5m5my0g0wvg6e2qbf79k91wzhcoep40hrend3re36jaejomss0goyaxx6yph5rrwieg3gkrvys699riza6kfak1tx9uy46onxt4fs3tp95e05v3xcf0jdldsz5ukqozsefo53wxl2m5rh5cdx8dkxq1fktr' - expect(cleanDeploymentName(reallyLongName)).toHaveLength(165) - }) -}) diff --git a/packages/indexer-common/src/actions.ts b/packages/indexer-common/src/actions.ts index 7a464a1e1..a6f920ab8 100644 --- a/packages/indexer-common/src/actions.ts +++ b/packages/indexer-common/src/actions.ts @@ -1,8 +1,9 @@ -import { ActionManager, NetworkMonitor } from './indexer-management' +import { NetworkMonitor } from './indexer-management' import { AllocationStatus } from './allocations' import { WhereOperators, WhereOptions } from 'sequelize' import { Op } from 'sequelize' import { WhereAttributeHashValue } from 'sequelize/types/model' +import { validateNetworkIdentifier } from './parsers' export interface ActionParamsInput { deploymentID?: string @@ -17,6 +18,7 @@ export interface ActionItem { type: ActionType reason: string status?: ActionStatus + protocolNetwork: string } export interface ActionUpdateInput { @@ -28,6 +30,7 @@ export interface ActionUpdateInput { type?: ActionType status?: ActionStatus reason?: string + protocolNetwork?: string } export interface ActionInput { @@ -41,6 +44,7 @@ export interface ActionInput { reason: string status: ActionStatus priority: number | undefined + protocolNetwork: string } export const isValidActionInput = ( @@ -76,12 +80,23 @@ export const isValidActionInput = ( export const validateActionInputs = async ( actions: ActionInput[], - actionManager: ActionManager, networkMonitor: NetworkMonitor, ): Promise => { // Validate actions before adding to queue // TODO: Perform all checks simultaneously and throw combined error if 1 or more fail for (const action of actions) { + // Must have a valid protocol network identifier + if (!action.protocolNetwork) { + throw Error("Cannot set an action without the field 'protocolNetwork'") + } + + try { + // Set the parsed network identifier back in the action input object + action.protocolNetwork = validateNetworkIdentifier(action.protocolNetwork) + } catch (e) { + throw Error(`Invalid value for the field 'protocolNetwork'. ${e}`) + } + // Must have the required params for the action type if (!isValidActionInput(action)) { throw new Error( @@ -143,6 +158,7 @@ export interface ActionFilter { source?: string reason?: string updatedAt?: WhereOperators + protocolNetwork?: string } export const actionFilterToWhereOptions = (filter: ActionFilter): WhereOptions => { @@ -173,6 +189,7 @@ export interface ActionResult { priority: number | undefined failureReason: string | null transaction: string | null + protocolNetwork: string } export enum ActionType { @@ -205,4 +222,5 @@ export enum ActionParams { PRIORITY = 'priority', CREATED_AT = 'createdAt', UPDATED_AT = 'updatedAt', + PROTOCOL_NETWORK = 'protocolNetwork', } diff --git a/packages/indexer-common/src/allocations/query-fees.test.ts b/packages/indexer-common/src/allocations/query-fees.test.ts index 9e5812363..d42342ea4 100644 --- a/packages/indexer-common/src/allocations/query-fees.test.ts +++ b/packages/indexer-common/src/allocations/query-fees.test.ts @@ -27,10 +27,53 @@ const TEST_DATA: Array = [ }, ] -test('encode a single partial voucher', () => { - encodePartialVouchers(TEST_DATA.slice(0, 1)).unwrap() -}) +const TEST_DATA_MULTIPLE_ALLOCATIONS_PARTIAL_VOUCHERS: Array = [ + { + allocation: '0x7bea8894b5ab5a36cdc2d8be9290046801dd5fed', + fees: '1208491688206053754', + receipt_id_max: '0x1321676bb44e606cda1779a8d92af9', + receipt_id_min: '0x01460d518a7b0b278dcd2bf882c11a', + signature: + '0x81140e47bc06819e133735bbd622213d50029d958115e4f905f18fcede2dce0f027669b2a5a6b0303d1692d7f2f01db65236f632f4c02450b102275c2cb816e11c', + }, + { + allocation: '0x6aea8894b5ab5a36cdc2d8be9290046801dd5fed', + fees: '1215330506986176264', + receipt_id_max: '0x1ea67d3d01eb7d6b25e33332e4f5fd', + receipt_id_min: '0x13ce97080acc5acf664404fdb9038f', + signature: + '0x62374ca18ad2713d149e1e90c3771d7fc4e5c643f7f063bacd497b7be2089c72404026ea675e189017c072850811422514a9781c663414f42689f697dc262ad21c', + }, + { + allocation: '0x6aea8894b5ab5a36cdc2d8be9290046801dd5fed', + fees: '1215454440881710511', + receipt_id_max: '0x2e8f582977cbacb24b4fc0d0d52436', + receipt_id_min: '0x217ae93ba26b959edfa33eeb08c0ba', + signature: + '0x657ddb7e0b09c8b0a5938df2d29d5d6d9412c5f153b4a409ca05cb6c62be1cf30016394462eaf2d1b86049f3ad71c0c2162bc97965a98a19a3bbf372106b82c01c', + }, +] + +describe('Encode partial vouchers', () => { + test('encode a single partial voucher', () => { + const partialVoucherData = TEST_DATA.slice(0, 1) + expect(encodePartialVouchers(partialVoucherData)).toEqual({ + allocation: partialVoucherData[0].allocation, + partialVouchers: partialVoucherData, + }) + }) + + test('encode multiple partial vouchers', () => { + expect(encodePartialVouchers(TEST_DATA)).toEqual({ + allocation: TEST_DATA[0].allocation, + partialVouchers: TEST_DATA, + }) + }) -test('encode multiple partial vouchers', () => { - encodePartialVouchers(TEST_DATA).unwrap() + test('fail to encode vouchers because they are from multiple allocations', () => { + const partialVoucherData = TEST_DATA_MULTIPLE_ALLOCATIONS_PARTIAL_VOUCHERS + expect(() => encodePartialVouchers(partialVoucherData)).toThrowError( + `Partial vouchers set must be for a single allocation, '2' unique allocations represented`, + ) + }) }) diff --git a/packages/indexer-common/src/allocations/query-fees.ts b/packages/indexer-common/src/allocations/query-fees.ts index c42dc3a58..22ed08e9d 100644 --- a/packages/indexer-common/src/allocations/query-fees.ts +++ b/packages/indexer-common/src/allocations/query-fees.ts @@ -18,7 +18,8 @@ import { Voucher, ensureAllocationSummary, TransactionManager, -} from '@graphprotocol/indexer-common' + specification as spec, +} from '..' import { DHeap } from '@thi.ng/heaps' import { BigNumber, BigNumberish, Contract } from 'ethers' import { Op } from 'sequelize' @@ -55,16 +56,18 @@ interface ReceiptMetrics { voucherCollectedFees: Gauge } +export interface AllocationPartialVouchers { + allocation: string + partialVouchers: PartialVoucher[] +} + export interface AllocationReceiptCollectorOptions { logger: Logger metrics: Metrics transactionManager: TransactionManager allocationExchange: Contract models: QueryFeeModels - collectEndpoint: string - voucherRedemptionThreshold: BigNumber - voucherRedemptionBatchThreshold: BigNumber - voucherRedemptionMaxBatchSize: number + networkSpecification: spec.NetworkSpecification } export interface ReceiptCollector { @@ -73,49 +76,64 @@ export interface ReceiptCollector { } export class AllocationReceiptCollector implements ReceiptCollector { - private logger: Logger - private metrics: ReceiptMetrics - private models: QueryFeeModels - private transactionManager: TransactionManager - private allocationExchange: Contract - private collectEndpoint: URL - private partialVoucherEndpoint: URL - private voucherEndpoint: URL - private receiptsToCollect!: DHeap - private voucherRedemptionThreshold: BigNumber - private voucherRedemptionBatchThreshold: BigNumber - private voucherRedemptionMaxBatchSize: number - - constructor({ + declare logger: Logger + declare metrics: ReceiptMetrics + declare models: QueryFeeModels + declare transactionManager: TransactionManager + declare allocationExchange: Contract + declare collectEndpoint: URL + declare partialVoucherEndpoint: URL + declare voucherEndpoint: URL + declare receiptsToCollect: DHeap + declare voucherRedemptionThreshold: BigNumber + declare voucherRedemptionBatchThreshold: BigNumber + declare voucherRedemptionMaxBatchSize: number + declare protocolNetwork: string + + // eslint-disable-next-line @typescript-eslint/no-empty-function -- Private constructor to prevent direct instantiation + private constructor() {} + + public static async create({ logger, metrics, transactionManager, models, - collectEndpoint, allocationExchange, - voucherRedemptionThreshold, - voucherRedemptionBatchThreshold, - voucherRedemptionMaxBatchSize, - }: AllocationReceiptCollectorOptions) { - this.logger = logger.child({ component: 'AllocationReceiptCollector' }) - this.metrics = registerReceiptMetrics(metrics) - this.transactionManager = transactionManager - this.models = models - this.collectEndpoint = new URL(collectEndpoint) - this.allocationExchange = allocationExchange - this.partialVoucherEndpoint = new URL( - collectEndpoint.replace('/collect-receipts', '/partial-voucher'), - ) - this.voucherEndpoint = new URL( - collectEndpoint.replace('/collect-receipts', '/voucher'), + networkSpecification, + }: AllocationReceiptCollectorOptions): Promise { + const collector = new AllocationReceiptCollector() + collector.logger = logger.child({ component: 'AllocationReceiptCollector' }) + collector.metrics = registerReceiptMetrics( + metrics, + networkSpecification.networkIdentifier, ) - - this.voucherRedemptionThreshold = voucherRedemptionThreshold - this.voucherRedemptionBatchThreshold = voucherRedemptionBatchThreshold - this.voucherRedemptionMaxBatchSize = voucherRedemptionMaxBatchSize - - this.startReceiptCollecting() - this.startVoucherProcessing() + collector.transactionManager = transactionManager + collector.models = models + collector.allocationExchange = allocationExchange + collector.protocolNetwork = networkSpecification.networkIdentifier + + // Process Gateway routes + const gatewayUrls = processGatewayRoutes(networkSpecification.gateway.url) + collector.collectEndpoint = gatewayUrls.collectReceipts + collector.voucherEndpoint = gatewayUrls.voucher + collector.partialVoucherEndpoint = gatewayUrls.partialVoucher + + const { + voucherRedemptionThreshold, + voucherRedemptionBatchThreshold, + voucherRedemptionMaxBatchSize, + } = networkSpecification.indexerOptions + collector.voucherRedemptionThreshold = voucherRedemptionThreshold + collector.voucherRedemptionBatchThreshold = voucherRedemptionBatchThreshold + collector.voucherRedemptionMaxBatchSize = voucherRedemptionMaxBatchSize + + // Start the AllocationReceiptCollector + // TODO: Consider calling methods conditionally based on a boolean + // flag during startup. + collector.startReceiptCollecting() + collector.startVoucherProcessing() + await collector.queuePendingReceiptsFromDatabase() + return collector } async rememberAllocations( @@ -138,8 +156,9 @@ export class AllocationReceiptCollector implements ReceiptCollector { this.models, allocation, transaction, + this.protocolNetwork, ) - await summary.save() + await summary.save({ transaction }) } }, ) @@ -160,7 +179,7 @@ export class AllocationReceiptCollector implements ReceiptCollector { }) try { - logger.debug(`Queue allocation receipts for collecting`) + logger.debug(`Queue allocation receipts for collecting`, { actionID, allocation }) const now = new Date() @@ -172,14 +191,17 @@ export class AllocationReceiptCollector implements ReceiptCollector { await this.models.allocationSummaries.update( { closedAt: now }, { - where: { allocation: allocation.id }, + where: { + allocation: allocation.id, + protocolNetwork: this.protocolNetwork, + }, transaction, }, ) // Return all receipts for the just-closed allocation return this.models.allocationReceipts.findAll({ - where: { allocation: allocation.id }, + where: { allocation: allocation.id, protocolNetwork: this.protocolNetwork }, order: ['id'], transaction, }) @@ -187,11 +209,11 @@ export class AllocationReceiptCollector implements ReceiptCollector { ) this.metrics.receiptsToCollect.set( - { allocation: receipts[0].allocation }, + { allocation: receipts[0]?.allocation }, receipts.length, ) if (receipts.length <= 0) { - logger.debug(`No receipts to collect for allocation`) + logger.debug(`No receipts to collect for allocation`, { actionID, allocation }) return false } @@ -205,6 +227,8 @@ export class AllocationReceiptCollector implements ReceiptCollector { logger.info(`Successfully queued allocation receipts for collecting`, { receipts: receipts.length, timeout: new Date(timeout).toLocaleString(), + actionID, + allocation, }) return true } catch (err) { @@ -212,6 +236,8 @@ export class AllocationReceiptCollector implements ReceiptCollector { this.metrics.failedReceipts.inc({ allocation: allocation.id }) this.logger.error(`Failed to queue allocation receipts for collecting`, { error, + actionID, + allocation, }) throw error } @@ -248,7 +274,13 @@ export class AllocationReceiptCollector implements ReceiptCollector { private startVoucherProcessing() { timer(30_000).pipe(async () => { - const pendingVouchers = await this.pendingVouchers() // Ordered by value + let pendingVouchers: Voucher[] = [] + try { + pendingVouchers = await this.pendingVouchers() // Ordered by value + } catch (err) { + this.logger.warn(`Failed to query pending vouchers`, { err }) + return + } const logger = this.logger.child({}) @@ -258,7 +290,10 @@ export class AllocationReceiptCollector implements ReceiptCollector { if (await this.allocationExchange.allocationsRedeemed(voucher.allocation)) { try { await this.models.vouchers.destroy({ - where: { allocation: voucher.allocation }, + where: { + allocation: voucher.allocation, + protocolNetwork: this.protocolNetwork, + }, }) logger.warn( `Query fee voucher for allocation already redeemed, deleted local voucher copy`, @@ -334,6 +369,7 @@ export class AllocationReceiptCollector implements ReceiptCollector { private async pendingVouchers(): Promise { return this.models.vouchers.findAll({ + where: { protocolNetwork: this.protocolNetwork }, order: [['amount', 'DESC']], // sorted by highest value to maximise the value of the batch limit: this.voucherRedemptionMaxBatchSize, // limit the number of vouchers to the max batch size }) @@ -360,6 +396,7 @@ export class AllocationReceiptCollector implements ReceiptCollector { const allocation = receipts[0].allocation const logger = this.logger.child({ allocation, + function: 'obtainReceiptsVoucher()', }) // Gross underestimated number of receipts the gateway take at once const receiptsThreshold = 25_000 @@ -381,6 +418,12 @@ export class AllocationReceiptCollector implements ReceiptCollector { { headers: { 'Content-Type': 'application/octet-stream' } }, ) } else { + logger.info( + `Too many receipts to collect in oneshot, collecting in batches of '${receiptsThreshold} receipts`, + { + receipts: receipts.length, + }, + ) // Split receipts in batches and collect partial vouchers const partialVouchers: Array = [] for (let i = 0; i < receipts.length; i += receiptsThreshold) { @@ -403,7 +446,6 @@ export class AllocationReceiptCollector implements ReceiptCollector { this.metrics.partialVouchersToExchange.set({ allocation }, partialVouchers.length) logger.debug(`Partial vouchers to exchange`, { partialVouchers: partialVouchers.length, - hexStringLength: partialVouchers[0].allocation, }) const encodedPartialVouchers = encodePartialVouchers(partialVouchers) @@ -411,20 +453,34 @@ export class AllocationReceiptCollector implements ReceiptCollector { // Exchange the partial vouchers for a voucher response = await axios.post( this.voucherEndpoint.toString(), - encodedPartialVouchers.unwrap().buffer, - { headers: { 'Content-Type': 'application/octet-stream' } }, + encodedPartialVouchers, + { + headers: { 'Content-Type': 'application/json' }, + }, ) } - const voucher = response.data as { + logger.trace('Gateway response', { + response, + allocation, + }) + + // Depending of which Gateway endpoint was used, fee information can come in different fields + const fees = response.data.fees ?? response.data.amount + if (!fees || !response.data.allocation || !response.data.signature) { + throw new Error('Failed to parse response from Gateay') + } + + const voucher = { ...response.data, fees } as { allocation: string - amount: string + fees: string signature: string } + this.metrics.vouchers.inc({ allocation, }) - this.metrics.voucherCollectedFees.set({ allocation }, parseFloat(voucher.amount)) + this.metrics.voucherCollectedFees.set({ allocation }, parseFloat(voucher.fees)) // Replace the receipts with the voucher in one db transaction; // should this fail, we'll try to collect these receipts again @@ -439,30 +495,38 @@ export class AllocationReceiptCollector implements ReceiptCollector { await this.models.allocationReceipts.destroy({ where: { id: receipts.map((receipt) => receipt.id), + protocolNetwork: this.protocolNetwork, }, transaction, }) - logger.debug(`Add voucher received in exchange for receipts to the database`) + logger.debug(`Add voucher received in exchange for receipts to the database`, { + voucher, + }) // Update the query fees tracked against the allocation const [summary] = await ensureAllocationSummary( this.models, toAddress(voucher.allocation), transaction, + this.protocolNetwork, ) summary.collectedFees = BigNumber.from(summary.collectedFees) - .add(voucher.amount) + .add(voucher.fees) .toString() await summary.save({ transaction }) // Add the voucher to the database await this.models.vouchers.findOrCreate({ - where: { allocation: toAddress(voucher.allocation) }, + where: { + allocation: toAddress(voucher.allocation), + protocolNetwork: this.protocolNetwork, + }, defaults: { allocation: toAddress(voucher.allocation), - amount: voucher.amount, + amount: voucher.fees, signature: voucher.signature, + protocolNetwork: this.protocolNetwork, }, transaction, }) @@ -478,11 +542,12 @@ export class AllocationReceiptCollector implements ReceiptCollector { private async submitVouchers(vouchers: Voucher[]): Promise { const logger = this.logger.child({ + function: 'submitVouchers()', voucherBatchSize: vouchers.length, }) logger.info(`Redeem query voucher batch on chain`, { - allocations: vouchers.map((voucher) => voucher.allocation), + vouchers, }) const stopTimer = this.metrics.vouchersRedeemDuration.startTimer({ allocation: vouchers[0].allocation, @@ -523,11 +588,12 @@ export class AllocationReceiptCollector implements ReceiptCollector { this.models, toAddress(voucher.allocation), transaction, + this.protocolNetwork, ) summary.withdrawnFees = BigNumber.from(summary.withdrawnFees) .add(voucher.amount) .toString() - await summary.save() + await summary.save({ transaction }) } }, ) @@ -544,7 +610,10 @@ export class AllocationReceiptCollector implements ReceiptCollector { logger.info(`Successfully redeemed query fee voucher, delete local copy`) try { await this.models.vouchers.destroy({ - where: { allocation: vouchers.map((voucher) => voucher.allocation) }, + where: { + allocation: vouchers.map((voucher) => voucher.allocation), + protocolNetwork: this.protocolNetwork, + }, }) this.metrics.successVoucherRedeems.inc({ allocation: vouchers[0].allocation }) logger.info(`Successfully deleted local voucher copy`) @@ -558,7 +627,7 @@ export class AllocationReceiptCollector implements ReceiptCollector { public async queuePendingReceiptsFromDatabase(): Promise { // Obtain all closed allocations const closedAllocations = await this.models.allocationSummaries.findAll({ - where: { closedAt: { [Op.not]: null } }, + where: { closedAt: { [Op.not]: null }, protocolNetwork: this.protocolNetwork }, }) // Create a receipts batch for each of these allocations @@ -576,6 +645,7 @@ export class AllocationReceiptCollector implements ReceiptCollector { const uncollectedReceipts = await this.models.allocationReceipts.findAll({ where: { allocation: closedAllocations.map((summary) => summary.allocation), + protocolNetwork: this.protocolNetwork, }, order: ['id'], }) @@ -606,104 +676,127 @@ export class AllocationReceiptCollector implements ReceiptCollector { } } -export function encodePartialVouchers(partialVouchers: PartialVoucher[]): BytesWriter { - // Take the partial vouchers and request for a full voucher - // [allocationId, partialVouchers[]] (in bytes) - // A voucher request needs allocation id which all partial vouchers shares, - // and a list of attributes (fees, signature, receipt id min, and receipt id max) - // from each partial voucher, all in form of 0x-prefixed hex string (32bytes) - const encodedPartialVouchers = new BytesWriter(20 + 128 * partialVouchers.length) - - encodedPartialVouchers.writeHex(partialVouchers[0].allocation) - for (const partialVoucher of partialVouchers) { - // [fees, signature, receipt_id_min, receipt_id_max] as 0x-prefixed string list - const fee = BigNumber.from(partialVoucher.fees).toHexString() - // We slice the hex string to remove the "0x" prefix from the byte length calculation - const feeByteLength = fee.slice(2).length / 2 - const feePadding = 33 - feeByteLength - encodedPartialVouchers.writeZeroes(feePadding) - encodedPartialVouchers.writeHex(fee) - encodedPartialVouchers.writeHex(partialVoucher.signature) - encodedPartialVouchers.writeHex(partialVoucher.receipt_id_min) - encodedPartialVouchers.writeHex(partialVoucher.receipt_id_max) +export function encodePartialVouchers( + partialVouchers: PartialVoucher[], +): AllocationPartialVouchers { + const uniqueAllocations = new Set(partialVouchers.map((voucher) => voucher.allocation)) + .size + if (uniqueAllocations !== 1) { + throw Error( + `Partial vouchers set must be for a single allocation, '${uniqueAllocations}' unique allocations represented`, + ) + } + + return { + allocation: partialVouchers[0].allocation, + partialVouchers, } - return encodedPartialVouchers } -const registerReceiptMetrics = (metrics: Metrics) => ({ +const registerReceiptMetrics = (metrics: Metrics, networkIdentifier: string) => ({ receiptsToCollect: new metrics.client.Gauge({ - name: 'indexer_agent_receipts_to_collect', + name: `indexer_agent_receipts_to_collect_${networkIdentifier}`, help: 'Individual receipts to collect', registers: [metrics.registry], labelNames: ['allocation'], }), failedReceipts: new metrics.client.Counter({ - name: 'indexer_agent_receipts_failed', + name: `indexer_agent_receipts_failed_${networkIdentifier}`, help: 'Failed to queue receipts to collect', registers: [metrics.registry], labelNames: ['allocation'], }), partialVouchersToExchange: new metrics.client.Gauge({ - name: 'indexer_agent_vouchers_to_exchange', + name: `indexer_agent_vouchers_to_exchange_${networkIdentifier}`, help: 'Individual partial vouchers to exchange', registers: [metrics.registry], labelNames: ['allocation'], }), receiptsCollectDuration: new metrics.client.Histogram({ - name: 'indexer_agent_receipts_exchange_duration', + name: `indexer_agent_receipts_exchange_duration_${networkIdentifier}`, help: 'Duration of processing and exchanging receipts to voucher', registers: [metrics.registry], labelNames: ['allocation'], }), vouchers: new metrics.client.Counter({ - name: 'indexer_agent_vouchers', + name: `indexer_agent_vouchers_${networkIdentifier}`, help: 'Individual vouchers to redeem', registers: [metrics.registry], labelNames: ['allocation'], }), successVoucherRedeems: new metrics.client.Counter({ - name: 'indexer_agent_voucher_exchanges_ok', + name: `indexer_agent_voucher_exchanges_ok_${networkIdentifier}`, help: 'Successfully redeemed vouchers', registers: [metrics.registry], labelNames: ['allocation'], }), invalidVoucherRedeems: new metrics.client.Counter({ - name: 'indexer_agent_voucher_exchanges_invalid', + name: `indexer_agent_voucher_exchanges_invalid_${networkIdentifier}`, help: 'Invalid vouchers redeems - tx paused or unauthorized', registers: [metrics.registry], labelNames: ['allocation'], }), failedVoucherRedeems: new metrics.client.Counter({ - name: 'indexer_agent_voucher_redeems_failed', + name: `indexer_agent_voucher_redeems_failed_${networkIdentifier}`, help: 'Failed redeems for vouchers', registers: [metrics.registry], labelNames: ['allocation'], }), vouchersRedeemDuration: new metrics.client.Histogram({ - name: 'indexer_agent_vouchers_redeem_duration', + name: `indexer_agent_vouchers_redeem_duration_${networkIdentifier}`, help: 'Duration of redeeming vouchers', registers: [metrics.registry], labelNames: ['allocation'], }), vouchersBatchRedeemSize: new metrics.client.Gauge({ - name: 'indexer_agent_vouchers_redeem', + name: `indexer_agent_vouchers_redeem_${networkIdentifier}`, help: 'Size of redeeming batched vouchers', registers: [metrics.registry], }), voucherCollectedFees: new metrics.client.Gauge({ - name: 'indexer_agent_voucher_collected_fees', + name: `indexer_agent_voucher_collected_fees_${networkIdentifier}`, help: 'Amount of query fees collected for a voucher', registers: [metrics.registry], labelNames: ['allocation'], }), }) + +interface GatewayRoutes { + collectReceipts: URL + voucher: URL + partialVoucher: URL +} + +function processGatewayRoutes(input: string): GatewayRoutes { + const GATEWAY_ROUTES = { + collectReceipts: 'collect-receipts', + voucher: 'voucher', + partialVoucher: 'partial-voucher', + } + + // Strip existing information except for protocol and host + const inputURL = new URL(input) + const base = `${inputURL.protocol}//${inputURL.host}` + + function route(pathname: string): URL { + const url = new URL(base) + url.pathname = pathname + return url + } + + return { + collectReceipts: route(GATEWAY_ROUTES.collectReceipts), + voucher: route(GATEWAY_ROUTES.voucher), + partialVoucher: route(GATEWAY_ROUTES.partialVoucher), + } +} diff --git a/packages/indexer-common/src/epoch-subgraph.ts b/packages/indexer-common/src/epoch-subgraph.ts index 0f4339888..d2248c4eb 100644 --- a/packages/indexer-common/src/epoch-subgraph.ts +++ b/packages/indexer-common/src/epoch-subgraph.ts @@ -2,12 +2,19 @@ import axios, { AxiosInstance, AxiosResponse } from 'axios' import { DocumentNode, print } from 'graphql' import { CombinedError } from '@urql/core' import { QueryResult } from './network-subgraph' - +import { Logger } from '@graphprotocol/common-ts' +import { SubgraphFreshnessChecker } from './subgraphs' export class EpochSubgraph { - private constructor(private endpointClient: AxiosInstance) {} + endpointClient: AxiosInstance + freshnessChecker: SubgraphFreshnessChecker + logger: Logger - public static async create(endpoint: string): Promise { - const endpointClient = axios.create({ + constructor( + endpoint: string, + freshnessChecker: SubgraphFreshnessChecker, + logger: Logger, + ) { + this.endpointClient = axios.create({ baseURL: endpoint, headers: { 'content-type': 'application/json' }, @@ -17,8 +24,17 @@ export class EpochSubgraph { // Don't transform responses transformResponse: (data) => data, }) - // Create the Epoch subgraph instance - return new EpochSubgraph(endpointClient) + this.freshnessChecker = freshnessChecker + this.logger = logger + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async checkedQuery( + query: DocumentNode, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + variables?: Record, + ): Promise> { + return this.freshnessChecker.checkedQuery(this, query, variables) } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -32,6 +48,7 @@ export class EpochSubgraph { variables, }) const data = JSON.parse(response.data) + this.logger.trace('Epoch Subgraph query', { data }) if (data.errors) { return { error: new CombinedError({ graphQLErrors: data.errors }) } } diff --git a/packages/indexer-common/src/errors.ts b/packages/indexer-common/src/errors.ts index 0f61aecef..2b1575190 100644 --- a/packages/indexer-common/src/errors.ts +++ b/packages/indexer-common/src/errors.ts @@ -96,7 +96,7 @@ export const INDEXER_ERROR_MESSAGES: Record = { IE006: 'Failed to cross-check allocation state with contracts', IE007: 'Failed to check for network pause', IE008: 'Failed to check operator status for indexer', - IE009: 'Failed to query subgraph deployments worth indexing', + IE009: 'Failed to query subgraph deployments', IE010: 'Failed to query indexer allocations', IE011: 'Failed to query claimable indexer allocations', IE012: 'Failed to register indexer', @@ -162,7 +162,8 @@ export const INDEXER_ERROR_MESSAGES: Record = { IE071: 'Add Epoch subgraph support for non-protocol chains', IE072: 'Failed to execute batch tx (contract: staking)', IE073: 'Failed to query subgraph features from indexing statuses endpoint', - IE074: 'Failed to deploy the graft base for the target deployment', + IE074: 'Failed to deploy subgraph: network not supported', + IE075: 'Failed to deploy the graft base for the target deployment', } export type IndexerErrorCause = unknown diff --git a/packages/indexer-common/src/graph-node.ts b/packages/indexer-common/src/graph-node.ts new file mode 100644 index 000000000..5157ea4a2 --- /dev/null +++ b/packages/indexer-common/src/graph-node.ts @@ -0,0 +1,655 @@ +import gql from 'graphql-tag' +import jayson, { Client as RpcClient } from 'jayson/promise' +import { Logger, SubgraphDeploymentID } from '@graphprotocol/common-ts' +import { Client, createClient } from '@urql/core' +import { + INDEXER_ERROR_MESSAGES, + IndexerError, + indexerError, + IndexerErrorCode, +} from './errors' +import { BlockPointer, ChainIndexingStatus, IndexingStatus } from './types' +import pRetry from 'p-retry' +import axios, { AxiosInstance } from 'axios' +import fetch from 'isomorphic-fetch' + +interface indexNode { + id: string + deployments: string[] +} + +export interface SubgraphDeploymentAssignment { + id: SubgraphDeploymentID + node: string +} + +export interface IndexingStatusFetcherOptions { + logger: Logger + statusEndpoint: string +} + +export interface SubgraphFeatures { + // `null` is only expected when Graph Node detects validation errors in the Subgraph Manifest. + network: string | null +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const parseGraphQLIndexingStatus = (indexingStatus: any): IndexingStatus => ({ + subgraphDeployment: new SubgraphDeploymentID(indexingStatus.subgraphDeployment), + synced: indexingStatus.synced, + health: indexingStatus.health, + fatalError: indexingStatus.fatalError, + node: indexingStatus.node, + chains: indexingStatus.chains.map(parseGraphQLChain), +}) + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const parseGraphQLChain = (chain: any): ChainIndexingStatus => ({ + network: chain.network, + latestBlock: parseGraphQLBlockPointer(chain.latestBlock), + chainHeadBlock: parseGraphQLBlockPointer(chain.chainHeadBlock), + earliestBlock: parseGraphQLBlockPointer(chain.earliestBlock), +}) + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const parseGraphQLBlockPointer = (block: any): BlockPointer | null => + block + ? { + number: +block.number, + hash: block.hash, + } + : null + +export class GraphNode { + admin: RpcClient + private queryBaseURL: URL + status: Client + logger: Logger + indexNodeIDs: string[] + + constructor( + logger: Logger, + adminEndpoint: string, + queryEndpoint: string, + statusEndpoint: string, + indexNodeIDs: string[], + ) { + this.logger = logger.child({ component: 'GraphNode' }) + this.status = createClient({ + url: statusEndpoint, + fetch, + requestPolicy: 'network-only', + }) + + if (adminEndpoint.startsWith('https')) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.admin = jayson.Client.https(adminEndpoint as any) + } else { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.admin = jayson.Client.http(adminEndpoint as any) + } + + this.queryBaseURL = new URL(`/subgraphs/id/`, queryEndpoint) + + this.indexNodeIDs = indexNodeIDs + } + + async connect(): Promise { + try { + this.logger.info(`Check if indexing status API is available`) + await pRetry( + async () => { + const deployments = await this.subgraphDeployments() + this.logger.info(`Successfully connected to indexing status API`, { + currentDeployments: deployments.map((deployment) => deployment.display), + }) + }, + { + retries: 10, + maxTimeout: 10000, + onFailedAttempt: (err) => { + this.logger.warn(`Indexing statuses could not be queried`, { + attempt: err.attemptNumber, + retriesLeft: err.retriesLeft, + err: err.message, + }) + }, + } as pRetry.Options, + ) + } catch (error) { + const err = indexerError(IndexerErrorCode.IE024, error) + this.logger.error(`Failed to connect to indexing status API`, { + err, + }) + throw err + } + } + + // AxiosClient factory scoped by subgraph IFPS hash + getQueryClient(deploymentIpfsHash: string): AxiosInstance { + return axios.create({ + baseURL: new URL(deploymentIpfsHash, this.queryBaseURL).toString(), + headers: { 'content-type': 'application/json' }, + responseType: 'text', // Don't parse responses as JSON + transformResponse: (data) => data, // Don't transform responses + }) + } + + public async subgraphDeployments(): Promise { + return (await this.subgraphDeploymentsAssignments()).map((details) => details.id) + } + + public async subgraphDeploymentsAssignments(): Promise { + try { + this.logger.debug('Fetch subgraph deployment assignments') + const result = await this.status + .query(gql` + { + indexingStatuses { + subgraphDeployment: subgraph + node + } + } + `) + .toPromise() + + if (result.error) { + throw result.error + } + + if (!result.data.indexingStatuses || result.data.length === 0) { + this.logger.warn(`No 'indexingStatuses' data returned from index nodes`, { + data: result.data, + }) + return [] + } + + type QueryResult = { subgraphDeployment: string; node: string } + + return result.data.indexingStatuses + .filter((status: QueryResult) => status.node && status.node !== 'removed') + .map((status: QueryResult) => { + return { + id: new SubgraphDeploymentID(status.subgraphDeployment), + node: status.node, + } + }) + } catch (error) { + const err = indexerError(IndexerErrorCode.IE018, error) + this.logger.error(`Failed to query indexing status API`, { err }) + throw err + } + } + + async indexNodes(): Promise { + try { + this.logger.trace(`Querying indexing statuses`) + const result = await this.status + .query(gql` + { + indexingStatuses { + subgraphDeployment: subgraph + node + } + } + `) + .toPromise() + + if (result.error) { + throw result.error + } + + this.logger.trace(`Queried indexing statuses`, { + data: result.data, + }) + + if (!result.data.indexingStatuses) { + throw new Error( + "Received invalid results when querying indexing statuses: Response is missing a value for the 'indexingStatus' field", + ) + } + + const indexNodes: indexNode[] = [] + result.data.indexingStatuses.map( + (status: { subgraphDeployment: string; node: string }) => { + const node = indexNodes.find((node) => node.id === status.node) + node + ? node.deployments.push(status.subgraphDeployment) + : indexNodes.push({ + id: status.node, + deployments: [status.subgraphDeployment], + }) + }, + ) + + this.logger.trace(`Queried index nodes`, { + indexNodes, + }) + return indexNodes + } catch (error) { + const err = indexerError(IndexerErrorCode.IE018, error) + this.logger.error(`Failed to query index nodes API (Should get a different IE?)`, { + err, + }) + throw err + } + } + + // -------------------------------------------------------------------------------- + // * Subgraph Management + // -------------------------------------------------------------------------------- + + async create(name: string): Promise { + try { + this.logger.info(`Create subgraph name`, { name }) + const response = await this.admin.request('subgraph_create', { name }) + if (response.error) { + throw response.error + } + this.logger.info(`Successfully created subgraph name`, { name }) + } catch (error) { + if (error.message.includes('already exists')) { + this.logger.debug(`Subgraph name already exists, will deploy to existing name`, { + name, + }) + return + } + throw error + } + } + + async deploy( + name: string, + deployment: SubgraphDeploymentID, + node_id: string, + ): Promise { + try { + this.logger.info(`Deploy subgraph deployment`, { + name, + deployment: deployment.display, + node_id, + }) + const response = await this.admin.request('subgraph_deploy', { + name, + ipfs_hash: deployment.ipfsHash, + node_id: node_id, + }) + + this.logger.trace(`Response from 'subgraph_deploy' call`, { + deployment: deployment.display, + name, + node_id, + response, + }) + + if (response.error) { + throw response.error + } + this.logger.info(`Successfully deployed subgraph deployment`, { + name, + deployment: deployment.display, + }) + } catch (error) { + // If more specific error not found use the generic 'Failed to deploy' error code + let errorCode = IndexerErrorCode.IE026 + + if (error.message && error.message.includes('network not supported')) { + errorCode = IndexerErrorCode.IE074 + } + + const err = indexerError(errorCode, error) + this.logger.error(INDEXER_ERROR_MESSAGES[errorCode], { + name, + deployment: deployment.display, + err, + }) + throw err + } + } + + async remove(deployment: SubgraphDeploymentID): Promise { + try { + this.logger.info(`Remove subgraph deployment`, { + deployment: deployment.display, + }) + const response = await this.admin.request('subgraph_reassign', { + node_id: 'removed', + ipfs_hash: deployment.ipfsHash, + }) + if (response.error) { + throw response.error + } + this.logger.info(`Successfully removed subgraph deployment`, { + deployment: deployment.display, + }) + } catch (error) { + const errorCode = IndexerErrorCode.IE027 + this.logger.error(INDEXER_ERROR_MESSAGES[errorCode], { + deployment: deployment.display, + error: indexerError(errorCode, error), + }) + } + } + + async reassign(deployment: SubgraphDeploymentID, node: string): Promise { + try { + this.logger.info(`Reassign subgraph deployment`, { + deployment: deployment.display, + node, + }) + const response = await this.admin.request('subgraph_reassign', { + node_id: node, + ipfs_hash: deployment.ipfsHash, + }) + if (response.error) { + throw response.error + } + } catch (error) { + if (error.message.includes('unchanged')) { + this.logger.debug(`Subgraph deployment assignment unchanged`, { + deployment: deployment.display, + node, + }) + return + } + const errorCode = IndexerErrorCode.IE028 + const err = indexerError(errorCode, error) + this.logger.error(INDEXER_ERROR_MESSAGES[errorCode], { + deployment: deployment.display, + err, + }) + throw err + } + } + + async ensure(name: string, deployment: SubgraphDeploymentID): Promise { + try { + // Randomly assign to unused nodes if they exist, + // otherwise use the node with lowest deployments assigned + const indexNodes = (await this.indexNodes()).filter( + (node: { id: string; deployments: Array }) => { + return node.id && node.id !== 'removed' + }, + ) + const usedIndexNodeIDs = indexNodes.map((node) => node.id) + const unusedNodes = this.indexNodeIDs.filter( + (nodeID) => !(nodeID in usedIndexNodeIDs), + ) + + const targetNode = unusedNodes + ? unusedNodes[Math.floor(Math.random() * unusedNodes.length)] + : indexNodes.sort((nodeA, nodeB) => { + return nodeA.deployments.length - nodeB.deployments.length + })[0].id + await this.create(name) + await this.deploy(name, deployment, targetNode) + await this.reassign(deployment, targetNode) + } catch (error) { + if (!(error instanceof IndexerError)) { + const errorCode = IndexerErrorCode.IE020 + this.logger.error(INDEXER_ERROR_MESSAGES[errorCode], { + name, + deployment: deployment.display, + error: indexerError(errorCode, error), + }) + } + } + } + + // -------------------------------------------------------------------------------- + // * Indexing Status + // -------------------------------------------------------------------------------- + public async indexingStatus( + deployments: SubgraphDeploymentID[], + ): Promise { + const indexingStatusesQueryBody = ` + subgraphDeployment: subgraph + synced + health + fatalError { + handler + message + } + node + chains { + network + ... on EthereumIndexingStatus { + latestBlock { + number + hash + } + chainHeadBlock { + number + hash + } + earliestBlock { + number + hash + } + } + }` + const query = + deployments.length > 0 + ? `query indexingStatuses($deployments: [String!]!) { + indexingStatuses(subgraphs: $deployments) { + ${indexingStatusesQueryBody} + } + }` + : `query indexingStatuses { + indexingStatuses { + ${indexingStatusesQueryBody} + } + }` + + const queryIndexingStatuses = async () => { + const result = await this.status + .query(query, { deployments: deployments.map((id) => id.ipfsHash) }) + .toPromise() + + return ( + result.data.indexingStatuses + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .map((status: any) => ({ + ...status, + subgraphDeployment: new SubgraphDeploymentID(status.subgraphDeployment), + })) + ) + } + + try { + return await pRetry(queryIndexingStatuses, { + retries: 5, + maxTimeout: 10000, + onFailedAttempt: (err) => { + this.logger.warn(`Indexing statuses could not be queried`, { + attempt: err.attemptNumber, + retriesLeft: err.retriesLeft, + deployments, + err: err.message, + }) + }, + } as pRetry.Options) + } catch (error) { + const err = indexerError(IndexerErrorCode.IE018, error) + this.logger.error(`Failed to query indexing status API`, { + deployments, + err, + }) + throw err + } + } + + public async proofOfIndexing( + deployment: SubgraphDeploymentID, + block: BlockPointer, + indexerAddress: string, + ): Promise { + try { + return await pRetry( + async (attempt) => { + const result = await this.status + .query( + gql` + query proofOfIndexing( + $subgraph: String! + $blockNumber: Int! + $blockHash: String! + $indexer: String! + ) { + proofOfIndexing( + subgraph: $subgraph + blockNumber: $blockNumber + blockHash: $blockHash + indexer: $indexer + ) + } + `, + { + subgraph: deployment.ipfsHash, + blockNumber: +block.number, + blockHash: block.hash, + indexer: indexerAddress, + }, + ) + .toPromise() + + if (result.error) { + if ( + result.error.message && + result.error.message.includes('DeploymentNotFound') + ) { + return undefined + } + throw result.error + } + this.logger.trace('Reference POI generated', { + indexer: indexerAddress, + subgraph: deployment.ipfsHash, + block: block, + proof: result.data.proofOfIndexing, + attempt, + }) + + return result.data.proofOfIndexing + }, + { + retries: 5, + maxTimeout: 10000, + onFailedAttempt: (err) => { + this.logger.warn(`Proof of indexing could not be queried`, { + attempt: err.attemptNumber, + retriesLeft: err.retriesLeft, + err: err.message, + }) + }, + } as pRetry.Options, + ) + } catch (error) { + const err = indexerError(IndexerErrorCode.IE019, error) + this.logger.error(`Failed to query proof of indexing`, { + subgraph: deployment.ipfsHash, + blockHash: block, + indexer: indexerAddress, + err: err, + }) + return undefined + } + } + + public async blockHashFromNumber( + networkAlias: string, + blockNumber: number, + ): Promise { + this.logger.trace(`Querying blockHashFromNumber`, { networkAlias, blockNumber }) + try { + return await pRetry( + async (attempt) => { + const result = await this.status + .query( + gql` + query blockHashFromNumber($network: String!, $blockNumber: Int!) { + blockHashFromNumber(network: $network, blockNumber: $blockNumber) + } + `, + { + network: networkAlias, + blockNumber, + }, + ) + .toPromise() + + if (!result.data || !result.data.blockHashFromNumber || result.error) { + throw new Error( + `Failed to query graph node for blockHashFromNumber: ${ + result.error ?? 'no data returned' + }`, + ) + } + + this.logger.trace('Resolved block hash', { + networkAlias, + blockNumber, + blockHash: result.data.blockHashFromNumber, + attempt, + }) + + return `0x${result.data.blockHashFromNumber}` + }, + { + retries: 5, + maxTimeout: 10000, + onFailedAttempt: (err) => { + this.logger.warn(`Block hash could not be queried`, { + networkAlias, + blockNumber, + attempt: err.attemptNumber, + retriesLeft: err.retriesLeft, + err: err.message, + }) + }, + } as pRetry.Options, + ) + } catch (error) { + const err = indexerError(IndexerErrorCode.IE070, error) + this.logger.error(`Failed to query block hash`, { + networkAlias, + blockNumber, + error: error.message, + }) + throw err + } + } + + public async subgraphFeatures( + subgraphDeploymentId: SubgraphDeploymentID, + ): Promise { + const subgraphId = subgraphDeploymentId.ipfsHash + try { + const result = await this.status + .query( + gql` + query subgraphFeatures($subgraphId: String!) { + subgraphFeatures(subgraphId: $subgraphId) { + network + } + } + `, + { subgraphId }, + ) + .toPromise() + + if (result.error) { + throw result.error + } + if (!result.data) { + throw new Error('Subgraph Deployment Not Found') + } + return result.data.subgraphFeatures as SubgraphFeatures + } catch (error) { + const errorCode = IndexerErrorCode.IE073 + const err = indexerError(errorCode, error) + this.logger.error(INDEXER_ERROR_MESSAGES[errorCode], { err, subgraphId }) + throw err + } + } +} diff --git a/packages/indexer-common/src/index.ts b/packages/indexer-common/src/index.ts index 3c931d0d2..0090cad0d 100644 --- a/packages/indexer-common/src/index.ts +++ b/packages/indexer-common/src/index.ts @@ -1,15 +1,19 @@ export * from './actions' export * from './allocations' export * from './async-cache' +export * from './epoch-subgraph' export * from './errors' -export * from './query-fees' export * from './indexer-management' -export * from './indexing-status' +export * from './graph-node' +export * from './operator' export * from './network' export * from './network-subgraph' -export * from './epoch-subgraph' +export * from './query-fees' export * from './rules' export * from './subgraphs' export * from './transactions' -export * from './utils' export * from './types' +export * from './utils' +export * from './parsers' +export * as specification from './network-specification' +export * from './multi-networks' diff --git a/packages/indexer-common/src/indexer-management/__tests__/allocations.test.ts b/packages/indexer-common/src/indexer-management/__tests__/allocations.test.ts new file mode 100644 index 000000000..12454f584 --- /dev/null +++ b/packages/indexer-common/src/indexer-management/__tests__/allocations.test.ts @@ -0,0 +1,169 @@ +import { + Action, + ActionType, + AllocationManager, + defineIndexerManagementModels, + defineQueryFeeModels, + GraphNode, + IndexerManagementModels, + Network, + QueryFeeModels, +} from '@graphprotocol/indexer-common' +import { + connectDatabase, + createLogger, + createMetrics, + Logger, + Metrics, + parseGRT, +} from '@graphprotocol/common-ts' +import { + invalidReallocateAction, + invalidUnallocateAction, + queuedAllocateAction, + testNetworkSpecification, +} from './util' +import { Sequelize } from 'sequelize' + +// Make global Jest variables available +// eslint-disable-next-line @typescript-eslint/no-explicit-any +declare const __DATABASE__: any +declare const __LOG_LEVEL__: never + +let allocationManager: AllocationManager +let logger: Logger +let managementModels: IndexerManagementModels +let metrics: Metrics +let queryFeeModels: QueryFeeModels +let sequelize: Sequelize + +const setup = async () => { + logger = createLogger({ + name: 'Indexer API Client', + async: false, + level: __LOG_LEVEL__ ?? 'error', + }) + metrics = createMetrics() + // Clearing the registry prevents duplicate metric registration in the default registry. + metrics.registry.clear() + sequelize = await connectDatabase(__DATABASE__) + managementModels = defineIndexerManagementModels(sequelize) + queryFeeModels = defineQueryFeeModels(sequelize) + sequelize = await sequelize.sync({ force: true }) + + const graphNode = new GraphNode( + logger, + 'https://test-admin-endpoint.xyz', + 'https://test-query-endpoint.xyz', + 'https://test-status-endpoint.xyz', + [], + ) + + const network = await Network.create( + logger, + testNetworkSpecification, + queryFeeModels, + graphNode, + metrics, + ) + // TODO: Can we expose AllocationManager from client so we don't need to build this separately? + + allocationManager = new AllocationManager( + logger.child({ protocolNetwork: network.specification.networkIdentifier }), + managementModels, + graphNode, + network, + ) +} + +const setupEach = async () => { + sequelize = await sequelize.sync({ force: true }) +} +const teardownEach = async () => { + // Clear out query fee model tables + await queryFeeModels.allocationReceipts.truncate({ cascade: true }) + await queryFeeModels.vouchers.truncate({ cascade: true }) + await queryFeeModels.transferReceipts.truncate({ cascade: true }) + await queryFeeModels.transfers.truncate({ cascade: true }) + await queryFeeModels.allocationSummaries.truncate({ cascade: true }) + + // Clear out indexer management models + await managementModels.Action.truncate({ cascade: true }) + await managementModels.CostModel.truncate({ cascade: true }) + await managementModels.IndexingRule.truncate({ cascade: true }) + await managementModels.POIDispute.truncate({ cascade: true }) +} + +const teardownAll = async () => { + await sequelize.drop({}) +} + +describe('Allocation Manager', () => { + beforeAll(setup) + beforeEach(setupEach) + afterEach(teardownEach) + afterAll(teardownAll) + + // We have been rate-limited on CI as this test uses RPC providers, + // so we set its timeout to a higher value than usual. + jest.setTimeout(30_000) + + // Reuse an existing allocation with 25 sextillion allocated GRT + const allocationID = '0x96737b6a31f40edaf96c567efbb98935aa906ab9' + + // Redefine test actions to use that allocation ID + const unallocateAction = { + ...invalidUnallocateAction, + poi: '0x1', // non-zero POI + allocationID, + } + const reallocateAction = { + ...invalidReallocateAction, + amount: '10000', + allocationID, + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore: Mocking the Action type for this test + const actions = [queuedAllocateAction, unallocateAction, reallocateAction] as Action[] + + test('stakeUsageSummary() correctly calculates token balances for array of actions', async () => { + const balances = await Promise.all( + actions.map((action: Action) => allocationManager.stakeUsageSummary(action)), + ) + + const allocate = balances[0] + const unallocate = balances[1] + const reallocate = balances[2] + + // Allocate test action + expect(allocate.action.type).toBe(ActionType.ALLOCATE) + expect(allocate.allocates).toStrictEqual(parseGRT('10000')) + expect(allocate.rewards.isZero()).toBeTruthy() + expect(allocate.unallocates.isZero()).toBeTruthy() + expect(allocate.balance).toStrictEqual(parseGRT('10000')) + + // Unallocate test action + expect(unallocate.action.type).toBe(ActionType.UNALLOCATE) + expect(unallocate.allocates.isZero()).toBeTruthy() + expect(unallocate.rewards.isZero()).toBeFalsy() + expect(unallocate.unallocates).toStrictEqual(parseGRT('25000')) + expect(unallocate.balance).toStrictEqual( + unallocate.allocates.sub(unallocate.unallocates).sub(unallocate.rewards), + ) + + // This Reallocate test Action intentionally uses a null or zeroed POI, so it should not accrue rewards. + expect(reallocate.action.type).toBe(ActionType.REALLOCATE) + expect(reallocate.allocates).toStrictEqual(parseGRT('10000')) + expect(reallocate.rewards.isZero()).toBeTruthy() + expect(reallocate.unallocates).toStrictEqual(parseGRT('25000')) + expect(reallocate.balance).toStrictEqual(parseGRT('-15000')) + }) + + test('validateActionBatchFeasibility() validates and correctly sorts actions based on net token balance', async () => { + const reordered = await allocationManager.validateActionBatchFeasibilty(actions) + expect(reordered[0]).toStrictEqual(unallocateAction) + expect(reordered[1]).toStrictEqual(reallocateAction) + expect(reordered[2]).toStrictEqual(queuedAllocateAction) + }) +}) diff --git a/packages/indexer-common/src/indexer-management/__tests__/helpers.ts b/packages/indexer-common/src/indexer-management/__tests__/helpers.test.ts similarity index 76% rename from packages/indexer-common/src/indexer-management/__tests__/helpers.ts rename to packages/indexer-common/src/indexer-management/__tests__/helpers.test.ts index b3cc7084b..906a70e31 100644 --- a/packages/indexer-common/src/indexer-management/__tests__/helpers.ts +++ b/packages/indexer-common/src/indexer-management/__tests__/helpers.test.ts @@ -13,8 +13,10 @@ import { IndexingDecisionBasis, IndexingRuleAttributes, } from '../models' +import { defineQueryFeeModels, specification as spec } from '../../index' +import { networkIsL1, networkIsL2 } from '../types' import { fetchIndexingRules, upsertIndexingRule } from '../rules' -import { SubgraphIdentifierType } from '../../subgraphs' +import { SubgraphFreshnessChecker, SubgraphIdentifierType } from '../../subgraphs' import { ActionManager } from '../actions' import { actionFilterToWhereOptions, ActionStatus, ActionType } from '../../actions' import { literal, Op, Sequelize } from 'sequelize' @@ -24,7 +26,7 @@ import { EpochSubgraph, indexerError, IndexerErrorCode, - IndexingStatusResolver, + GraphNode, NetworkMonitor, NetworkSubgraph, resolveChainAlias, @@ -32,6 +34,7 @@ import { SubgraphDeployment, getTestProvider, } from '@graphprotocol/indexer-common' +import { mockLogger, mockProvider } from '../../__tests__/subgraph.test' import { BigNumber, ethers, utils } from 'ethers' // Make global Jest variable available @@ -44,7 +47,7 @@ let sequelize: Sequelize let models: IndexerManagementModels let ethereum: ethers.providers.BaseProvider let contracts: NetworkContracts -let indexingStatusResolver: IndexingStatusResolver +let graphNode: GraphNode let networkSubgraph: NetworkSubgraph let epochSubgraph: EpochSubgraph let networkMonitor: NetworkMonitor @@ -56,7 +59,8 @@ const setupModels = async () => { // Spin up db sequelize = await connectDatabase(__DATABASE__) models = defineIndexerManagementModels(sequelize) - await sequelize.sync({ force: true }) + defineQueryFeeModels(sequelize) + sequelize = await sequelize.sync({ force: true }) } const setupMonitor = async () => { @@ -69,25 +73,50 @@ const setupMonitor = async () => { }) ethereum = getTestProvider('goerli') contracts = await connectContracts(ethereum, 5) + + const subgraphFreshnessChecker = new SubgraphFreshnessChecker( + 'Test Subgraph', + mockProvider, + 10, + 10, + mockLogger, + 1, + ) + networkSubgraph = await NetworkSubgraph.create({ logger, endpoint: 'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli', deployment: undefined, + subgraphFreshnessChecker, }) - epochSubgraph = await EpochSubgraph.create( + + epochSubgraph = new EpochSubgraph( 'https://api.thegraph.com/subgraphs/name/graphprotocol/goerli-epoch-block-oracle', + subgraphFreshnessChecker, + logger, ) - indexingStatusResolver = new IndexingStatusResolver({ - logger: logger, + graphNode = new GraphNode( + logger, + 'http://test-admin-endpoint.xyz', + 'https://test-query-endpoint.xyz', statusEndpoint, + [], + ) + + const indexerOptions = spec.IndexerOptions.parse({ + address: '0xc61127cdfb5380df4214b0200b9a07c7c49d34f9', + mnemonic: + 'word ivory whale diesel slab pelican voyage oxygen chat find tobacco sport', + url: 'http://test-url.xyz', }) + networkMonitor = new NetworkMonitor( resolveChainId('goerli'), contracts, - toAddress('0xc61127cdfb5380df4214b0200b9a07c7c49d34f9'), + indexerOptions, logger, - indexingStatusResolver, + graphNode, networkSubgraph, ethereum, epochSubgraph, @@ -101,7 +130,6 @@ const createMockAllocation = (): Allocation => { stakedTokens: BigNumber.from(50000), signalledTokens: BigNumber.from(100000), queryFeesAmount: BigNumber.from(0), - activeAllocations: 2, } as SubgraphDeployment const mockAllocation = { id: toAddress('0xbAd8935f75903A1eF5ea62199d98Fd7c3c1ab20C'), @@ -138,8 +166,8 @@ describe('Indexing Rules', () => { allocationAmount: '5000', identifierType: SubgraphIdentifierType.DEPLOYMENT, decisionBasis: IndexingDecisionBasis.ALWAYS, + protocolNetwork: 'goerli', } as Partial - const setIndexingRuleResult = await upsertIndexingRule(logger, models, indexingRule) expect(setIndexingRuleResult).toHaveProperty( 'allocationAmount', @@ -155,7 +183,8 @@ describe('Indexing Rules', () => { IndexingDecisionBasis.ALWAYS, ) - await expect(fetchIndexingRules(models, false)).resolves.toHaveLength(1) + // When reading directly to the database, `protocolNetwork` must be in the CAIP2-ID format. + await expect(fetchIndexingRules(models, false, 'eip155:5')).resolves.toHaveLength(1) }) }) @@ -204,6 +233,8 @@ describe('Actions', () => { source: 'indexerAgent', reason: 'indexingRule', priority: 0, + // When writing directly to the database, `protocolNetwork` must be in the CAIP2-ID format. + protocolNetwork: 'eip155:5', } await models.Action.upsert(action) @@ -315,3 +346,52 @@ describe.skip('Monitor', () => { ).rejects.toEqual(indexerError(IndexerErrorCode.IE018, `Could not resolve POI`)) }) }) + +describe('Network layer detection', () => { + interface NetworkLayer { + name: string + l1: boolean + l2: boolean + } + + // Should be true for L1 and false for L2 + const l1Networks: NetworkLayer[] = ['mainnet', 'eip155:1', 'goerli', 'eip155:5'].map( + (name: string) => ({ name, l1: true, l2: false }), + ) + + // Should be false for L1 and true for L2 + const l2Networks: NetworkLayer[] = [ + 'arbitrum-one', + 'eip155:42161', + 'arbitrum-goerli', + 'eip155:421613', + ].map((name: string) => ({ name, l1: false, l2: true })) + + // Those will be false for L1 and L2 + const nonProtocolNetworks: NetworkLayer[] = [ + 'fantom', + 'eip155:250', + 'hardhat', + 'eip155:1337', + 'matic', + 'eip155:137', + 'gnosis', + 'eip155:100', + ].map((name: string) => ({ name, l1: false, l2: false })) + + const testCases = [...l1Networks, ...l2Networks, ...nonProtocolNetworks] + + test.each(testCases)('Can detect network layer [$name]', (network) => { + expect(networkIsL1(network.name)).toStrictEqual(network.l1) + expect(networkIsL2(network.name)).toStrictEqual(network.l2) + }) + + const invalidTProtocolNetworkNames = ['invalid-name', 'eip155:9999'] + + test.each(invalidTProtocolNetworkNames)( + 'Throws error when protocol network is unknown [%s]', + (invalidProtocolNetworkName) => { + expect(() => networkIsL1(invalidProtocolNetworkName)).toThrow() + }, + ) +}) diff --git a/packages/indexer-common/src/indexer-management/__tests__/resolvers/actions.ts b/packages/indexer-common/src/indexer-management/__tests__/resolvers/actions.test.ts similarity index 74% rename from packages/indexer-common/src/indexer-management/__tests__/resolvers/actions.ts rename to packages/indexer-common/src/indexer-management/__tests__/resolvers/actions.test.ts index 6b436ab32..dc7df5ef4 100644 --- a/packages/indexer-common/src/indexer-management/__tests__/resolvers/actions.ts +++ b/packages/indexer-common/src/indexer-management/__tests__/resolvers/actions.test.ts @@ -2,51 +2,42 @@ import { Sequelize } from 'sequelize' import gql from 'graphql-tag' -import { BigNumber, ethers, Wallet } from 'ethers' import { - connectContracts, connectDatabase, createLogger, - createMetrics, Logger, Metrics, - mutable, - NetworkContracts, - parseGRT, - toAddress, + createMetrics, } from '@graphprotocol/common-ts' -import { - createIndexerManagementClient, - IndexerManagementClient, - IndexerManagementDefaults, -} from '../../client' +import { IndexerManagementClient } from '../../client' import { Action, defineIndexerManagementModels, IndexerManagementModels, } from '../../models' import { - defineQueryFeeModels, ActionInput, ActionParams, ActionStatus, ActionType, - AllocationReceiptCollector, - IndexingStatusResolver, - NetworkSubgraph, + defineQueryFeeModels, OrderDirection, QueryFeeModels, - TransactionManager, - NetworkMonitor, - EpochSubgraph, - resolveChainId, - AllocationManager, - SubgraphManager, - getTestProvider, } from '@graphprotocol/indexer-common' import { CombinedError } from '@urql/core' import { GraphQLError } from 'graphql' +import { + allocateToNotPublishedDeployment, + createTestManagementClient, + invalidReallocateAction, + invalidUnallocateAction, + queuedAllocateAction, + subgraphDeployment1, + subgraphDeployment2, + subgraphDeployment3, + notPublishedSubgraphDeployment, +} from '../util' const QUEUE_ACTIONS_MUTATION = gql` mutation queueActions($actions: [ActionInput!]!) { @@ -64,6 +55,7 @@ const QUEUE_ACTIONS_MUTATION = gql` transaction failureReason status + protocolNetwork } } ` @@ -84,6 +76,7 @@ const APPROVE_ACTIONS_MUTATION = gql` transaction failureReason status + protocolNetwork } } ` @@ -104,6 +97,7 @@ const CANCEL_ACTIONS_MUTATION = gql` transaction failureReason status + protocolNetwork } } ` @@ -124,6 +118,7 @@ const UPDATE_ACTIONS_MUTATION = gql` transaction failureReason status + protocolNetwork } } ` @@ -148,6 +143,7 @@ const ACTIONS_QUERY = gql` transaction failureReason status + protocolNetwork } } ` @@ -157,13 +153,12 @@ const DELETE_ACTIONS_MUTATION = gql` deleteActions(actionIDs: $actionIDs) } ` +type ActionTestInput = Record async function actionInputToExpected( input: ActionInput, id: number, - // eslint-disable-next-line @typescript-eslint/no-explicit-any -): Promise<{ [key: string]: any }> { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const expected: Record = { ...input } +): Promise { + const expected: ActionTestInput = { ...input } expected.id = id for (const actionKey in Action.getAttributes()) { @@ -171,103 +166,21 @@ async function actionInputToExpected( expected[actionKey] = null } } - return expected -} -const defaults: IndexerManagementDefaults = { - globalIndexingRule: { - allocationAmount: parseGRT('100'), - parallelAllocations: 1, - requireSupported: true, - safety: true, - }, + // We expect the protocol network to be transformed to it's CAIP2-ID + // form for all inputs + if (input.protocolNetwork === 'goerli') { + expected.protocolNetwork = 'eip155:5' + } + + return expected } -const subgraphDeployment1 = 'Qmew9PZUJCoDzXqqU6vGyTENTKHrrN4dy5h94kertfudqy' -const subgraphDeployment2 = 'QmWq1pmnhEvx25qxpYYj9Yp6E1xMKMVoUjXVQBxUJmreSe' -const subgraphDeployment3 = 'QmRhH2nhNibDVPZmYqq3TUZZARZ77vgjYCvPNiGBCogtgM' -const notPublishedSubgraphDeployment = 'QmeqJ6hsdyk9dVbo1tvRgAxWrVS3rkERiEMsxzPShKLco6' - -const queuedAllocateAction = { - status: ActionStatus.QUEUED, - type: ActionType.ALLOCATE, - deploymentID: subgraphDeployment1, - amount: '10000', - force: false, - source: 'indexerAgent', - reason: 'indexingRule', - priority: 0, -} as ActionInput - -const allocateToNotPublishedDeployment = { - status: ActionStatus.QUEUED, - type: ActionType.ALLOCATE, - deploymentID: notPublishedSubgraphDeployment, - amount: '10000', - force: false, - source: 'indexerAgent', - reason: 'indexingRule', - priority: 0, -} as ActionInput - -const invalidUnallocateAction = { - status: ActionStatus.QUEUED, - type: ActionType.UNALLOCATE, - allocationID: '0x8f63930129e585c69482b56390a09b6b176f4a4c', - deploymentID: subgraphDeployment1, - amount: undefined, - poi: undefined, - force: false, - source: 'indexerAgent', - reason: 'indexingRule', - priority: 0, -} as ActionInput - -// const queuedReallocateAction = { -// status: ActionStatus.QUEUED, -// type: ActionType.REALLOCATE, -// allocationID: '0x8f63930129e585c69482b56390a09b6b176f4a4c', -// deploymentID: subgraphDeployment1, -// poi: undefined, -// amount: '27000', -// force: false, -// source: 'indexerAgent', -// reason: 'indexingRule', -// priority: 0, -// } as ActionInput - -const invalidReallocateAction = { - status: ActionStatus.QUEUED, - type: ActionType.REALLOCATE, - allocationID: '0x8f63930129e585c69482b56390a09b6b176f4a4c', - deploymentID: subgraphDeployment1, - poi: undefined, - amount: undefined, - force: false, - source: 'indexerAgent', - reason: 'indexingRule', - priority: 0, -} as ActionInput - -const indexNodeIDs = ['node_1'] - -let ethereum: ethers.providers.BaseProvider let sequelize: Sequelize let managementModels: IndexerManagementModels let queryFeeModels: QueryFeeModels -let address: string -let contracts: NetworkContracts let logger: Logger -let indexingStatusResolver: IndexingStatusResolver -let networkSubgraph: NetworkSubgraph let client: IndexerManagementClient -let transactionManager: TransactionManager -let wallet: Wallet -let epochSubgraph: EpochSubgraph -let networkMonitor: NetworkMonitor -let receiptCollector: AllocationReceiptCollector -let mockedSubgraphManager: SubgraphManager -let allocationManager: AllocationManager let metrics: Metrics // Make global Jest variables available @@ -276,104 +189,19 @@ declare const __DATABASE__: any declare const __LOG_LEVEL__: never const setup = async () => { - const statusEndpoint = 'http://localhost:8030/graphql' - const deploymentManagementEndpoint = 'http://localhost:8020/' - address = '0x90f8bf6a479f320ead074411a4b0e7944ea8c9c1' - sequelize = await connectDatabase(__DATABASE__) queryFeeModels = defineQueryFeeModels(sequelize) managementModels = defineIndexerManagementModels(sequelize) sequelize = await sequelize.sync({ force: true }) - ethereum = getTestProvider('goerli') - wallet = Wallet.createRandom() - contracts = await connectContracts(ethereum, 5) + metrics = createMetrics() + // Clearing the registry prevents duplicate metric registration in the default registry. + metrics.registry.clear() logger = createLogger({ name: 'Indexer API Client', async: false, level: __LOG_LEVEL__ ?? 'error', }) - - indexingStatusResolver = new IndexingStatusResolver({ - logger: logger, - statusEndpoint, - }) - networkSubgraph = await NetworkSubgraph.create({ - logger, - endpoint: - 'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli', - deployment: undefined, - }) - transactionManager = new TransactionManager( - ethereum, - wallet, - mutable(false), - mutable(true), - 240000, - 1.2, - 100 * 10 ** 9, - 0, - ) - - epochSubgraph = await EpochSubgraph.create( - 'https://api.thegraph.com/subgraphs/name/graphprotocol/goerli-epoch-block-oracle', - ) - - metrics = createMetrics() - receiptCollector = new AllocationReceiptCollector({ - logger, - metrics, - transactionManager: transactionManager, - models: queryFeeModels, - allocationExchange: contracts.allocationExchange, - collectEndpoint: 'http://localhost:8030/', - voucherRedemptionThreshold: BigNumber.from(200), - voucherRedemptionBatchThreshold: BigNumber.from(2000), - voucherRedemptionMaxBatchSize: 100, - }) - - networkMonitor = new NetworkMonitor( - resolveChainId('goerli'), - contracts, - toAddress('0xc61127cdfb5380df4214b0200b9a07c7c49d34f9'), - logger, - indexingStatusResolver, - networkSubgraph, - ethereum, - epochSubgraph, - ) - - client = await createIndexerManagementClient({ - models: managementModels, - address, - contracts, - indexingStatusResolver, - indexNodeIDs, - deploymentManagementEndpoint, - networkSubgraph, - receiptCollector, - transactionManager, - networkMonitor, - logger, - defaults, - features: { - injectDai: true, - }, - }) - mockedSubgraphManager = new SubgraphManager( - 'fake endpoint', - ['fake node id'], - indexingStatusResolver, - ) - allocationManager = new AllocationManager( - contracts, - logger, - address, - managementModels, - networkMonitor, - receiptCollector, - mockedSubgraphManager, - transactionManager, - ) + client = await createTestManagementClient(__DATABASE__, logger, true, metrics) } const setupEach = async () => { @@ -395,11 +223,11 @@ const teardownEach = async () => { } const teardownAll = async () => { - metrics.registry.clear() await sequelize.drop({}) } describe('Actions', () => { + jest.setTimeout(60_000) beforeAll(setup) beforeEach(setupEach) afterEach(teardownEach) @@ -666,6 +494,7 @@ describe('Actions', () => { ['source', 'String'], ['reason', 'String'], ['priority', 'Int'], + ['protocolNetwork', 'String'], ] const graphQLErrors = expectedFieldNamesAndTypes.map( ([fieldName, fieldType]) => @@ -682,7 +511,8 @@ describe('Actions', () => { test('Reject action with invalid params for action type', async () => { const inputAction = invalidReallocateAction - + const expected = { ...inputAction, protocolNetwork: 'eip155:5' } + const fields = JSON.stringify(expected) await expect( client.mutation(QUEUE_ACTIONS_MUTATION, { actions: [inputAction] }).toPromise(), ).resolves.toHaveProperty( @@ -690,7 +520,7 @@ describe('Actions', () => { new CombinedError({ graphQLErrors: [ new GraphQLError( - 'Failed to queue action: Invalid action input, actionInput: {"status":"queued","type":"reallocate","deploymentID":"Qmew9PZUJCoDzXqqU6vGyTENTKHrrN4dy5h94kertfudqy","allocationID":"0x8f63930129e585c69482b56390a09b6b176f4a4c","force":false,"source":"indexerAgent","reason":"indexingRule","priority":0}', + `Failed to queue action: Invalid action input, actionInput: ${fields}`, ), ], }), @@ -897,6 +727,8 @@ describe('Actions', () => { source: 'indexerAgent', reason: 'indexingRule', priority: 0, + // When writing directly to the database, `protocolNetwork` must be in the CAIP2-ID format. + protocolNetwork: 'eip155:5', } as ActionInput const proposedAction = { @@ -907,6 +739,7 @@ describe('Actions', () => { source: 'indexerAgent', reason: 'indexingRule', priority: 0, + protocolNetwork: 'goerli', } as ActionInput await managementModels.Action.create(failedAction, { @@ -914,9 +747,11 @@ describe('Actions', () => { returning: true, }) - await expect( - client.mutation(QUEUE_ACTIONS_MUTATION, { actions: [proposedAction] }).toPromise(), - ).resolves.toHaveProperty( + const result = await client + .mutation(QUEUE_ACTIONS_MUTATION, { actions: [proposedAction] }) + .toPromise() + + expect(result).toHaveProperty( 'error', new CombinedError({ graphQLErrors: [ @@ -947,6 +782,8 @@ describe('Actions', () => { source: 'indexerAgent', reason: 'indexingRule', priority: 0, + // When writing directly to the database, `protocolNetwork` must be in the CAIP2-ID format. + protocolNetwork: 'eip155:5', } as ActionInput const proposedAction = { @@ -957,6 +794,7 @@ describe('Actions', () => { source: 'indexerAgent', reason: 'indexingRule', priority: 0, + protocolNetwork: 'goerli', } as ActionInput await managementModels.Action.create(successfulAction, { @@ -997,6 +835,8 @@ describe('Actions', () => { source: 'indexerAgent', reason: 'indexingRule', priority: 0, + // When writing directly to the database, `protocolNetwork` must be in the CAIP2-ID format. + protocolNetwork: 'eip155:5', } as ActionInput const queuedAllocateAction = { @@ -1008,6 +848,7 @@ describe('Actions', () => { source: 'indexerAgent', reason: 'indexingRule', priority: 0, + protocolNetwork: 'goerli', } as ActionInput await managementModels.Action.create(queuedUnallocateAction, { @@ -1049,72 +890,3 @@ describe('Actions', () => { ).resolves.toHaveProperty('data.updateActions', updatedExpecteds) }) }) - -describe('Allocation Manager', () => { - beforeAll(setup) - beforeEach(setupEach) - afterEach(teardownEach) - afterAll(teardownAll) - - // We have been rate-limited on CI as this test uses RPC providers, - // so we set its timeout to a higher value than usual. - jest.setTimeout(30_000) - - // Reuse an existing allocation with 25 sextillion allocated GRT - const allocationID = '0x96737b6a31f40edaf96c567efbb98935aa906ab9' - - // Redefine test actions to use that allocation ID - const unallocateAction = { - ...invalidUnallocateAction, - poi: '0x1', // non-zero POI - allocationID, - } - const reallocateAction = { - ...invalidReallocateAction, - amount: '10000', - allocationID, - } - - const actions = [queuedAllocateAction, unallocateAction, reallocateAction] as Action[] - - test('resolveActionDelta() correctly calculates token balances for array of actions', async () => { - const mapper = (x: Action) => allocationManager.resolveActionDelta(x) - const balances = await Promise.all(actions.map(mapper)) - - console.table(balances) - - const allocate = balances[0] - const unallocate = balances[1] - const reallocate = balances[2] - - // Allocate test action - expect(allocate.action.type).toBe(ActionType.ALLOCATE) - expect(allocate.allocates).toStrictEqual(parseGRT('10000')) - expect(allocate.rewards.isZero()).toBeTruthy() - expect(allocate.unallocates.isZero()).toBeTruthy() - expect(allocate.balance).toStrictEqual(parseGRT('10000')) - - // Unallocate test action - expect(unallocate.action.type).toBe(ActionType.UNALLOCATE) - expect(unallocate.allocates.isZero()).toBeTruthy() - expect(unallocate.rewards.isZero()).toBeFalsy() - expect(unallocate.unallocates).toStrictEqual(parseGRT('25000')) - expect(unallocate.balance).toStrictEqual( - unallocate.allocates.sub(unallocate.unallocates).sub(unallocate.rewards), - ) - - // This Reallocate test Action intentionally uses a null or zeroed POI, so it should not accrue rewards. - expect(reallocate.action.type).toBe(ActionType.REALLOCATE) - expect(reallocate.allocates).toStrictEqual(parseGRT('10000')) - expect(reallocate.rewards.isZero()).toBeTruthy() - expect(reallocate.unallocates).toStrictEqual(parseGRT('25000')) - expect(reallocate.balance).toStrictEqual(parseGRT('-15000')) - }) - - test('validateActionBatchFeasibility() validates and correctly sorts actions based on net token balance', async () => { - const reordered = await allocationManager.validateActionBatchFeasibilty(actions) - expect(reordered[0]).toStrictEqual(unallocateAction) - expect(reordered[1]).toStrictEqual(reallocateAction) - expect(reordered[2]).toStrictEqual(queuedAllocateAction) - }) -}) diff --git a/packages/indexer-common/src/indexer-management/__tests__/resolvers/cost-models.ts b/packages/indexer-common/src/indexer-management/__tests__/resolvers/cost-models.test.ts similarity index 91% rename from packages/indexer-common/src/indexer-management/__tests__/resolvers/cost-models.ts rename to packages/indexer-common/src/indexer-management/__tests__/resolvers/cost-models.test.ts index 13c14ca62..035264e76 100644 --- a/packages/indexer-common/src/indexer-management/__tests__/resolvers/cost-models.ts +++ b/packages/indexer-common/src/indexer-management/__tests__/resolvers/cost-models.test.ts @@ -1,28 +1,17 @@ import { Sequelize } from 'sequelize' import gql from 'graphql-tag' -import { ethers } from 'ethers' import { - connectDatabase, - connectContracts, createLogger, Logger, - NetworkContracts, - parseGRT, + connectDatabase, + createMetrics, } from '@graphprotocol/common-ts' - -import { - createIndexerManagementClient, - IndexerManagementClient, - IndexerManagementDefaults, -} from '../../client' +import { IndexerManagementClient } from '../../client' import { defineIndexerManagementModels, IndexerManagementModels } from '../../models' import { CombinedError } from '@urql/core' import { GraphQLError } from 'graphql' -import { - IndexingStatusResolver, - NetworkSubgraph, - getTestProvider, -} from '@graphprotocol/indexer-common' +import { createTestManagementClient } from '../util' +import { defineQueryFeeModels } from '../../../query-fees/models' // Make global Jest variable available // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -81,61 +70,35 @@ const GET_COST_MODELS_DEPLOYMENTS_QUERY = gql` let sequelize: Sequelize let models: IndexerManagementModels -let address: string -let contracts: NetworkContracts let logger: Logger -let indexNodeIDs: string[] -let statusEndpoint: string -let indexingStatusResolver: IndexingStatusResolver -let networkSubgraph: NetworkSubgraph let client: IndexerManagementClient - -const defaults: IndexerManagementDefaults = { - globalIndexingRule: { - allocationAmount: parseGRT('100'), - parallelAllocations: 1, - }, -} +const metrics = createMetrics() const setupAll = async () => { + logger = createLogger({ + name: 'Indexer API Client', + async: false, + level: __LOG_LEVEL__ ?? 'error', + }) + // Spin up db sequelize = await connectDatabase(__DATABASE__) models = defineIndexerManagementModels(sequelize) - address = '0xtest' - contracts = await connectContracts(getTestProvider('goerli'), 5) + defineQueryFeeModels(sequelize) sequelize = await sequelize.sync({ force: true }) logger = createLogger({ name: 'Indexer API Client', async: false, level: __LOG_LEVEL__ ?? 'error', }) - indexNodeIDs = ['node_1'] - statusEndpoint = 'http://localhost:8030/graphql' - indexingStatusResolver = new IndexingStatusResolver({ - logger: logger, - statusEndpoint, - }) - networkSubgraph = await NetworkSubgraph.create({ - logger, - endpoint: - 'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli', - deployment: undefined, - }) - client = await createIndexerManagementClient({ - models, - address, - contracts, - indexingStatusResolver, - indexNodeIDs, - deploymentManagementEndpoint: statusEndpoint, - networkSubgraph, + client = await createTestManagementClient( + __DATABASE__, logger, - defaults, - features: { - injectDai: true, - }, - }) + true, + metrics, + 'eip155:1', // Override with mainnet to enable the Cost Model feature + ) } const teardownAll = async () => { @@ -154,6 +117,7 @@ const teardownEach = async () => { } describe('Cost models', () => { + jest.setTimeout(60_000) beforeAll(setupAll) beforeEach(setupEach) afterEach(teardownEach) @@ -755,37 +719,33 @@ describe('Feature: Inject $DAI variable', () => { }) test('If feature is disabled, $DAI variable is not preserved', async () => { - // Recreate client with features.injectDai = false - client = await createIndexerManagementClient({ - models, - address, - contracts, - indexingStatusResolver, - indexNodeIDs, - deploymentManagementEndpoint: statusEndpoint, - networkSubgraph, + const clientNoInjectDai = await createTestManagementClient( + __DATABASE__, logger, - defaults, - features: { - injectDai: false, - }, - }) + false, + metrics, + 'eip155:1', // Override with mainnet to enable the Cost Model feature + ) + const initial = { deployment: '0x0000000000000000000000000000000000000000000000000000000000000000', model: 'query { votes } => 10 * $n;', variables: JSON.stringify({ n: 5, DAI: '10.0' }), } - await client.mutation(SET_COST_MODEL_MUTATION, { costModel: initial }).toPromise() + await clientNoInjectDai + .mutation(SET_COST_MODEL_MUTATION, { costModel: initial }) + .toPromise() const update = { deployment: '0x0000000000000000000000000000000000000000000000000000000000000000', model: initial.model, variables: JSON.stringify({}), } - await client.mutation(SET_COST_MODEL_MUTATION, { costModel: update }).toPromise() - await expect(client.query(GET_COST_MODELS_QUERY).toPromise()).resolves.toHaveProperty( - 'data.costModels', - [update], - ) + await clientNoInjectDai + .mutation(SET_COST_MODEL_MUTATION, { costModel: update }) + .toPromise() + await expect( + clientNoInjectDai.query(GET_COST_MODELS_QUERY).toPromise(), + ).resolves.toHaveProperty('data.costModels', [update]) }) }) diff --git a/packages/indexer-common/src/indexer-management/__tests__/resolvers/indexing-rules.ts b/packages/indexer-common/src/indexer-management/__tests__/resolvers/indexing-rules.test.ts similarity index 76% rename from packages/indexer-common/src/indexer-management/__tests__/resolvers/indexing-rules.ts rename to packages/indexer-common/src/indexer-management/__tests__/resolvers/indexing-rules.test.ts index d77c72402..7de22746b 100644 --- a/packages/indexer-common/src/indexer-management/__tests__/resolvers/indexing-rules.ts +++ b/packages/indexer-common/src/indexer-management/__tests__/resolvers/indexing-rules.test.ts @@ -1,20 +1,12 @@ import { Sequelize } from 'sequelize' import gql from 'graphql-tag' -import { ethers } from 'ethers' import { connectDatabase, - connectContracts, createLogger, Logger, - NetworkContracts, - parseGRT, + createMetrics, } from '@graphprotocol/common-ts' - -import { - createIndexerManagementClient, - IndexerManagementClient, - IndexerManagementDefaults, -} from '../../client' +import { IndexerManagementClient } from '../../client' import { defineIndexerManagementModels, IndexerManagementModels, @@ -22,12 +14,12 @@ import { INDEXING_RULE_GLOBAL, } from '../../models' import { - IndexingStatusResolver, - NetworkSubgraph, SubgraphIdentifierType, - getTestProvider, + defineQueryFeeModels, } from '@graphprotocol/indexer-common' +import { createTestManagementClient, defaults } from '../util' + // Make global Jest variable available // eslint-disable-next-line @typescript-eslint/no-explicit-any declare const __DATABASE__: any @@ -52,24 +44,25 @@ const SET_INDEXING_RULE_MUTATION = gql` decisionBasis requireSupported safety + protocolNetwork } } ` const DELETE_INDEXING_RULE_MUTATION = gql` - mutation deleteIndexingRule($identifier: String!) { + mutation deleteIndexingRule($identifier: IndexingRuleIdentifier!) { deleteIndexingRule(identifier: $identifier) } ` const DELETE_INDEXING_RULES_MUTATION = gql` - mutation deleteIndexingRules($identifiers: [String!]!) { + mutation deleteIndexingRules($identifiers: [IndexingRuleIdentifier!]!) { deleteIndexingRules(identifiers: $identifiers) } ` const INDEXING_RULE_QUERY = gql` - query indexingRule($identifier: String!, $merged: Boolean!) { + query indexingRule($identifier: IndexingRuleIdentifier!, $merged: Boolean!) { indexingRule(identifier: $identifier, merged: $merged) { identifier identifierType @@ -86,13 +79,14 @@ const INDEXING_RULE_QUERY = gql` decisionBasis requireSupported safety + protocolNetwork } } ` const INDEXING_RULES_QUERY = gql` - query indexingRules($merged: Boolean!) { - indexingRules(merged: $merged) { + query indexingRules($merged: Boolean!, $protocolNetwork: String!) { + indexingRules(merged: $merged, protocolNetwork: $protocolNetwork) { identifier identifierType allocationAmount @@ -108,66 +102,29 @@ const INDEXING_RULES_QUERY = gql` decisionBasis requireSupported safety + protocolNetwork } } ` let sequelize: Sequelize let models: IndexerManagementModels -let address: string -let contracts: NetworkContracts let logger: Logger -let indexingStatusResolver: IndexingStatusResolver -let networkSubgraph: NetworkSubgraph let client: IndexerManagementClient - -const defaults: IndexerManagementDefaults = { - globalIndexingRule: { - allocationAmount: parseGRT('100'), - parallelAllocations: 1, - requireSupported: true, - safety: true, - }, -} +const metrics = createMetrics() const setupAll = async () => { - // Spin up db sequelize = await connectDatabase(__DATABASE__) models = defineIndexerManagementModels(sequelize) - address = '0xtest' - contracts = await connectContracts(getTestProvider('goerli'), 5) + defineQueryFeeModels(sequelize) await sequelize.sync({ force: true }) + logger = createLogger({ name: 'Indexer API Client', async: false, level: __LOG_LEVEL__ ?? 'error', }) - const statusEndpoint = 'http://localhost:8030/graphql' - indexingStatusResolver = new IndexingStatusResolver({ - logger: logger, - statusEndpoint, - }) - networkSubgraph = await NetworkSubgraph.create({ - logger, - endpoint: - 'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli', - deployment: undefined, - }) - const indexNodeIDs = ['node_1'] - client = await createIndexerManagementClient({ - models, - address, - contracts, - indexingStatusResolver, - indexNodeIDs, - deploymentManagementEndpoint: statusEndpoint, - networkSubgraph, - logger, - defaults, - features: { - injectDai: true, - }, - }) + client = await createTestManagementClient(__DATABASE__, logger, true, metrics) } const teardownAll = async () => { @@ -186,6 +143,7 @@ const teardownEach = async () => { } describe('Indexing rules', () => { + jest.setTimeout(60_000) beforeAll(setupAll) beforeEach(setupEach) afterEach(teardownEach) @@ -196,6 +154,7 @@ describe('Indexing rules', () => { identifier: INDEXING_RULE_GLOBAL, identifierType: SubgraphIdentifierType.GROUP, allocationAmount: '1000', + protocolNetwork: 'goerli', } const expected = { @@ -212,6 +171,7 @@ describe('Indexing rules', () => { decisionBasis: IndexingDecisionBasis.RULES, requireSupported: true, safety: true, + protocolNetwork: 'eip155:5', } // Update the rule and ensure the right data is returned @@ -220,11 +180,12 @@ describe('Indexing rules', () => { ).resolves.toHaveProperty('data.setIndexingRule', expected) // Query the rule to make sure it's updated in the db - await expect( - client - .query(INDEXING_RULE_QUERY, { identifier: INDEXING_RULE_GLOBAL, merged: false }) - .toPromise(), - ).resolves.toHaveProperty('data.indexingRule', expected) + const ruleIdentifier = { identifier: INDEXING_RULE_GLOBAL, protocolNetwork: 'goerli' } + + const result = await client + .query(INDEXING_RULE_QUERY, { identifier: ruleIdentifier, merged: false }) + .toPromise() + expect(result).toHaveProperty('data.indexingRule', expected) }) test('Set and get global rule (complete)', async () => { @@ -244,10 +205,12 @@ describe('Indexing rules', () => { decisionBasis: IndexingDecisionBasis.RULES, requireSupported: true, safety: true, + protocolNetwork: 'goerli', } const expected = { ...input, + protocolNetwork: 'eip155:5', } // Update the rule @@ -256,9 +219,10 @@ describe('Indexing rules', () => { ).resolves.toHaveProperty('data.setIndexingRule', expected) // Query the rule to make sure it's updated in the db + const ruleIdentifier = { identifier: INDEXING_RULE_GLOBAL, protocolNetwork: 'goerli' } await expect( client - .query(INDEXING_RULE_QUERY, { identifier: INDEXING_RULE_GLOBAL, merged: false }) + .query(INDEXING_RULE_QUERY, { identifier: ruleIdentifier, merged: false }) .toPromise(), ).resolves.toHaveProperty('data.indexingRule', expected) }) @@ -269,6 +233,7 @@ describe('Indexing rules', () => { identifierType: SubgraphIdentifierType.GROUP, allocationAmount: '1', minSignal: '2', + protocolNetwork: 'goerli', } const original = { @@ -284,6 +249,7 @@ describe('Indexing rules', () => { decisionBasis: IndexingDecisionBasis.RULES, requireSupported: true, safety: true, + protocolNetwork: 'eip155:5', } // Write the original @@ -299,11 +265,13 @@ describe('Indexing rules', () => { decisionBasis: IndexingDecisionBasis.OFFCHAIN, autoRenewal: true, safety: false, + protocolNetwork: 'goerli', } const expected = { ...original, ...update, + protocolNetwork: 'eip155:5', } // Update the rule @@ -312,20 +280,23 @@ describe('Indexing rules', () => { ).resolves.toHaveProperty('data.setIndexingRule', expected) // Query the rule to make sure it's updated in the db + const ruleIdentifier = { identifier: INDEXING_RULE_GLOBAL, protocolNetwork: 'goerli' } await expect( client - .query(INDEXING_RULE_QUERY, { identifier: INDEXING_RULE_GLOBAL, merged: false }) + .query(INDEXING_RULE_QUERY, { identifier: ruleIdentifier, merged: false }) .toPromise(), ).resolves.toHaveProperty('data.indexingRule', expected) }) test('Set and get deployment rule (partial update)', async () => { + const originalIdentifier = 'QmZSJPm74tvhgr8uzhqvyQm2J6YSbUEj4nF6j8WxxUQLsC' const originalInput = { - identifier: 'QmZSJPm74tvhgr8uzhqvyQm2J6YSbUEj4nF6j8WxxUQLsC', + identifier: originalIdentifier, identifierType: SubgraphIdentifierType.DEPLOYMENT, allocationAmount: '1', minSignal: '2', decisionBasis: IndexingDecisionBasis.OFFCHAIN, + protocolNetwork: 'goerli', } const original = { @@ -341,6 +312,7 @@ describe('Indexing rules', () => { decisionBasis: IndexingDecisionBasis.OFFCHAIN, requireSupported: true, safety: true, + protocolNetwork: 'eip155:5', } // Write the original @@ -349,7 +321,7 @@ describe('Indexing rules', () => { ).resolves.toHaveProperty('data.setIndexingRule', original) const update = { - identifier: 'QmZSJPm74tvhgr8uzhqvyQm2J6YSbUEj4nF6j8WxxUQLsC', + identifier: originalIdentifier, identifierType: SubgraphIdentifierType.DEPLOYMENT, allocationAmount: null, maxSignal: '3', @@ -358,11 +330,13 @@ describe('Indexing rules', () => { autoRenewal: false, requireSupported: false, safety: false, + protocolNetwork: 'goerli', } const expected = { ...original, ...update, + protocolNetwork: 'eip155:5', } // Update the rule @@ -371,10 +345,14 @@ describe('Indexing rules', () => { ).resolves.toHaveProperty('data.setIndexingRule', expected) // Query the rule to make sure it's updated in the db + const ruleIdentifier = { + identifier: update.identifier, + protocolNetwork: update.protocolNetwork, + } await expect( client .query(INDEXING_RULE_QUERY, { - identifier: 'QmZSJPm74tvhgr8uzhqvyQm2J6YSbUEj4nF6j8WxxUQLsC', + identifier: ruleIdentifier, merged: false, }) .toPromise(), @@ -386,14 +364,16 @@ describe('Indexing rules', () => { allocationLifetime: null, decisionBasis: IndexingDecisionBasis.NEVER, autoRenewal: true, + protocolNetwork: 'goerli', } const expectedAgain = { ...original, ...update, ...updateAgain, + protocolNetwork: 'eip155:5', } - expectedAgain.identifier = 'QmZSJPm74tvhgr8uzhqvyQm2J6YSbUEj4nF6j8WxxUQLsC' + expectedAgain.identifier = originalIdentifier // Update the rule await expect( @@ -401,10 +381,14 @@ describe('Indexing rules', () => { ).resolves.toHaveProperty('data.setIndexingRule', expectedAgain) // Query the rule to make sure it's updated in the db + const ruleIdentifierAgain = { + identifier: originalIdentifier, + protocolNetwork: updateAgain.protocolNetwork, + } await expect( client .query(INDEXING_RULE_QUERY, { - identifier: 'QmZSJPm74tvhgr8uzhqvyQm2J6YSbUEj4nF6j8WxxUQLsC', + identifier: ruleIdentifierAgain, merged: false, }) .toPromise(), @@ -418,6 +402,7 @@ describe('Indexing rules', () => { allocationAmount: '1', minSignal: '1', decisionBasis: IndexingDecisionBasis.NEVER, + protocolNetwork: 'goerli', } const deploymentInput = { @@ -429,6 +414,7 @@ describe('Indexing rules', () => { requireSupported: false, autoRenewal: false, safety: true, + protocolNetwork: 'goerli', } const globalExpected = { @@ -444,6 +430,7 @@ describe('Indexing rules', () => { decisionBasis: IndexingDecisionBasis.NEVER, requireSupported: true, safety: true, + protocolNetwork: 'eip155:5', } const deploymentExpected = { @@ -459,6 +446,7 @@ describe('Indexing rules', () => { decisionBasis: IndexingDecisionBasis.OFFCHAIN, requireSupported: false, safety: true, + protocolNetwork: 'eip155:5', } deploymentExpected.identifier = 'QmZSJPm74tvhgr8uzhqvyQm2J6YSbUEj4nF6j8WxxUQLsC' @@ -471,21 +459,28 @@ describe('Indexing rules', () => { ).resolves.toHaveProperty('data.setIndexingRule', deploymentExpected) // Query the global rule + const globalRuleIdentifier = { + identifier: INDEXING_RULE_GLOBAL, + protocolNetwork: 'goerli', + } await expect( client .query(INDEXING_RULE_QUERY, { - identifier: INDEXING_RULE_GLOBAL, + identifier: globalRuleIdentifier, merged: false, }) .toPromise(), ).resolves.toHaveProperty('data.indexingRule', globalExpected) // Query the rule for the deployment + const deploymentRuleIdentifier = { + identifier: '0xa4e311bfa7edabed7b31d93e0b3e751659669852ef46adbedd44dc2454db4bf3', + protocolNetwork: 'goerli', + } await expect( client .query(INDEXING_RULE_QUERY, { - identifier: - '0xa4e311bfa7edabed7b31d93e0b3e751659669852ef46adbedd44dc2454db4bf3', + identifier: deploymentRuleIdentifier, merged: false, }) .toPromise(), @@ -493,7 +488,9 @@ describe('Indexing rules', () => { // Query all rules together await expect( - client.query(INDEXING_RULES_QUERY, { merged: false }).toPromise(), + client + .query(INDEXING_RULES_QUERY, { merged: false, protocolNetwork: 'goerli' }) + .toPromise(), ).resolves.toHaveProperty('data.indexingRules', [globalExpected, deploymentExpected]) }) @@ -505,6 +502,7 @@ describe('Indexing rules', () => { minSignal: '2', allocationLifetime: 20, autoRenewal: false, + protocolNetwork: 'goerli', } const expected = { @@ -520,6 +518,7 @@ describe('Indexing rules', () => { decisionBasis: IndexingDecisionBasis.RULES, requireSupported: true, safety: true, + protocolNetwork: 'eip155:5', } // Write the rule @@ -529,19 +528,26 @@ describe('Indexing rules', () => { // Query all rules await expect( - client.query(INDEXING_RULES_QUERY, { merged: false }).toPromise(), + client + .query(INDEXING_RULES_QUERY, { merged: false, protocolNetwork: 'goerli' }) + .toPromise(), ).resolves.toHaveProperty('data.indexingRules', [expected]) // Delete the rule + const ruleIdentifier = { identifier: expected.identifier, protocolNetwork: 'goerli' } await expect( client - .mutation(DELETE_INDEXING_RULE_MUTATION, { identifier: expected.identifier }) + .mutation(DELETE_INDEXING_RULE_MUTATION, { + identifier: ruleIdentifier, + }) .toPromise(), ).resolves.toHaveProperty('data.deleteIndexingRule', true) // Query all rules together await expect( - client.query(INDEXING_RULES_QUERY, { merged: false }).toPromise(), + client + .query(INDEXING_RULES_QUERY, { merged: false, protocolNetwork: 'goerli' }) + .toPromise(), ).resolves.toHaveProperty('data.indexingRules', []) }) @@ -552,6 +558,7 @@ describe('Indexing rules', () => { allocationAmount: '1', requireSupported: true, safety: true, + protocolNetwork: 'goerli', } const expectedBefore = { @@ -566,6 +573,7 @@ describe('Indexing rules', () => { minAverageQueryFees: null, custom: null, decisionBasis: IndexingDecisionBasis.RULES, + protocolNetwork: 'eip155:5', } // Write the rule @@ -575,7 +583,9 @@ describe('Indexing rules', () => { // Query all rules await expect( - client.query(INDEXING_RULES_QUERY, { merged: false }).toPromise(), + client + .query(INDEXING_RULES_QUERY, { merged: false, protocolNetwork: 'goerli' }) + .toPromise(), ).resolves.toHaveProperty('data.indexingRules', [expectedBefore]) // Clear the allocationAmount field @@ -592,7 +602,9 @@ describe('Indexing rules', () => { // Query the rules again to see that the update went through await expect( - client.query(INDEXING_RULES_QUERY, { merged: false }).toPromise(), + client + .query(INDEXING_RULES_QUERY, { merged: false, protocolNetwork: 'goerli' }) + .toPromise(), ).resolves.toHaveProperty('data.indexingRules', [ { ...expectedBefore, allocationAmount: null }, ]) @@ -610,6 +622,7 @@ describe('Indexing rules', () => { requireSupported: true, autoRenewal: true, safety: false, + protocolNetwork: 'goerli', } const deploymentInput = { @@ -622,6 +635,7 @@ describe('Indexing rules', () => { autoRenewal: false, requireSupported: false, safety: true, + protocolNetwork: 'goerli', } const globalExpected = { @@ -636,6 +650,7 @@ describe('Indexing rules', () => { decisionBasis: IndexingDecisionBasis.NEVER, requireSupported: true, safety: false, + protocolNetwork: 'eip155:5', } const deploymentExpected = { @@ -651,6 +666,7 @@ describe('Indexing rules', () => { decisionBasis: IndexingDecisionBasis.OFFCHAIN, requireSupported: false, safety: true, + protocolNetwork: 'eip155:5', } const deploymentMergedExpected = { @@ -666,6 +682,7 @@ describe('Indexing rules', () => { decisionBasis: IndexingDecisionBasis.OFFCHAIN, requireSupported: false, safety: true, + protocolNetwork: 'eip155:5', } // Write the orginals @@ -677,20 +694,28 @@ describe('Indexing rules', () => { ).resolves.toHaveProperty('data.setIndexingRule', deploymentExpected) // Query the global rule + const globalRuleIdentifier = { + identifier: INDEXING_RULE_GLOBAL, + protocolNetwork: 'goerli', + } await expect( client .query(INDEXING_RULE_QUERY, { - identifier: INDEXING_RULE_GLOBAL, + identifier: globalRuleIdentifier, merged: false, }) .toPromise(), ).resolves.toHaveProperty('data.indexingRule', globalExpected) // Query the rule for the deployment merged with the global rule + const ruleIdentifier = { + identifier: 'QmZSJPm74tvhgr8uzhqvyQm2J6YSbUEj4nF6j8WxxUQLsC', + protocolNetwork: 'goerli', + } await expect( client .query(INDEXING_RULE_QUERY, { - identifier: 'QmZSJPm74tvhgr8uzhqvyQm2J6YSbUEj4nF6j8WxxUQLsC', + identifier: ruleIdentifier, merged: true, }) .toPromise(), @@ -698,12 +723,16 @@ describe('Indexing rules', () => { // Query all rules together (without merging) await expect( - client.query(INDEXING_RULES_QUERY, { merged: false }).toPromise(), + client + .query(INDEXING_RULES_QUERY, { merged: false, protocolNetwork: 'goerli' }) + .toPromise(), ).resolves.toHaveProperty('data.indexingRules', [globalExpected, deploymentExpected]) // Query all rules together (with merging) await expect( - client.query(INDEXING_RULES_QUERY, { merged: true }).toPromise(), + client + .query(INDEXING_RULES_QUERY, { merged: true, protocolNetwork: 'goerli' }) + .toPromise(), ).resolves.toHaveProperty('data.indexingRules', [ globalExpected, deploymentMergedExpected, @@ -718,27 +747,34 @@ describe('Indexing rules', () => { minSignal: '1', decisionBasis: IndexingDecisionBasis.NEVER, minAverageQueryFees: '1', + protocolNetwork: 'goerli', } await client.mutation(SET_INDEXING_RULE_MUTATION, { rule: globalInput }).toPromise() + const globalRuleIdentifier = { + identifier: INDEXING_RULE_GLOBAL, + protocolNetwork: 'goerli', + } await expect( client .mutation(DELETE_INDEXING_RULE_MUTATION, { - identifier: 'global', + identifier: globalRuleIdentifier, }) .toPromise(), ).resolves.toHaveProperty('data.deleteIndexingRule', true) await expect( - client.query(INDEXING_RULES_QUERY, { merged: false }).toPromise(), + client + .query(INDEXING_RULES_QUERY, { merged: false, protocolNetwork: 'goerli' }) + .toPromise(), ).resolves.toHaveProperty('data.indexingRules', [ { ...defaults.globalIndexingRule, allocationAmount: defaults.globalIndexingRule.allocationAmount.toString(), custom: null, decisionBasis: 'rules', - identifier: 'global', + identifier: INDEXING_RULE_GLOBAL, identifierType: SubgraphIdentifierType.GROUP, allocationLifetime: null, autoRenewal: true, @@ -747,6 +783,7 @@ describe('Indexing rules', () => { minAverageQueryFees: null, minSignal: null, minStake: null, + protocolNetwork: 'eip155:5', }, ]) }) @@ -761,6 +798,7 @@ describe('Indexing rules', () => { minAverageQueryFees: '1', requireSupported: false, safety: false, + protocolNetwork: 'goerli', } const deploymentInput = { @@ -770,6 +808,7 @@ describe('Indexing rules', () => { minSignal: '2', requireSupported: true, safety: true, + protocolNetwork: 'goerli', } await client.mutation(SET_INDEXING_RULE_MUTATION, { rule: globalInput }).toPromise() @@ -777,26 +816,34 @@ describe('Indexing rules', () => { .mutation(SET_INDEXING_RULE_MUTATION, { rule: deploymentInput }) .toPromise() + const globalRuleIdentifier = { + identifier: INDEXING_RULE_GLOBAL, + protocolNetwork: 'goerli', + } + const deploymentRuleIdentifier = { + identifier: '0xa4e311bfa7edabed7b31d93e0b3e751659669852ef46adbedd44dc2454db4bf3', + protocolNetwork: 'goerli', + } + await expect( client .mutation(DELETE_INDEXING_RULES_MUTATION, { - identifiers: [ - 'global', - '0xa4e311bfa7edabed7b31d93e0b3e751659669852ef46adbedd44dc2454db4bf3', - ], + identifiers: [globalRuleIdentifier, deploymentRuleIdentifier], }) .toPromise(), ).resolves.toHaveProperty('data.deleteIndexingRules', true) await expect( - client.query(INDEXING_RULES_QUERY, { merged: false }).toPromise(), + client + .query(INDEXING_RULES_QUERY, { merged: false, protocolNetwork: 'goerli' }) + .toPromise(), ).resolves.toHaveProperty('data.indexingRules', [ { ...defaults.globalIndexingRule, allocationAmount: defaults.globalIndexingRule.allocationAmount.toString(), custom: null, decisionBasis: 'rules', - identifier: 'global', + identifier: INDEXING_RULE_GLOBAL, identifierType: SubgraphIdentifierType.GROUP, allocationLifetime: null, autoRenewal: true, @@ -807,7 +854,33 @@ describe('Indexing rules', () => { minStake: null, requireSupported: true, safety: true, + protocolNetwork: 'eip155:5', }, ]) }) + test('Invalid protocolNetwork value prevents rule creation', async () => { + const deploymentInput = { + identifier: 'QmZSJPm74tvhgr8uzhqvyQm2J6YSbUEj4nF6j8WxxUQLsC', + identifierType: SubgraphIdentifierType.DEPLOYMENT, + allocationAmount: '1', + minSignal: '2', + requireSupported: true, + safety: true, + protocolNetwork: 'unsupported', + } + + const result = await client + .mutation(SET_INDEXING_RULE_MUTATION, { rule: deploymentInput }) + .toPromise() + + // Mutation must not succeed + expect(result).toHaveProperty('data', null) + + // Must not create any Rule in the database + + const rows = await client + .query(INDEXING_RULES_QUERY, { merged: false, protocolNetwork: 'goerli' }) + .toPromise() + expect(rows.data.indexingRules).toEqual([]) + }) }) diff --git a/packages/indexer-common/src/indexer-management/__tests__/resolvers/poi-disputes.ts b/packages/indexer-common/src/indexer-management/__tests__/resolvers/poi-disputes.test.ts similarity index 69% rename from packages/indexer-common/src/indexer-management/__tests__/resolvers/poi-disputes.ts rename to packages/indexer-common/src/indexer-management/__tests__/resolvers/poi-disputes.test.ts index 03c3ac8a0..86932d136 100644 --- a/packages/indexer-common/src/indexer-management/__tests__/resolvers/poi-disputes.ts +++ b/packages/indexer-common/src/indexer-management/__tests__/resolvers/poi-disputes.test.ts @@ -1,30 +1,19 @@ import { Sequelize } from 'sequelize' import gql from 'graphql-tag' -import { ethers } from 'ethers' import { + createMetrics, connectDatabase, - connectContracts, createLogger, Logger, - NetworkContracts, - parseGRT, } from '@graphprotocol/common-ts' - -import { - createIndexerManagementClient, - IndexerManagementClient, - IndexerManagementDefaults, -} from '../../client' +import { IndexerManagementClient } from '../../client' import { defineIndexerManagementModels, IndexerManagementModels, POIDisputeAttributes, } from '../../models' -import { - IndexingStatusResolver, - NetworkSubgraph, - getTestProvider, -} from '@graphprotocol/indexer-common' +import { createTestManagementClient } from '../util' +import { defineQueryFeeModels } from '../../../query-fees/models' // Make global Jest variable available // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -48,13 +37,14 @@ const STORE_POI_DISPUTES_MUTATION = gql` previousEpochStartBlockNumber previousEpochReferenceProof status + protocolNetwork } } ` const GET_POI_DISPUTE_QUERY = gql` - query dispute($allocationID: String!) { - dispute(allocationID: $allocationID) { + query dispute($identifier: POIDisputeIdentifier!) { + dispute(identifier: $identifier) { allocationID subgraphDeploymentID allocationIndexer @@ -68,13 +58,18 @@ const GET_POI_DISPUTE_QUERY = gql` previousEpochStartBlockNumber previousEpochReferenceProof status + protocolNetwork } } ` const GET_POI_DISPUTES_QUERY = gql` - query disputes($status: String!, $minClosedEpoch: Int!) { - disputes(status: $status, minClosedEpoch: $minClosedEpoch) { + query disputes($status: String!, $minClosedEpoch: Int!, $protocolNetwork: String!) { + disputes( + status: $status + minClosedEpoch: $minClosedEpoch + protocolNetwork: $protocolNetwork + ) { allocationID subgraphDeploymentID allocationIndexer @@ -88,13 +83,14 @@ const GET_POI_DISPUTES_QUERY = gql` previousEpochStartBlockNumber previousEpochReferenceProof status + protocolNetwork } } ` const DELETE_POI_DISPUTES_QUERY = gql` - mutation deleteDisputes($allocationIDs: [String!]!) { - deleteDisputes(allocationIDs: $allocationIDs) + mutation deleteDisputes($identifiers: [POIDisputeIdentifier!]!) { + deleteDisputes(identifiers: $identifiers) } ` @@ -116,6 +112,7 @@ const TEST_DISPUTE_1: POIDisputeAttributes = { previousEpochReferenceProof: '0xd04b5601739a1638719696d0735c92439267a89248c6fd21388d9600f5c942f6', status: 'potential', + protocolNetwork: 'goerli', } const TEST_DISPUTE_2: POIDisputeAttributes = { allocationID: '0x085fd2ADc1B96c26c266DecAb6A3098EA0eda619', @@ -135,6 +132,7 @@ const TEST_DISPUTE_2: POIDisputeAttributes = { previousEpochReferenceProof: '0xd04b5601739a1638719696d0735c92439267a89248c6fd21388d9600f5c942f6', status: 'potential', + protocolNetwork: 'goerli', } const TEST_DISPUTE_3: POIDisputeAttributes = { @@ -155,6 +153,7 @@ const TEST_DISPUTE_3: POIDisputeAttributes = { previousEpochReferenceProof: '0xd04b5601739a1638719696d0735c92439267a89248c6fd21388d9600f5c942f6', status: 'potential', + protocolNetwork: 'goerli', } const TEST_DISPUTES_ARRAY = [TEST_DISPUTE_1, TEST_DISPUTE_2] @@ -164,6 +163,9 @@ function toObject(dispute: POIDisputeAttributes): Record { // eslint-disable-next-line @typescript-eslint/no-explicit-any const expected: Record = Object.assign({}, dispute) expected.allocationAmount = expected.allocationAmount.toString() + if (expected.protocolNetwork === 'goerli') { + expected.protocolNetwork = 'eip155:5' + } return expected } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -178,57 +180,23 @@ function toObjectArray(disputes: POIDisputeAttributes[]): Record[] let sequelize: Sequelize let models: IndexerManagementModels -let address: string -let contracts: NetworkContracts let logger: Logger -let indexingStatusResolver: IndexingStatusResolver -let networkSubgraph: NetworkSubgraph let client: IndexerManagementClient - -const defaults = { - globalIndexingRule: { - allocationAmount: parseGRT('100'), - }, -} as IndexerManagementDefaults +const metrics = createMetrics() const setupAll = async () => { // Spin up db sequelize = await connectDatabase(__DATABASE__) models = defineIndexerManagementModels(sequelize) - address = '0xtest' - contracts = await connectContracts(getTestProvider('goerli'), 5) - await sequelize.sync({ force: true }) + defineQueryFeeModels(sequelize) + sequelize = await sequelize.sync({ force: true }) logger = createLogger({ - name: 'POI dispute tests', + name: 'Indexer API Client', async: false, level: __LOG_LEVEL__ ?? 'error', }) - const statusEndpoint = 'http://localhost:8030/graphql' - indexingStatusResolver = new IndexingStatusResolver({ - logger: logger, - statusEndpoint, - }) - networkSubgraph = await NetworkSubgraph.create({ - logger, - endpoint: - 'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-testnet', - deployment: undefined, - }) - const indexNodeIDs = ['node_1'] - client = await createIndexerManagementClient({ - models, - address, - contracts, - indexingStatusResolver, - indexNodeIDs, - networkSubgraph, - deploymentManagementEndpoint: statusEndpoint, - logger, - defaults, - features: { - injectDai: true, - }, - }) + client = await createTestManagementClient(__DATABASE__, logger, true, metrics) + logger.info('Finished setting up Test Indexer Management Client') } const setupEach = async () => { @@ -247,6 +215,7 @@ const teardownAll = async () => { } describe('POI disputes', () => { + jest.setTimeout(60_000) beforeAll(setupAll) beforeEach(setupEach) afterEach(teardownEach) @@ -266,12 +235,12 @@ describe('POI disputes', () => { }) test('Get non-existent dispute', async () => { + const identifier = { + allocationID: '0x0000000000000000000000000000000000000001', + protocolNetwork: 'goerli', + } await expect( - client - .query(GET_POI_DISPUTE_QUERY, { - allocationID: '0x0000000000000000000000000000000000000001', - }) - .toPromise(), + client.query(GET_POI_DISPUTE_QUERY, { identifier }).toPromise(), ).resolves.toHaveProperty('data.dispute', null) }) @@ -281,11 +250,14 @@ describe('POI disputes', () => { await client.mutation(STORE_POI_DISPUTES_MUTATION, { disputes: disputes }).toPromise() for (const dispute of disputes) { + const identifier = { + allocationID: dispute.allocationID, + protocolNetwork: 'eip155:5', + } + const expected = { ...dispute, protocolNetwork: 'eip155:5' } await expect( - client - .query(GET_POI_DISPUTE_QUERY, { allocationID: dispute.allocationID }) - .toPromise(), - ).resolves.toHaveProperty('data.dispute', dispute) + client.query(GET_POI_DISPUTE_QUERY, { identifier }).toPromise(), + ).resolves.toHaveProperty('data.dispute', expected) } }) @@ -294,11 +266,21 @@ describe('POI disputes', () => { await client.mutation(STORE_POI_DISPUTES_MUTATION, { disputes: disputes }).toPromise() + // Once persisted, the protocol network identifier assumes the CAIP2-ID format + const expected = disputes.map((dispute) => ({ + ...dispute, + protocolNetwork: 'eip155:5', + })) + await expect( client - .query(GET_POI_DISPUTES_QUERY, { status: 'potential', minClosedEpoch: 0 }) + .query(GET_POI_DISPUTES_QUERY, { + status: 'potential', + minClosedEpoch: 0, + protocolNetwork: 'goerli', + }) .toPromise(), - ).resolves.toHaveProperty('data.disputes', disputes) + ).resolves.toHaveProperty('data.disputes', expected) }) test('Get disputes with closed epoch greater than', async () => { @@ -306,11 +288,18 @@ describe('POI disputes', () => { await client.mutation(STORE_POI_DISPUTES_MUTATION, { disputes: disputes }).toPromise() + // Once persisted, the protocol network identifier assumes the CAIP2-ID format + const expected = [{ ...TEST_DISPUTE_2, protocolNetwork: 'eip155:5' }] + await expect( client - .query(GET_POI_DISPUTES_QUERY, { status: 'potential', minClosedEpoch: 205 }) + .query(GET_POI_DISPUTES_QUERY, { + status: 'potential', + minClosedEpoch: 205, + protocolNetwork: 'goerli', + }) .toPromise(), - ).resolves.toHaveProperty('data.disputes', [TEST_DISPUTE_2]) + ).resolves.toHaveProperty('data.disputes', expected) }) test('Remove dispute from store', async () => { @@ -318,20 +307,36 @@ describe('POI disputes', () => { await client.mutation(STORE_POI_DISPUTES_MUTATION, { disputes: disputes }).toPromise() + const identifiers = [ + { + allocationID: '0xbAd8935f75903A1eF5ea62199d98Fd7c3c1ab20C', + protocolNetwork: 'goerli', + }, + ] await expect( client .mutation(DELETE_POI_DISPUTES_QUERY, { - allocationIDs: ['0xbAd8935f75903A1eF5ea62199d98Fd7c3c1ab20C'], + identifiers, }) .toPromise(), ).resolves.toHaveProperty('data.deleteDisputes', 1) disputes.splice(0, 1) + // Once persisted, the protocol network identifier assumes the CAIP2-ID format + const expected = disputes.map((dispute) => ({ + ...dispute, + protocolNetwork: 'eip155:5', + })) + await expect( client - .query(GET_POI_DISPUTES_QUERY, { status: 'potential', minClosedEpoch: 0 }) + .query(GET_POI_DISPUTES_QUERY, { + status: 'potential', + minClosedEpoch: 0, + protocolNetwork: 'goerli', + }) .toPromise(), - ).resolves.toHaveProperty('data.disputes', disputes) + ).resolves.toHaveProperty('data.disputes', expected) }) test('Remove multiple disputes from store', async () => { @@ -339,22 +344,36 @@ describe('POI disputes', () => { await client.mutation(STORE_POI_DISPUTES_MUTATION, { disputes: disputes }).toPromise() + const identifiers = [ + { + allocationID: '0xbAd8935f75903A1eF5ea62199d98Fd7c3c1ab20C', + protocolNetwork: 'goerli', + }, + { + allocationID: '0x085fd2ADc1B96c26c266DecAb6A3098EA0eda619', + protocolNetwork: 'goerli', + }, + ] + await expect( - client - .mutation(DELETE_POI_DISPUTES_QUERY, { - allocationIDs: [ - '0xbAd8935f75903A1eF5ea62199d98Fd7c3c1ab20C', - '0x085fd2ADc1B96c26c266DecAb6A3098EA0eda619', - ], - }) - .toPromise(), + client.mutation(DELETE_POI_DISPUTES_QUERY, { identifiers }).toPromise(), ).resolves.toHaveProperty('data.deleteDisputes', 2) disputes.splice(0, 2) + // Once persisted, the protocol network identifier assumes the CAIP2-ID format + const expected = disputes.map((dispute) => ({ + ...dispute, + protocolNetwork: 'eip155:5', + })) + await expect( client - .query(GET_POI_DISPUTES_QUERY, { status: 'potential', minClosedEpoch: 0 }) + .query(GET_POI_DISPUTES_QUERY, { + status: 'potential', + minClosedEpoch: 0, + protocolNetwork: 'goerli', + }) .toPromise(), - ).resolves.toHaveProperty('data.disputes', disputes) + ).resolves.toHaveProperty('data.disputes', expected) }) }) diff --git a/packages/indexer-common/src/indexer-management/__tests__/util.ts b/packages/indexer-common/src/indexer-management/__tests__/util.ts new file mode 100644 index 000000000..4dac58992 --- /dev/null +++ b/packages/indexer-common/src/indexer-management/__tests__/util.ts @@ -0,0 +1,188 @@ +import { + ActionInput, + ActionStatus, + ActionType, + createIndexerManagementClient, + defineIndexerManagementModels, + defineQueryFeeModels, + GraphNode, + IndexerManagementClient, + IndexerManagementDefaults, + MultiNetworks, + Network, + specification, +} from '@graphprotocol/indexer-common' +import { connectDatabase, Metrics, Logger, parseGRT } from '@graphprotocol/common-ts' + +const PUBLIC_JSON_RPC_ENDPOINT = 'https://ethereum-goerli.publicnode.com' + +const testProviderUrl = + process.env.INDEXER_TEST_JRPC_PROVIDER_URL ?? PUBLIC_JSON_RPC_ENDPOINT + +export const testNetworkSpecification: specification.NetworkSpecification = + specification.NetworkSpecification.parse({ + networkIdentifier: 'goerli', + gateway: { + url: 'http://localhost:8030/', + }, + networkProvider: { + url: testProviderUrl, + }, + indexerOptions: { + address: '0xf56b5d582920E4527A818FBDd801C0D80A394CB8', + mnemonic: + 'famous aspect index polar tornado zero wedding electric floor chalk tenant junk', + url: 'http://test-indexer.xyz', + }, + subgraphs: { + networkSubgraph: { + url: 'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli', + }, + epochSubgraph: { + url: 'http://test-url.xyz', + }, + }, + transactionMonitoring: { + gasIncreaseTimeout: 240000, + gasIncreaseFactor: 1.2, + baseFeePerGasMax: 100 * 10 ** 9, + maxTransactionAttempts: 0, + }, + dai: { + contractAddress: '0x4e8a4C63Df58bf59Fef513aB67a76319a9faf448', + inject: false, + }, + }) + +export const createTestManagementClient = async ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + databaseOptions: any, + logger: Logger, + injectDai: boolean, + metrics: Metrics, + networkIdentifierOverride?: string, +): Promise => { + // Clearing the registry prevents duplicate metric registration in the default registry. + metrics.registry.clear() + + // Spin up db + let sequelize = await connectDatabase(databaseOptions) + const queryFeeModels = defineQueryFeeModels(sequelize) + const managementModels = defineIndexerManagementModels(sequelize) + sequelize = await sequelize.sync({ force: true }) + const statusEndpoint = 'http://localhost:8030/graphql' + const graphNode = new GraphNode( + logger, + 'http://test-admin-endpoint.xyz', + 'https://test-query-endpoint.xyz', + statusEndpoint, + [], + ) + const indexNodeIDs = ['node_1'] + + const networkSpecification = { ...testNetworkSpecification } + networkSpecification.dai.inject = injectDai + + const defaults: IndexerManagementDefaults = { + globalIndexingRule: { + allocationAmount: parseGRT('100'), + parallelAllocations: 1, + requireSupported: true, + safety: true, + protocolNetwork: 'goerli', + }, + } + + const network = await Network.create( + logger, + networkSpecification, + queryFeeModels, + graphNode, + metrics, + ) + + if (networkIdentifierOverride) { + network.specification.networkIdentifier = networkIdentifierOverride + } + + const multiNetworks = new MultiNetworks( + [network], + (n: Network) => n.specification.networkIdentifier, + ) + + return await createIndexerManagementClient({ + models: managementModels, + graphNode, + indexNodeIDs, + logger, + defaults, + multiNetworks, + }) +} + +export const defaults: IndexerManagementDefaults = { + globalIndexingRule: { + allocationAmount: parseGRT('100'), + parallelAllocations: 1, + requireSupported: true, + safety: true, + }, +} + +export const subgraphDeployment1 = 'Qmew9PZUJCoDzXqqU6vGyTENTKHrrN4dy5h94kertfudqy' +export const subgraphDeployment2 = 'QmWq1pmnhEvx25qxpYYj9Yp6E1xMKMVoUjXVQBxUJmreSe' +export const subgraphDeployment3 = 'QmRhH2nhNibDVPZmYqq3TUZZARZ77vgjYCvPNiGBCogtgM' +export const notPublishedSubgraphDeployment = + 'QmeqJ6hsdyk9dVbo1tvRgAxWrVS3rkERiEMsxzPShKLco6' + +export const queuedAllocateAction = { + status: ActionStatus.QUEUED, + type: ActionType.ALLOCATE, + deploymentID: subgraphDeployment1, + amount: '10000', + force: false, + source: 'indexerAgent', + reason: 'indexingRule', + priority: 0, + protocolNetwork: 'goerli', +} as ActionInput + +export const allocateToNotPublishedDeployment = { + status: ActionStatus.QUEUED, + type: ActionType.ALLOCATE, + deploymentID: notPublishedSubgraphDeployment, + amount: '10000', + force: false, + source: 'indexerAgent', + reason: 'indexingRule', + priority: 0, + protocolNetwork: 'goerli', +} as ActionInput + +export const invalidUnallocateAction = { + status: ActionStatus.QUEUED, + type: ActionType.UNALLOCATE, + allocationID: '0x8f63930129e585c69482b56390a09b6b176f4a4c', + deploymentID: subgraphDeployment1, + amount: undefined, + poi: undefined, + force: false, + source: 'indexerAgent', + reason: 'indexingRule', + priority: 0, + protocolNetwork: 'goerli', +} as ActionInput + +export const invalidReallocateAction = { + status: ActionStatus.QUEUED, + type: ActionType.REALLOCATE, + deploymentID: subgraphDeployment1, + allocationID: '0x8f63930129e585c69482b56390a09b6b176f4a4c', + poi: undefined, + amount: undefined, + force: false, + source: 'indexerAgent', + reason: 'indexingRule', + priority: 0, + protocolNetwork: 'goerli', +} as ActionInput diff --git a/packages/indexer-common/src/indexer-management/actions.ts b/packages/indexer-common/src/indexer-management/actions.ts index a6952affc..7efbbafcb 100644 --- a/packages/indexer-common/src/indexer-management/actions.ts +++ b/packages/indexer-common/src/indexer-management/actions.ts @@ -5,6 +5,7 @@ import { ActionParams, ActionStatus, ActionUpdateInput, + AllocationManager, AllocationManagementMode, AllocationResult, AllocationStatus, @@ -12,49 +13,90 @@ import { IndexerErrorCode, IndexerManagementModels, isActionFailure, - NetworkMonitor, + MultiNetworks, + NetworkMapped, + Network, OrderDirection, + GraphNode, } from '@graphprotocol/indexer-common' -import { AllocationManager } from './allocations' + import { Order, Transaction } from 'sequelize' import { Eventual, join, Logger, timer } from '@graphprotocol/common-ts' +import groupBy from 'lodash.groupby' export class ActionManager { - constructor( - public allocationManager: AllocationManager, - public networkMonitor: NetworkMonitor, - private logger: Logger, - private models: IndexerManagementModels, - private allocationManagementMode?: AllocationManagementMode, - private autoAllocationMinBatchSize?: number, - ) {} - - private async batchReady(approvedActions: Action[]): Promise { + declare multiNetworks: MultiNetworks + declare logger: Logger + declare models: IndexerManagementModels + declare allocationManagers: NetworkMapped + + static async create( + multiNetworks: MultiNetworks, + logger: Logger, + models: IndexerManagementModels, + graphNode: GraphNode, + ): Promise { + const actionManager = new ActionManager() + actionManager.multiNetworks = multiNetworks + actionManager.logger = logger.child({ component: 'ActionManager' }) + actionManager.models = models + actionManager.allocationManagers = await multiNetworks.map(async (network) => { + return new AllocationManager( + logger.child({ + component: 'AllocationManager', + protocolNetwork: network.specification.networkIdentifier, + }), + models, + graphNode, + network, + ) + }) + + logger.info('Begin monitoring the queue for approved actions to execute') + await actionManager.monitorQueue() + + return actionManager + } + + private async batchReady( + approvedActions: Action[], + network: Network, + logger: Logger, + ): Promise { + logger.info('Batch ready?', { + approvedActions, + }) + if (approvedActions.length < 1) { + logger.info('Batch not ready: No approved actions found') return false } // In auto management mode the worker will execute the batch if: // 1) Number of approved actions >= minimum batch size // or 2) Oldest affected allocation will expiring after the current epoch - if (this.allocationManagementMode === AllocationManagementMode.AUTO) { + if ( + network.specification.indexerOptions.allocationManagementMode === + AllocationManagementMode.AUTO + ) { const meetsMinBatchSize = - approvedActions.length >= (this.autoAllocationMinBatchSize ?? 1) + approvedActions.length >= + (network.specification.indexerOptions.autoAllocationMinBatchSize ?? 1) const approvedDeploymentIDs = approvedActions.map((action) => action.deploymentID) const affectedAllocations = ( - await this.networkMonitor.allocations(AllocationStatus.ACTIVE) + await network.networkMonitor.allocations(AllocationStatus.ACTIVE) ).filter((a) => approvedDeploymentIDs.includes(a.subgraphDeployment.id.ipfsHash)) let affectedAllocationExpiring = false if (affectedAllocations.length) { - const currentEpoch = await this.networkMonitor.currentEpochNumber() - const maxAllocationEpoch = await this.networkMonitor.maxAllocationEpoch() + const currentEpoch = await network.networkMonitor.currentEpochNumber() + const maxAllocationEpoch = await network.networkMonitor.maxAllocationEpoch() // affectedAllocations are ordered by creation time so use index 0 for oldest allocation to check expiration affectedAllocationExpiring = currentEpoch >= affectedAllocations[0].createdAtEpoch + maxAllocationEpoch } - this.logger.debug( + logger.debug( 'Auto allocation management executes the batch if at least one requirement is met', { currentBatchSize: approvedActions.length, @@ -73,37 +115,87 @@ export class ActionManager { } async monitorQueue(): Promise { + const logger = this.logger.child({ component: 'QueueMonitor' }) const approvedActions: Eventual = timer(30_000).tryMap( - async () => - await ActionManager.fetchActions(this.models, { - status: ActionStatus.APPROVED, - }), + async () => { + logger.trace('Fetching approved actions') + let actions: Action[] = [] + try { + actions = await ActionManager.fetchActions(this.models, { + status: ActionStatus.APPROVED, + }) + logger.trace(`Fetched ${actions.length} approved actions`) + } catch (err) { + logger.warn('Failed to fetch approved actions from queue', { err }) + } + + return actions + }, { // eslint-disable-next-line @typescript-eslint/no-explicit-any onError: (err: any) => - this.logger.warn('Failed to fetch approved actions from queue', { err }), + logger.warn('Failed to fetch approved actions from queue', { err }), }, ) join({ approvedActions }).pipe(async ({ approvedActions }) => { - if (await this.batchReady(approvedActions)) { - this.logger.info('Executing batch of approved actions', { - actions: approvedActions, - note: 'If actions were approved very recently they may be missing from this list but will still be taken', - }) - - try { - const attemptedActions = await this.executeApprovedActions() + logger.debug('Approved actions found, evaluating batch') + const approvedActionsByNetwork: NetworkMapped = groupBy( + approvedActions, + (action: Action) => action.protocolNetwork, + ) - this.logger.trace('Attempted to execute all approved actions', { - actions: attemptedActions, + await this.multiNetworks.mapNetworkMapped( + approvedActionsByNetwork, + async (network: Network, approvedActions: Action[]) => { + const networkLogger = logger.child({ + protocolNetwork: network.specification.networkIdentifier, + indexer: network.specification.indexerOptions.address, + operator: network.transactionManager.wallet.address, }) - } catch (error) { - this.logger.error('Failed to execute batch of approved actions', { - error, - }) - } - } + + if (await this.batchReady(approvedActions, network, networkLogger)) { + const paused = await network.paused.value() + const isOperator = await network.isOperator.value() + networkLogger.debug('Batch ready, preparing to execute', { + paused, + isOperator, + protocolNetwork: network.specification.networkIdentifier, + }) + // Do nothing else if the network is paused + if (paused) { + networkLogger.info( + `The network is currently paused, not doing anything until it resumes`, + ) + return + } + + // Do nothing if we're not authorized as an operator for the indexer + if (!isOperator) { + networkLogger.error(`Not authorized as an operator for the indexer`, { + err: indexerError(IndexerErrorCode.IE034), + }) + return + } + + networkLogger.info('Executing batch of approved actions', { + actions: approvedActions, + note: 'If actions were approved very recently they may be missing from this batch', + }) + + try { + const attemptedActions = await this.executeApprovedActions(network) + networkLogger.trace('Attempted to execute all approved actions', { + actions: attemptedActions, + }) + } catch (error) { + networkLogger.error('Failed to execute batch of approved actions', { + error, + }) + } + } + }, + ) }) } @@ -131,46 +223,70 @@ export class ActionManager { return updatedActions } - async executeApprovedActions(): Promise { + async executeApprovedActions(network: Network): Promise { let updatedActions: Action[] = [] + const protocolNetwork = network.specification.networkIdentifier + const logger = this.logger.child({ + function: 'executeApprovedActions', + protocolNetwork, + }) + logger.trace('Begin database transaction for executing approved actions') // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await this.models.Action.sequelize!.transaction( { isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE }, async (transaction) => { - // Execute already approved actions in the order of type and priority. - // Unallocate actions are prioritized to free up stake that can be used - // in subsequent reallocate and allocate actions. - // Reallocate actions are prioritized before allocate as they are for - // existing syncing deployments with relatively smaller changes made. - const actionTypePriority = ['unallocate', 'reallocate', 'allocate'] - const approvedActions = ( - await this.models.Action.findAll({ - where: { status: ActionStatus.APPROVED }, - order: [['priority', 'ASC']], - transaction, - lock: transaction.LOCK.UPDATE, + let approvedActions + try { + // Execute already approved actions in the order of type and priority. + // Unallocate actions are prioritized to free up stake that can be used + // in subsequent reallocate and allocate actions. + // Reallocate actions are prioritized before allocate as they are for + // existing syncing deployments with relatively smaller changes made. + const actionTypePriority = ['unallocate', 'reallocate', 'allocate'] + approvedActions = ( + await this.models.Action.findAll({ + where: { + status: ActionStatus.APPROVED, + protocolNetwork, + }, + order: [['priority', 'ASC']], + transaction, + lock: transaction.LOCK.UPDATE, + }) + ).sort(function (a, b) { + return actionTypePriority.indexOf(a.type) - actionTypePriority.indexOf(b.type) }) - ).sort(function (a, b) { - return actionTypePriority.indexOf(a.type) - actionTypePriority.indexOf(b.type) - }) + if (approvedActions.length === 0) { + logger.debug('No approved actions were found for this network') + return [] + } + logger.debug( + `Found ${approvedActions.length} approved actions for this network `, + { approvedActions }, + ) + } catch (error) { + logger.error('Failed to query approved actions for network', { error }) + return [] + } try { // This will return all results if successful, if failed it will return the failed actions - const results = await this.allocationManager.executeBatch(approvedActions) + const allocationManager = + this.allocationManagers[network.specification.networkIdentifier] + const results = await allocationManager.executeBatch(approvedActions) - this.logger.debug('Completed batch action execution', { + logger.debug('Completed batch action execution', { results, }) - updatedActions = await this.updateActionStatuses(results, transaction) } catch (error) { - this.logger.error(`Failed to execute batch tx on staking contract: ${error}`) + logger.error(`Failed to execute batch tx on staking contract: ${error}`) throw indexerError(IndexerErrorCode.IE072, error) } }, ) - + logger.trace('End database transaction for executing approved actions') return updatedActions } diff --git a/packages/indexer-common/src/indexer-management/allocations.ts b/packages/indexer-common/src/indexer-management/allocations.ts index 89de40a7e..7b729b004 100644 --- a/packages/indexer-common/src/indexer-management/allocations.ts +++ b/packages/indexer-common/src/indexer-management/allocations.ts @@ -1,7 +1,6 @@ import { formatGRT, Logger, - NetworkContracts, parseGRT, SubgraphDeploymentID, toAddress, @@ -16,7 +15,7 @@ import { CloseAllocationResult, CreateAllocationResult, fetchIndexingRules, - formatDeploymentName, + GraphNode, indexerError, IndexerError, IndexerErrorCode, @@ -25,10 +24,9 @@ import { IndexingRuleAttributes, isActionFailure, isDeploymentWorthAllocatingTowards, + Network, ReallocateAllocationResult, - ReceiptCollector, SubgraphIdentifierType, - TransactionManager, uniqueAllocationID, upsertIndexingRule, } from '@graphprotocol/indexer-common' @@ -38,11 +36,9 @@ import { BigNumberish, ContractReceipt, PopulatedTransaction, - providers, utils, } from 'ethers' -import { NetworkMonitor } from './monitor' -import { SubgraphManager } from './subgraphs' + import { BytesLike } from '@ethersproject/bytes' import pMap from 'p-map' @@ -72,7 +68,7 @@ export interface ReallocateTransactionParams { } // An Action with resolved Allocation and Unallocation values -export interface ResolvedAction { +export interface ActionStakeUsageSummary { action: Action allocates: BigNumber unallocates: BigNumber @@ -80,7 +76,10 @@ export interface ResolvedAction { balance: BigNumber } -export type PopulateTransactionResult = PopulatedTransaction | ActionFailure +export type PopulateTransactionResult = + | PopulatedTransaction + | PopulatedTransaction[] + | ActionFailure export type TransactionResult = | ContractReceipt @@ -90,30 +89,33 @@ export type TransactionResult = export class AllocationManager { constructor( - private contracts: NetworkContracts, private logger: Logger, - private indexer: string, private models: IndexerManagementModels, - private networkMonitor: NetworkMonitor, - private receiptCollector: ReceiptCollector, - private subgraphManager: SubgraphManager, - private transactionManager: TransactionManager, + private graphNode: GraphNode, + private network: Network, ) {} async executeBatch(actions: Action[]): Promise { + const logger = this.logger.child({ function: 'executeBatch' }) + logger.trace('Executing action batch', { actions }) const result = await this.executeTransactions(actions) if (Array.isArray(result)) { + logger.trace('Execute batch transaction failed', { actionBatchResult: result }) return result as ActionFailure[] } return await this.confirmTransactions(result, actions) } async executeTransactions(actions: Action[]): Promise { + const logger = this.logger.child({ function: 'executeTransactions' }) + logger.trace('Begin executing transactions', { actions }) if (actions.length < 1) { throw Error('Failed to populate batch transaction: no transactions supplied') } const validatedActions = await this.validateActionBatchFeasibilty(actions) + logger.trace('Validated actions', { validatedActions }) + const populateTransactionsResults = await this.prepareTransactions(validatedActions) const failedTransactionPreparations = populateTransactionsResults @@ -121,17 +123,25 @@ export class AllocationManager { .map((result) => result as ActionFailure) if (failedTransactionPreparations.length > 0) { + logger.trace('Failed to prepare transactions', { failedTransactionPreparations }) return failedTransactionPreparations } + logger.trace('Prepared transactions ', { + preparedTransactions: populateTransactionsResults, + }) + const callData = populateTransactionsResults + .flat() .map((tx) => tx as PopulatedTransaction) .filter((tx: PopulatedTransaction) => !!tx.data) .map((tx) => tx.data as string) + logger.trace('Prepared transaction calldata', { callData }) - return await this.transactionManager.executeTransaction( - async () => this.contracts.staking.estimateGas.multicall(callData), - async (gasLimit) => this.contracts.staking.multicall(callData, { gasLimit }), + return await this.network.transactionManager.executeTransaction( + async () => this.network.contracts.staking.estimateGas.multicall(callData), + async (gasLimit) => + this.network.contracts.staking.multicall(callData, { gasLimit }), this.logger.child({ actions: `${JSON.stringify(validatedActions.map((action) => action.id))}`, function: 'staking.multicall', @@ -143,9 +153,15 @@ export class AllocationManager { receipt: ContractReceipt | 'paused' | 'unauthorized', actions: Action[], ): Promise { + const logger = this.logger.child({ + function: 'confirmTransactions', + receipt, + actions, + }) + logger.trace('Confirming transaction') return pMap( actions, - async (action) => { + async (action: Action) => { try { return await this.confirmActionExecution(receipt, action) } catch (error) { @@ -163,6 +179,7 @@ export class AllocationManager { error instanceof IndexerError ? error.code : `Failed to confirm transactions: ${error.message}`, + protocolNetwork: action.protocolNetwork, } } }, @@ -170,32 +187,19 @@ export class AllocationManager { ) } - findEvent( - eventType: string, - contractInterface: utils.Interface, - logKey: string, - logValue: string, - receipt: ContractReceipt, - ): utils.Result | undefined { - const events: Event[] | providers.Log[] = receipt.events || receipt.logs - - return events - .filter((event) => - event.topics.includes(contractInterface.getEventTopic(eventType)), - ) - .map((event) => - contractInterface.decodeEventLog(eventType, event.data, event.topics), - ) - .find( - (eventLogs: utils.Result) => - eventLogs[logKey].toLocaleLowerCase() === logValue.toLocaleLowerCase(), - ) - } - async confirmActionExecution( receipt: ContractReceipt | 'paused' | 'unauthorized', action: Action, ): Promise { + // Ensure we are handling an action for the same configured network + if (action.protocolNetwork !== this.network.specification.networkIdentifier) { + const errorMessage = `AllocationManager is configured for '${this.network.specification.networkIdentifier}' but got an Action targeting '${action.protocolNetwork}' ` + this.logger.crit(errorMessage, { + action, + }) + throw new Error(errorMessage) + } + switch (action.type) { case ActionType.ALLOCATE: return await this.confirmAllocate( @@ -207,18 +211,30 @@ export class AllocationManager { receipt, ) case ActionType.UNALLOCATE: - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return await this.confirmUnallocate(action.id, action.allocationID!, receipt) + return await this.confirmUnallocate( + action.id, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + action.allocationID!, + receipt, + ) case ActionType.REALLOCATE: - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return await this.confirmReallocate(action.id, action.allocationID!, receipt) + return await this.confirmReallocate( + action.id, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + action.allocationID!, + receipt, + ) } } async prepareTransactions(actions: Action[]): Promise { - return await pMap(actions, async (action) => await this.prepareTransaction(action), { - stopOnError: false, - }) + return await pMap( + actions, + async (action: Action) => await this.prepareTransaction(action), + { + stopOnError: false, + }, + ) } async prepareTransaction(action: Action): Promise { const logger = this.logger.child({ action: action.id }) @@ -234,7 +250,6 @@ export class AllocationManager { new SubgraphDeploymentID(action.deploymentID!), // eslint-disable-next-line @typescript-eslint/no-non-null-assertion parseGRT(action.amount!), - undefined, ) case ActionType.UNALLOCATE: return await this.prepareUnallocate( @@ -265,6 +280,7 @@ export class AllocationManager { error instanceof IndexerError ? error.code : `Failed to prepare tx call data: ${error.message}`, + protocolNetwork: action.protocolNetwork, } } } @@ -273,14 +289,13 @@ export class AllocationManager { logger: Logger, deployment: SubgraphDeploymentID, amount: BigNumber, - indexNode: string | undefined, ): Promise { logger.info('Preparing to allocate', { deployment: deployment.ipfsHash, amount: amount.toString(), }) - const activeAllocations = await this.networkMonitor.allocations( + const activeAllocations = await this.network.networkMonitor.allocations( AllocationStatus.ACTIVE, ) const allocation = activeAllocations.find( @@ -308,33 +323,7 @@ export class AllocationManager { ) } - const currentEpoch = await this.contracts.epochManager.currentEpoch() - - // Identify how many GRT the indexer has staked - const freeStake = await this.contracts.staking.getIndexerCapacity(this.indexer) - - // If there isn't enough left for allocating, abort - if (freeStake.lt(amount)) { - logger.error( - `Allocation of ${formatGRT( - amount, - )} GRT cancelled: indexer only has a free stake amount of ${formatGRT( - freeStake, - )} GRT`, - ) - throw indexerError( - IndexerErrorCode.IE013, - `Allocation of ${formatGRT( - amount, - )} GRT cancelled: indexer only has a free stake amount of ${formatGRT( - freeStake, - )} GRT`, - ) - } - - const subgraphDeployment = await this.networkMonitor.requireSubgraphDeployment( - deployment.ipfsHash, - ) + const currentEpoch = await this.network.contracts.epochManager.currentEpoch() // Ensure graft dependency is resolved await this.subgraphManager.resolveGrafting( @@ -345,19 +334,16 @@ export class AllocationManager { 0, ) // Ensure subgraph is deployed before allocating - await this.subgraphManager.ensure( - logger, - this.models, - formatDeploymentName(subgraphDeployment), + await this.graphNode.ensure( + `indexer-agent/${deployment.ipfsHash.slice(-10)}`, deployment, - indexNode, ) logger.debug('Obtain a unique Allocation ID') // Obtain a unique allocation ID const { allocationSigner, allocationId } = uniqueAllocationID( - this.transactionManager.wallet.mnemonic.phrase, + this.network.transactionManager.wallet.mnemonic.phrase, currentEpoch.toNumber(), deployment, activeAllocations.map((allocation) => allocation.id), @@ -370,10 +356,10 @@ export class AllocationManager { // enum AllocationState { Null, Active, Closed, Finalized, Claimed } // // in the contracts. - const state = await this.contracts.staking.getAllocationState(allocationId) + const state = await this.network.contracts.staking.getAllocationState(allocationId) if (state !== 0) { logger.debug(`Skipping allocation as it already exists onchain`, { - indexer: this.indexer, + indexer: this.network.specification.indexerOptions.address, allocation: allocationId, state, }) @@ -386,17 +372,21 @@ export class AllocationManager { logger.debug('Generating new allocation ID proof', { newAllocationSigner: allocationSigner, newAllocationID: allocationId, - indexerAddress: this.indexer, + indexerAddress: this.network.specification.indexerOptions.address, }) - const proof = await allocationIdProof(allocationSigner, this.indexer, allocationId) + const proof = await allocationIdProof( + allocationSigner, + this.network.specification.indexerOptions.address, + allocationId, + ) logger.debug('Successfully generated allocation ID proof', { allocationIDProof: proof, }) return { - indexer: this.indexer, + indexer: this.network.specification.indexerOptions.address, subgraphDeploymentID: deployment.bytes32, tokens: amount, allocationID: allocationId, @@ -423,12 +413,13 @@ export class AllocationManager { ) } - const createAllocationEventLogs = this.findEvent( + const createAllocationEventLogs = this.network.transactionManager.findEvent( 'AllocationCreated', - this.contracts.staking.interface, + this.network.contracts.staking.interface, 'subgraphDeploymentID', subgraphDeployment.bytes32, receipt, + this.logger, ) if (!createAllocationEventLogs) { @@ -443,7 +434,7 @@ export class AllocationManager { }) // Remember allocation - await this.receiptCollector.rememberAllocations(actionID, [ + await this.network.receiptCollector.rememberAllocations(actionID, [ createAllocationEventLogs.allocationID, ]) @@ -458,6 +449,7 @@ export class AllocationManager { allocationAmount: amount, identifierType: SubgraphIdentifierType.DEPLOYMENT, decisionBasis: IndexingDecisionBasis.ALWAYS, + protocolNetwork: this.network.specification.networkIdentifier, } as Partial await upsertIndexingRule(logger, this.models, indexingRule) @@ -470,6 +462,7 @@ export class AllocationManager { deployment: deployment, allocation: createAllocationEventLogs.allocationID, allocatedTokens: amount, + protocolNetwork: this.network.specification.networkIdentifier, } } @@ -477,9 +470,8 @@ export class AllocationManager { logger: Logger, deployment: SubgraphDeploymentID, amount: BigNumber, - indexNode: string | undefined, ): Promise { - const params = await this.prepareAllocateParams(logger, deployment, amount, indexNode) + const params = await this.prepareAllocateParams(logger, deployment, amount) logger.debug(`Populating allocateFrom transaction`, { indexer: params.indexer, subgraphDeployment: params.subgraphDeploymentID, @@ -487,7 +479,7 @@ export class AllocationManager { allocation: params.allocationID, proof: params.proof, }) - return await this.contracts.staking.populateTransaction.allocateFrom( + return await this.network.contracts.staking.populateTransaction.allocateFrom( params.indexer, params.subgraphDeploymentID, params.tokens, @@ -497,65 +489,6 @@ export class AllocationManager { ) } - async allocate( - deployment: SubgraphDeploymentID, - amount: BigNumber, - indexNode: string | undefined, - ): Promise { - try { - const params = await this.prepareAllocateParams( - this.logger, - deployment, - amount, - indexNode, - ) - - this.logger.debug(`Sending allocateFrom transaction`, { - indexer: params.indexer, - subgraphDeployment: deployment.ipfsHash, - amount: formatGRT(params.tokens), - allocation: params.allocationID, - proof: params.proof, - }) - - const receipt = await this.transactionManager.executeTransaction( - async () => - this.contracts.staking.estimateGas.allocateFrom( - params.indexer, - params.subgraphDeploymentID, - params.tokens, - params.allocationID, - params.metadata, - params.proof, - ), - async (gasLimit) => - this.contracts.staking.allocateFrom( - params.indexer, - params.subgraphDeploymentID, - params.tokens, - params.allocationID, - params.metadata, - params.proof, - { gasLimit }, - ), - this.logger.child({ function: 'staking.allocate' }), - ) - - return await this.confirmAllocate( - 0, - deployment.ipfsHash, - amount.toString(), - receipt, - ) - } catch (error) { - this.logger.error(`Failed to allocate`, { - amount: formatGRT(amount), - error, - }) - throw error - } - } - async prepareUnallocateParams( logger: Logger, allocationID: string, @@ -566,9 +499,9 @@ export class AllocationManager { allocationID: allocationID, poi: poi || 'none provided', }) - const allocation = await this.networkMonitor.allocation(allocationID) + const allocation = await this.network.networkMonitor.allocation(allocationID) // Ensure allocation is old enough to close - const currentEpoch = await this.contracts.epochManager.currentEpoch() + const currentEpoch = await this.network.contracts.epochManager.currentEpoch() if (BigNumber.from(allocation.createdAtEpoch).eq(currentEpoch)) { throw indexerError( IndexerErrorCode.IE064, @@ -578,7 +511,7 @@ export class AllocationManager { ) } - poi = await this.networkMonitor.resolvePOI(allocation, poi, force) + poi = await this.network.networkMonitor.resolvePOI(allocation, poi, force) // Double-check whether the allocation is still active on chain, to // avoid unnecessary transactions. @@ -587,7 +520,7 @@ export class AllocationManager { // enum AllocationState { Null, Active, Closed, Finalized, Claimed } // // in the contracts. - const state = await this.contracts.staking.getAllocationState(allocation.id) + const state = await this.network.contracts.staking.getAllocationState(allocation.id) if (state !== 1) { throw indexerError(IndexerErrorCode.IE065) } @@ -614,12 +547,13 @@ export class AllocationManager { ) } - const closeAllocationEventLogs = this.findEvent( + const closeAllocationEventLogs = this.network.transactionManager.findEvent( 'AllocationClosed', - this.contracts.staking.interface, + this.network.contracts.staking.interface, 'allocationID', allocationID, receipt, + this.logger, ) if (!closeAllocationEventLogs) { @@ -629,12 +563,13 @@ export class AllocationManager { ) } - const rewardsEventLogs = this.findEvent( + const rewardsEventLogs = this.network.transactionManager.findEvent( 'RewardsAssigned', - this.contracts.rewardsManager.interface, + this.network.contracts.rewardsManager.interface, 'allocationID', allocationID, receipt, + this.logger, ) const rewardsAssigned = rewardsEventLogs ? rewardsEventLogs.amount : 0 @@ -652,9 +587,7 @@ export class AllocationManager { allocation: closeAllocationEventLogs.allocationID, indexer: closeAllocationEventLogs.indexer, amountGRT: formatGRT(closeAllocationEventLogs.tokens), - effectiveAllocation: closeAllocationEventLogs.effectiveAllocation.toString(), poi: closeAllocationEventLogs.poi, - epoch: closeAllocationEventLogs.epoch.toString(), transaction: receipt.transactionHash, indexingRewards: rewardsAssigned, }) @@ -662,9 +595,9 @@ export class AllocationManager { logger.info('Identifying receipts worth collecting', { allocation: closeAllocationEventLogs.allocationID, }) - const allocation = await this.networkMonitor.allocation(allocationID) + const allocation = await this.network.networkMonitor.allocation(allocationID) // Collect query fees for this allocation - const isCollectingQueryFees = await this.receiptCollector.collectReceipts( + const isCollectingQueryFees = await this.network.receiptCollector.collectReceipts( actionID, allocation, ) @@ -675,6 +608,7 @@ export class AllocationManager { ) const offchainIndexingRule = { identifier: allocation.subgraphDeployment.id.ipfsHash, + protocolNetwork: this.network.specification.networkIdentifier, identifierType: SubgraphIdentifierType.DEPLOYMENT, decisionBasis: IndexingDecisionBasis.OFFCHAIN, } as Partial @@ -689,6 +623,7 @@ export class AllocationManager { allocatedTokens: formatGRT(closeAllocationEventLogs.tokens), indexingRewards: formatGRT(rewardsAssigned), receiptsWorthCollecting: isCollectingQueryFees, + protocolNetwork: this.network.specification.networkIdentifier, } } @@ -700,7 +635,7 @@ export class AllocationManager { allocationID: params.allocationID, POI: params.poi, }) - return await this.contracts.staking.populateTransaction.closeAllocation( + return await this.network.contracts.staking.populateTransaction.closeAllocation( params.allocationID, params.poi, ) @@ -716,46 +651,6 @@ export class AllocationManager { return await this.populateUnallocateTransaction(logger, params) } - async unallocate( - allocationID: string, - poi: string | undefined, - force: boolean, - ): Promise { - try { - const params = await this.prepareUnallocateParams( - this.logger, - allocationID, - poi, - force, - ) - const allocation = await this.networkMonitor.allocation(allocationID) - this.logger.debug('Sending closeAllocation transaction') - const receipt = await this.transactionManager.executeTransaction( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - () => - this.contracts.staking.estimateGas.closeAllocation( - params.allocationID, - params.poi, - ), - (gasLimit) => - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.contracts.staking.closeAllocation(params.allocationID, params.poi, { - gasLimit, - }), - this.logger.child({ function: 'staking.closeAllocation' }), - ) - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return await this.confirmUnallocate(0, allocation.id!, receipt) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE015, error) - this.logger.warn(`Failed to close allocation`, { - err, - }) - throw err - } - } - async prepareReallocateParams( logger: Logger, allocationID: string, @@ -773,7 +668,7 @@ export class AllocationManager { /* Fetch all active allocations and search for our input parameter `allocationID`. * We don't call `fetchAllocations` here because all allocations will be required * later when generating a new `uniqueAllocationID`. */ - const activeAllocations = await this.networkMonitor.allocations( + const activeAllocations = await this.network.networkMonitor.allocations( AllocationStatus.ACTIVE, ) const allocationAddress = toAddress(allocationID) @@ -789,7 +684,7 @@ export class AllocationManager { } // Ensure allocation is old enough to close - const currentEpoch = await this.contracts.epochManager.currentEpoch() + const currentEpoch = await this.network.contracts.epochManager.currentEpoch() if (BigNumber.from(allocation.createdAtEpoch).eq(currentEpoch)) { throw indexerError( IndexerErrorCode.IE064, @@ -800,7 +695,11 @@ export class AllocationManager { } logger.debug('Resolving POI') - const allocationPOI = await this.networkMonitor.resolvePOI(allocation, poi, force) + const allocationPOI = await this.network.networkMonitor.resolvePOI( + allocation, + poi, + force, + ) logger.debug('POI resolved', { userProvidedPOI: poi, poi: allocationPOI, @@ -813,7 +712,7 @@ export class AllocationManager { // enum AllocationState { Null, Active, Closed, Finalized, Claimed } // // in the this.contracts. - const state = await this.contracts.staking.getAllocationState(allocation.id) + const state = await this.network.contracts.staking.getAllocationState(allocation.id) if (state !== 1) { logger.warn(`Allocation has already been closed`) throw indexerError(IndexerErrorCode.IE065, `Allocation has already been closed`) @@ -829,30 +728,9 @@ export class AllocationManager { ) } - // Identify how many GRT the indexer has staked - const freeStake = await this.contracts.staking.getIndexerCapacity(this.indexer) - - // When reallocating, we will first close the old allocation and free up the GRT in that allocation - // This GRT will be available in addition to freeStake for the new allocation - const postCloseFreeStake = freeStake.add(allocation.allocatedTokens) - - // If there isn't enough left for allocating, abort - if (postCloseFreeStake.lt(amount)) { - throw indexerError( - IndexerErrorCode.IE013, - `Unable to allocate ${formatGRT( - amount, - )} GRT: indexer only has a free stake amount of ${formatGRT( - freeStake, - )} GRT, plus ${formatGRT( - allocation.allocatedTokens, - )} GRT from the existing allocation`, - ) - } - logger.debug('Generating a new unique Allocation ID') const { allocationSigner, allocationId: newAllocationId } = uniqueAllocationID( - this.transactionManager.wallet.mnemonic.phrase, + this.network.transactionManager.wallet.mnemonic.phrase, currentEpoch.toNumber(), allocation.subgraphDeployment.id, activeAllocations.map((allocation) => allocation.id), @@ -870,12 +748,11 @@ export class AllocationManager { // enum AllocationState { Null, Active, Closed, Finalized, Claimed } // // in the this.contracts. - const newAllocationState = await this.contracts.staking.getAllocationState( - newAllocationId, - ) + const newAllocationState = + await this.network.contracts.staking.getAllocationState(newAllocationId) if (newAllocationState !== 0) { logger.warn(`Skipping Allocation as it already exists onchain`, { - indexer: this.indexer, + indexer: this.network.specification.indexerOptions.address, allocation: newAllocationId, newAllocationState, }) @@ -885,15 +762,19 @@ export class AllocationManager { logger.debug('Generating new allocation ID proof', { newAllocationSigner: allocationSigner, newAllocationID: newAllocationId, - indexerAddress: this.indexer, + indexerAddress: this.network.specification.indexerOptions.address, }) - const proof = await allocationIdProof(allocationSigner, this.indexer, newAllocationId) + const proof = await allocationIdProof( + allocationSigner, + this.network.specification.indexerOptions.address, + newAllocationId, + ) logger.debug('Successfully generated allocation ID proof', { allocationIDProof: proof, }) - logger.info(`Prepared closeAndAllocate transaction`, { - indexer: this.indexer, + logger.info(`Prepared close and allocate multicall transaction`, { + indexer: this.network.specification.indexerOptions.address, oldAllocationAmount: formatGRT(allocation.allocatedTokens), oldAllocation: allocation.id, newAllocation: newAllocationId, @@ -907,7 +788,7 @@ export class AllocationManager { return { closingAllocationID: allocation.id, poi: allocationPOI, - indexer: this.indexer, + indexer: this.network.specification.indexerOptions.address, subgraphDeploymentID: allocation.subgraphDeployment.id.bytes32, tokens: amount, newAllocationID: newAllocationId, @@ -922,7 +803,7 @@ export class AllocationManager { receipt: ContractReceipt | 'paused' | 'unauthorized', ): Promise { const logger = this.logger.child({ action: actionID }) - logger.info(`Confirming 'closeAndAllocate' transaction`, { + logger.info(`Confirming close and allocate 'multicall' transaction`, { allocationID, }) if (receipt === 'paused' || receipt === 'unauthorized') { @@ -932,12 +813,13 @@ export class AllocationManager { ) } - const closeAllocationEventLogs = this.findEvent( + const closeAllocationEventLogs = this.network.transactionManager.findEvent( 'AllocationClosed', - this.contracts.staking.interface, + this.network.contracts.staking.interface, 'allocationID', allocationID, receipt, + this.logger, ) if (!closeAllocationEventLogs) { @@ -947,12 +829,13 @@ export class AllocationManager { ) } - const createAllocationEventLogs = this.findEvent( + const createAllocationEventLogs = this.network.transactionManager.findEvent( 'AllocationCreated', - this.contracts.staking.interface, + this.network.contracts.staking.interface, 'subgraphDeploymentID', closeAllocationEventLogs.subgraphDeploymentID, receipt, + this.logger, ) if (!createAllocationEventLogs) { @@ -962,12 +845,13 @@ export class AllocationManager { ) } - const rewardsEventLogs = this.findEvent( + const rewardsEventLogs = this.network.transactionManager.findEvent( 'RewardsAssigned', - this.contracts.rewardsManager.interface, + this.network.contracts.rewardsManager.interface, 'allocationID', allocationID, receipt, + this.logger, ) const rewardsAssigned = rewardsEventLogs ? rewardsEventLogs.amount : 0 @@ -997,12 +881,22 @@ export class AllocationManager { logger.info('Identifying receipts worth collecting', { allocation: closeAllocationEventLogs.allocationID, }) - const allocation = await this.networkMonitor.allocation(allocationID) - // Collect query fees for this allocation - const isCollectingQueryFees = await this.receiptCollector.collectReceipts( - actionID, - allocation, - ) + let allocation + let isCollectingQueryFees = false + try { + allocation = await this.network.networkMonitor.allocation(allocationID) + // Collect query fees for this allocation + isCollectingQueryFees = await this.network.receiptCollector.collectReceipts( + actionID, + allocation, + ) + logger.debug('Finished receipt collection') + } catch (err) { + logger.error('Failed to collect receipts', { + err, + }) + throw err + } // If there is not yet an indexingRule that deems this deployment worth allocating to, make one if (!(await this.matchingRuleExists(logger, subgraphDeploymentID))) { @@ -1014,6 +908,7 @@ export class AllocationManager { allocationAmount: formatGRT(createAllocationEventLogs.tokens), identifierType: SubgraphIdentifierType.DEPLOYMENT, decisionBasis: IndexingDecisionBasis.ALWAYS, + protocolNetwork: this.network.specification.networkIdentifier, } as Partial await upsertIndexingRule(logger, this.models, indexingRule) @@ -1028,6 +923,7 @@ export class AllocationManager { receiptsWorthCollecting: isCollectingQueryFees, createdAllocation: createAllocationEventLogs.allocationID, createdAllocationStake: formatGRT(createAllocationEventLogs.tokens), + protocolNetwork: this.network.specification.networkIdentifier, } } @@ -1037,7 +933,7 @@ export class AllocationManager { poi: string | undefined, amount: BigNumber, force: boolean, - ): Promise { + ): Promise { const params = await this.prepareReallocateParams( logger, allocationID, @@ -1045,83 +941,33 @@ export class AllocationManager { amount, force, ) - return await this.contracts.staking.populateTransaction.closeAndAllocate( - params.closingAllocationID, - params.poi, - params.indexer, - params.subgraphDeploymentID, - params.tokens, - params.newAllocationID, - params.metadata, - params.proof, - ) - } - - async reallocate( - allocationID: string, - poi: string | undefined, - amount: BigNumber, - force: boolean, - ): Promise { - try { - const params = await this.prepareReallocateParams( - this.logger, - allocationID, - poi, - amount, - force, - ) - - this.logger.info(`Sending closeAndAllocate transaction`, { - indexer: params.indexer, - oldAllocation: params.closingAllocationID, - newAllocation: params.newAllocationID, - newAllocationAmount: formatGRT(params.tokens), - deployment: params.subgraphDeploymentID, - poi: params.poi, - proof: params.proof, - }) - - const receipt = await this.transactionManager.executeTransaction( - async () => - this.contracts.staking.estimateGas.closeAndAllocate( - params.closingAllocationID, - params.poi, - params.indexer, - params.subgraphDeploymentID, - params.tokens, - params.newAllocationID, - params.metadata, - params.proof, - ), - async (gasLimit) => - this.contracts.staking.closeAndAllocate( - params.closingAllocationID, - params.poi, - params.indexer, - params.subgraphDeploymentID, - params.tokens, - params.newAllocationID, - params.metadata, - params.proof, - { gasLimit }, - ), - this.logger.child({ function: 'staking.closeAndAllocate' }), - ) - return await this.confirmReallocate(0, allocationID, receipt) - } catch (error) { - this.logger.error(error.toString()) - throw error - } + return [ + await this.network.contracts.staking.populateTransaction.closeAllocation( + params.closingAllocationID, + params.poi, + ), + await this.network.contracts.staking.populateTransaction.allocateFrom( + params.indexer, + params.subgraphDeploymentID, + params.tokens, + params.newAllocationID, + params.metadata, + params.proof, + ), + ] } async matchingRuleExists( logger: Logger, subgraphDeploymentID: SubgraphDeploymentID, ): Promise { - const indexingRules = await fetchIndexingRules(this.models, true) - const subgraphDeployment = await this.networkMonitor.subgraphDeployment( + const indexingRules = await fetchIndexingRules( + this.models, + true, + this.network.specification.networkIdentifier, + ) + const subgraphDeployment = await this.network.networkMonitor.subgraphDeployment( subgraphDeploymentID.ipfsHash, ) if (!subgraphDeployment) { @@ -1134,7 +980,7 @@ export class AllocationManager { } // Calculates the balance (GRT delta) of a single Action. - async resolveActionDelta(action: Action): Promise { + async stakeUsageSummary(action: Action): Promise { let unallocates = BigNumber.from(0) let rewards = BigNumber.from(0) @@ -1159,14 +1005,14 @@ export class AllocationManager { } // Fetch the allocation on chain to inspect its amount - const allocation = await this.networkMonitor.allocation(action.allocationID) + const allocation = await this.network.networkMonitor.allocation(action.allocationID) // Accrue rewards, except for null or zeroed POI const zeroHexString = utils.hexlify(Array(32).fill(0)) rewards = !action.poi || action.poi === zeroHexString ? BigNumber.from(0) - : await this.contracts.rewardsManager.getRewards(action.allocationID) + : await this.network.contracts.rewardsManager.getRewards(action.allocationID) unallocates = unallocates.add(allocation.allocatedTokens) } @@ -1186,20 +1032,31 @@ export class AllocationManager { logger.debug(`Validating action batch`, { size: batch.length }) // Validate stake feasibility - const freeStake = await this.contracts.staking.getIndexerCapacity(this.indexer) - const mapper = async (action: Action) => this.resolveActionDelta(action) - const resolvedBatch = await pMap(batch, mapper) - const batchDelta: BigNumber = resolvedBatch - .map((resolvedAction) => resolvedAction.balance) - .reduce((a, b) => a.add(b)) - const newBalance = freeStake.sub(batchDelta) - if (newBalance.isNegative()) { + const indexerFreeStake = await this.network.contracts.staking.getIndexerCapacity( + this.network.specification.indexerOptions.address, + ) + const actionsBatchStakeusageSummaries = await pMap(batch, async (action: Action) => + this.stakeUsageSummary(action), + ) + const batchDelta: BigNumber = actionsBatchStakeusageSummaries + .map((summary: ActionStakeUsageSummary) => summary.balance) + .reduce((a: BigNumber, b: BigNumber) => a.add(b)) + const indexerNewBalance = indexerFreeStake.sub(batchDelta) + + logger.trace('Action batch stake usage summary', { + indexerFreeStake, + actionsBatchStakeusageSummaries, + batchDelta, + indexerNewBalance, + }) + + if (indexerNewBalance.isNegative()) { { throw indexerError( IndexerErrorCode.IE013, `Unfeasible action batch: Approved action batch GRT balance is ` + `${formatGRT(batchDelta)} ` + - `but available stake equals ${formatGRT(freeStake)}.`, + `but available stake equals ${formatGRT(indexerFreeStake)}.`, ) } } @@ -1207,8 +1064,10 @@ export class AllocationManager { /* Return actions sorted by GRT balance (ascending). * This ensures on-chain batch feasibility because higher unallocations are processed * first and larger allocations are processed last */ - return resolvedBatch - .sort((a, b) => (a.balance.gt(b.balance) ? 1 : -1)) - .map((a) => a.action) + return actionsBatchStakeusageSummaries + .sort((a: ActionStakeUsageSummary, b: ActionStakeUsageSummary) => + a.balance.gt(b.balance) ? 1 : -1, + ) + .map((a: ActionStakeUsageSummary) => a.action) } } diff --git a/packages/indexer-common/src/indexer-management/client.ts b/packages/indexer-common/src/indexer-management/client.ts index 0785597a1..648dea8cc 100644 --- a/packages/indexer-common/src/indexer-management/client.ts +++ b/packages/indexer-common/src/indexer-management/client.ts @@ -2,15 +2,7 @@ import { buildSchema, print } from 'graphql' import gql from 'graphql-tag' import { executeExchange } from '@urql/exchange-execute' import { Client, ClientOptions } from '@urql/core' -import { - equal, - Eventual, - Logger, - mutable, - NetworkContracts, - WritableEventual, -} from '@graphprotocol/common-ts' -import { NetworkSubgraph } from '../network-subgraph' +import { equal, Logger, mutable, WritableEventual } from '@graphprotocol/common-ts' import { IndexerManagementModels, IndexingRuleCreationAttributes } from './models' @@ -20,38 +12,19 @@ import costModelResolvers from './resolvers/cost-models' import indexingRuleResolvers from './resolvers/indexing-rules' import poiDisputeResolvers from './resolvers/poi-disputes' import statusResolvers from './resolvers/indexer-status' -import { BigNumber, ethers } from 'ethers' +import { BigNumber } from 'ethers' import { Op, Sequelize } from 'sequelize' -import { IndexingStatusResolver } from '../indexing-status' -import { TransactionManager } from '../transactions' -import { SubgraphManager } from './subgraphs' -import { AllocationReceiptCollector } from '../allocations/query-fees' -import { - ActionManager, - AllocationManagementMode, - AllocationManager, - NetworkMonitor, -} from '@graphprotocol/indexer-common' - -export interface IndexerManagementFeatures { - injectDai: boolean -} +import { GraphNode } from '../graph-node' +import { ActionManager, MultiNetworks, Network } from '@graphprotocol/indexer-common' export interface IndexerManagementResolverContext { models: IndexerManagementModels - address: string - contracts: NetworkContracts - indexingStatusResolver: IndexingStatusResolver - subgraphManager: SubgraphManager - networkMonitor: NetworkMonitor - networkSubgraph: NetworkSubgraph + graphNode: GraphNode logger: Logger defaults: IndexerManagementDefaults - features: IndexerManagementFeatures - dai: Eventual - actionManager: ActionManager - transactionManager: TransactionManager - receiptCollector: AllocationReceiptCollector + actionManager: ActionManager | undefined + multiNetworks: MultiNetworks | undefined + dai: WritableEventual } const SCHEMA_SDL = gql` @@ -79,6 +52,7 @@ const SCHEMA_SDL = gql` status: String allocation: String subgraphDeployment: String + protocolNetwork: String } enum AllocationStatus { @@ -102,12 +76,14 @@ const SCHEMA_SDL = gql` signalledTokens: BigInt! stakedTokens: BigInt! status: AllocationStatus! + protocolNetwork: String! } type CreateAllocationResult { allocation: String! deployment: String! allocatedTokens: String! + protocolNetwork: String! } type CloseAllocationResult { @@ -115,6 +91,7 @@ const SCHEMA_SDL = gql` allocatedTokens: String! indexingRewards: String! receiptsWorthCollecting: Boolean! + protocolNetwork: String! } type ReallocateAllocationResult { @@ -123,6 +100,7 @@ const SCHEMA_SDL = gql` receiptsWorthCollecting: Boolean! createdAllocation: String! createdAllocationStake: String! + protocolNetwork: String! } enum ActionStatus { @@ -156,6 +134,7 @@ const SCHEMA_SDL = gql` failureReason: String createdAt: BigInt! updatedAt: BigInt + protocolNetwork: String! } input ActionInput { @@ -169,6 +148,7 @@ const SCHEMA_SDL = gql` source: String! reason: String! priority: Int! + protocolNetwork: String! } input ActionUpdateInput { @@ -198,6 +178,7 @@ const SCHEMA_SDL = gql` priority createdAt updatedAt + protocolNetwork } type ActionResult { @@ -214,16 +195,23 @@ const SCHEMA_SDL = gql` transaction: String failureReason: String priority: Int + protocolNetwork: String! } input ActionFilter { id: Int + protocolNetwork: String type: ActionType status: String source: String reason: String } + input POIDisputeIdentifier { + allocationID: String! + protocolNetwork: String! + } + type POIDispute { allocationID: String! subgraphDeploymentID: String! @@ -238,6 +226,7 @@ const SCHEMA_SDL = gql` previousEpochStartBlockNumber: Int! previousEpochReferenceProof: String status: String! + protocolNetwork: String! } input POIDisputeInput { @@ -254,6 +243,7 @@ const SCHEMA_SDL = gql` previousEpochStartBlockNumber: Int! previousEpochReferenceProof: String status: String! + protocolNetwork: String! } type IndexingRule { @@ -272,6 +262,7 @@ const SCHEMA_SDL = gql` decisionBasis: IndexingDecisionBasis! requireSupported: Boolean! safety: Boolean! + protocolNetwork: String! } input IndexingRuleInput { @@ -290,6 +281,12 @@ const SCHEMA_SDL = gql` decisionBasis: IndexingDecisionBasis requireSupported: Boolean safety: Boolean + protocolNetwork: String! + } + + input IndexingRuleIdentifier { + identifier: String! + protocolNetwork: String! } type GeoLocation { @@ -299,6 +296,7 @@ const SCHEMA_SDL = gql` type IndexerRegistration { url: String + protocolNetwork: String! address: String registered: Boolean! location: GeoLocation @@ -349,6 +347,7 @@ const SCHEMA_SDL = gql` type IndexerEndpoint { url: String healthy: Boolean! + protocolNetwork: String! tests: [IndexerEndpointTest!]! } @@ -370,19 +369,26 @@ const SCHEMA_SDL = gql` } type Query { - indexingRule(identifier: String!, merged: Boolean! = false): IndexingRule - indexingRules(merged: Boolean! = false): [IndexingRule!]! - indexerRegistration: IndexerRegistration! + indexingRule( + identifier: IndexingRuleIdentifier! + merged: Boolean! = false + ): IndexingRule + indexingRules(merged: Boolean! = false, protocolNetwork: String): [IndexingRule!]! + indexerRegistration(protocolNetwork: String!): IndexerRegistration! indexerDeployments: [IndexerDeployment]! - indexerAllocations: [IndexerAllocation]! - indexerEndpoints: IndexerEndpoints! + indexerAllocations(protocolNetwork: String!): [IndexerAllocation]! + indexerEndpoints(protocolNetwork: String): [IndexerEndpoints!]! costModels(deployments: [String!]): [CostModel!]! costModel(deployment: String!): CostModel - dispute(allocationID: String!): POIDispute - disputes(status: String!, minClosedEpoch: Int!): [POIDispute]! - disputesClosedAfter(closedAfterBlock: BigInt!): [POIDispute]! + dispute(identifier: POIDisputeIdentifier!): POIDispute + disputes( + status: String! + minClosedEpoch: Int! + protocolNetwork: String + ): [POIDispute]! + disputesClosedAfter(closedAfterBlock: BigInt!, protocolNetwork: String): [POIDispute]! allocations(filter: AllocationFilter!): [Allocation!]! @@ -397,30 +403,33 @@ const SCHEMA_SDL = gql` type Mutation { setIndexingRule(rule: IndexingRuleInput!): IndexingRule! - deleteIndexingRule(identifier: String!): Boolean! - deleteIndexingRules(identifiers: [String!]!): Boolean! + deleteIndexingRule(identifier: IndexingRuleIdentifier!): Boolean! + deleteIndexingRules(identifiers: [IndexingRuleIdentifier!]!): Boolean! setCostModel(costModel: CostModelInput!): CostModel! deleteCostModels(deployments: [String!]!): Int! storeDisputes(disputes: [POIDisputeInput!]!): [POIDispute!] - deleteDisputes(allocationIDs: [String!]!): Int! + deleteDisputes(identifiers: [POIDisputeIdentifier!]!): Int! createAllocation( deployment: String! amount: String! indexNode: String + protocolNetwork: String! ): CreateAllocationResult! closeAllocation( allocation: String! poi: String force: Boolean + protocolNetwork: String! ): CloseAllocationResult! reallocateAllocation( allocation: String! poi: String amount: String! force: Boolean + protocolNetwork: String! ): ReallocateAllocationResult! updateAction(action: ActionInput!): Action! @@ -441,24 +450,15 @@ export interface IndexerManagementDefaults { } export interface IndexerManagementClientOptions { + logger: Logger models: IndexerManagementModels - address: string - contracts: NetworkContracts - indexingStatusResolver: IndexingStatusResolver + graphNode: GraphNode + // TODO:L2: Do we need this information? The GraphNode class auto-selects nodes based + // on availability. + // Ford: there were some edge cases where the GraphNode was not able to auto handle it on its own indexNodeIDs: string[] - deploymentManagementEndpoint: string - networkSubgraph: NetworkSubgraph - logger: Logger + multiNetworks: MultiNetworks | undefined defaults: IndexerManagementDefaults - features: IndexerManagementFeatures - ethereum?: ethers.providers.BaseProvider - transactionManager?: TransactionManager - receiptCollector?: AllocationReceiptCollector - networkMonitor?: NetworkMonitor - allocationManagementMode?: AllocationManagementMode - autoAllocationMinBatchSize?: number - ipfsEndpoint?: string - autoGraftResolverLimit?: number } export class IndexerManagementClient extends Client { @@ -505,28 +505,12 @@ export class IndexerManagementClient extends Client { } } +// TODO:L2: Put the IndexerManagementClient creation inside the Agent, and receive +// MultiNetworks from it export const createIndexerManagementClient = async ( options: IndexerManagementClientOptions, ): Promise => { - const { - models, - address, - contracts, - indexingStatusResolver, - indexNodeIDs, - deploymentManagementEndpoint, - networkSubgraph, - logger, - defaults, - features, - transactionManager, - receiptCollector, - networkMonitor, - allocationManagementMode, - autoAllocationMinBatchSize, - ipfsEndpoint, - autoGraftResolverLimit, - } = options + const { models, graphNode, logger, defaults, multiNetworks } = options const schema = buildSchema(print(SCHEMA_SDL)) const resolvers = { ...indexingRuleResolvers, @@ -539,62 +523,24 @@ export const createIndexerManagementClient = async ( const dai: WritableEventual = mutable() - const subgraphManager = new SubgraphManager( - deploymentManagementEndpoint, - indexNodeIDs, - indexingStatusResolver, - ipfsEndpoint, - autoGraftResolverLimit, - ) - let allocationManager: AllocationManager | undefined = undefined - let actionManager: ActionManager | undefined = undefined - - if (transactionManager && networkMonitor) { - if (receiptCollector) { - // TODO: AllocationManager construction inside ActionManager - allocationManager = new AllocationManager( - contracts, - logger.child({ component: 'AllocationManager' }), - address, - models, - networkMonitor, - receiptCollector, - subgraphManager, - transactionManager, - ) - actionManager = new ActionManager( - allocationManager, - networkMonitor, - logger.child({ component: 'ActionManager' }), - models, - allocationManagementMode, - autoAllocationMinBatchSize, - ) - - logger.info('Begin monitoring the queue for approved actions to execute') - await actionManager.monitorQueue() - } + const actionManager = multiNetworks + ? await ActionManager.create(multiNetworks, logger, models, graphNode) + : undefined + + const context: IndexerManagementResolverContext = { + models, + graphNode, + defaults, + logger: logger.child({ component: 'IndexerManagementClient' }), + dai, + multiNetworks, + actionManager, } const exchange = executeExchange({ schema, rootValue: resolvers, - context: { - models, - address, - contracts, - indexingStatusResolver, - subgraphManager, - networkMonitor, - networkSubgraph, - logger: logger ? logger.child({ component: 'IndexerManagementClient' }) : undefined, - defaults, - features, - dai, - transactionManager, - actionManager, - receiptCollector, - }, + context, }) return new IndexerManagementClient({ url: 'no-op', exchanges: [exchange] }, options, { diff --git a/packages/indexer-common/src/indexer-management/index.ts b/packages/indexer-common/src/indexer-management/index.ts index dd2d8d55a..6990e72b3 100644 --- a/packages/indexer-common/src/indexer-management/index.ts +++ b/packages/indexer-common/src/indexer-management/index.ts @@ -4,6 +4,5 @@ export * from './client' export * from './models' export * from './monitor' export * from './server' -export * from './subgraphs' export * from './rules' export * from './types' diff --git a/packages/indexer-common/src/indexer-management/models/action.ts b/packages/indexer-common/src/indexer-management/models/action.ts index d2da5ccf5..642a9fbca 100644 --- a/packages/indexer-common/src/indexer-management/models/action.ts +++ b/packages/indexer-common/src/indexer-management/models/action.ts @@ -8,6 +8,7 @@ import { Model, Sequelize, } from 'sequelize' +import { caip2IdRegex } from '../../parsers' import { ActionStatus, ActionType } from '@graphprotocol/indexer-common' export class Action extends Model< @@ -34,6 +35,8 @@ export class Action extends Model< declare createdAt: CreationOptional declare updatedAt: CreationOptional + declare protocolNetwork: string + // eslint-disable-next-line @typescript-eslint/ban-types public toGraphQL(): object { return { ...this.toJSON(), __typename: 'Action' } @@ -141,6 +144,13 @@ export const defineActionModels = (sequelize: Sequelize): ActionModels => { allowNull: true, defaultValue: null, }, + protocolNetwork: { + type: DataTypes.STRING(50), + primaryKey: true, + validate: { + is: caip2IdRegex, + }, + }, }, { modelName: 'Action', diff --git a/packages/indexer-common/src/indexer-management/models/indexing-rule.ts b/packages/indexer-common/src/indexer-management/models/indexing-rule.ts index fc3ddf1bb..76ee2b265 100644 --- a/packages/indexer-common/src/indexer-management/models/indexing-rule.ts +++ b/packages/indexer-common/src/indexer-management/models/indexing-rule.ts @@ -2,6 +2,7 @@ import { DataTypes, Model, Optional, Sequelize } from 'sequelize' import { processIdentifier, SubgraphIdentifierType } from '../../subgraphs' +import { caip2IdRegex } from '../../parsers' export enum IndexingDecisionBasis { RULES = 'rules', @@ -29,6 +30,14 @@ export interface IndexingRuleAttributes { decisionBasis: IndexingDecisionBasis requireSupported: boolean safety: boolean + protocolNetwork: string +} + +// Unambiguously identify a Indexing Rule in the Database. +// This type should match the IndexingRules primary key columns. +export interface IndexingRuleIdentifier { + identifier: string + protocolNetwork: string } export interface IndexingRuleCreationAttributes @@ -50,6 +59,7 @@ export interface IndexingRuleCreationAttributes | 'decisionBasis' | 'requireSupported' | 'safety' + | 'protocolNetwork' > {} export class IndexingRule @@ -72,6 +82,7 @@ export class IndexingRule public decisionBasis!: IndexingDecisionBasis public requireSupported!: boolean public safety!: boolean + public protocolNetwork!: string public createdAt!: Date public updatedAt!: Date @@ -144,7 +155,7 @@ export const defineIndexingRuleModels = (sequelize: Sequelize): IndexingRuleMode identifier: { type: DataTypes.STRING, primaryKey: true, - unique: true, + unique: false, allowNull: false, validate: { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -248,6 +259,14 @@ export const defineIndexingRuleModels = (sequelize: Sequelize): IndexingRuleMode allowNull: false, defaultValue: true, }, + protocolNetwork: { + type: DataTypes.STRING, + primaryKey: true, + allowNull: false, + validate: { + is: caip2IdRegex, + }, + }, }, { modelName: 'IndexingRule', diff --git a/packages/indexer-common/src/indexer-management/models/poi-dispute.ts b/packages/indexer-common/src/indexer-management/models/poi-dispute.ts index 9308b897f..3be43835b 100644 --- a/packages/indexer-common/src/indexer-management/models/poi-dispute.ts +++ b/packages/indexer-common/src/indexer-management/models/poi-dispute.ts @@ -2,6 +2,7 @@ import { Optional, Model, DataTypes, Sequelize } from 'sequelize' import { utils } from 'ethers' +import { caip2IdRegex } from '../../parsers' export interface POIDisputeAttributes { allocationID: string @@ -17,6 +18,14 @@ export interface POIDisputeAttributes { previousEpochStartBlockHash: string previousEpochStartBlockNumber: number status: string + protocolNetwork: string +} + +// Unambiguously identify a POI Dispute in the Database. +// This type should match the POIDispute primary key columns. +export interface POIDisputeIdentifier { + allocationID: string + protocolNetwork: string } export interface POIDisputeCreationAttributes @@ -35,6 +44,7 @@ export interface POIDisputeCreationAttributes | 'previousEpochStartBlockHash' | 'previousEpochStartBlockNumber' | 'status' + | 'protocolNetwork' > {} export class POIDispute @@ -54,6 +64,7 @@ export class POIDispute public previousEpochStartBlockHash!: string public previousEpochStartBlockNumber!: number public status!: string + public protocolNetwork!: string public createdAt!: Date public updatedAt!: Date @@ -244,6 +255,14 @@ export const definePOIDisputeModels = (sequelize: Sequelize): POIDisputeModels = type: DataTypes.STRING, allowNull: false, }, + protocolNetwork: { + type: DataTypes.STRING, + primaryKey: true, + allowNull: false, + validate: { + is: caip2IdRegex, + }, + }, }, { modelName: 'POIDispute', diff --git a/packages/indexer-common/src/indexer-management/monitor.ts b/packages/indexer-common/src/indexer-management/monitor.ts index c291cc916..4d955fe80 100644 --- a/packages/indexer-common/src/indexer-management/monitor.ts +++ b/packages/indexer-common/src/indexer-management/monitor.ts @@ -5,7 +5,7 @@ import { INDEXER_ERROR_MESSAGES, indexerError, IndexerErrorCode, - IndexingStatusResolver, + GraphNode, NetworkSubgraph, parseGraphQLAllocation, parseGraphQLEpochs, @@ -18,6 +18,7 @@ import { BlockPointer, resolveChainId, resolveChainAlias, + TransferredSubgraphDeployment, } from '@graphprotocol/indexer-common' import { Address, @@ -28,19 +29,23 @@ import { SubgraphDeploymentID, timer, toAddress, + formatGRT, } from '@graphprotocol/common-ts' +import { BigNumber } from 'ethers' import gql from 'graphql-tag' import { providers, utils, Wallet } from 'ethers' import pRetry from 'p-retry' +import { IndexerOptions } from '../network-specification' +import pMap from 'p-map' // The new read only Network class export class NetworkMonitor { constructor( public networkCAIPID: string, private contracts: NetworkContracts, - private indexer: Address, + private indexerOptions: IndexerOptions, private logger: Logger, - private indexingStatusResolver: IndexingStatusResolver, + private graphNode: GraphNode, private networkSubgraph: NetworkSubgraph, private ethereum: providers.BaseProvider, private epochSubgraph: EpochSubgraph, @@ -55,7 +60,7 @@ export class NetworkMonitor { } async allocation(allocationID: string): Promise { - const result = await this.networkSubgraph.query( + const result = await this.networkSubgraph.checkedQuery( gql` query allocation($allocation: String!) { allocation(id: $allocation) { @@ -89,12 +94,13 @@ export class NetworkMonitor { this.logger.warn(errorMessage) throw indexerError(IndexerErrorCode.IE063, errorMessage) } - return parseGraphQLAllocation(result.data.allocation) + return parseGraphQLAllocation(result.data.allocation, this.networkCAIPID) } async allocations(status: AllocationStatus): Promise { try { - const result = await this.networkSubgraph.query( + this.logger.debug(`Fetch ${status} allocations`) + const result = await this.networkSubgraph.checkedQuery( gql` query allocations($indexer: String!, $status: AllocationStatus!) { allocations( @@ -121,7 +127,7 @@ export class NetworkMonitor { } `, { - indexer: this.indexer.toLocaleLowerCase(), + indexer: this.indexerOptions.address.toLocaleLowerCase(), status: status, }, ) @@ -138,7 +144,7 @@ export class NetworkMonitor { this.logger.warn( `No ${ AllocationStatus[status.toUpperCase() as keyof typeof AllocationStatus] - } allocations found for indexer '${this.indexer}'`, + } allocations found for indexer '${this.indexerOptions.address}'`, ) return [] } @@ -155,7 +161,7 @@ export class NetworkMonitor { async epochs(epochNumbers: number[]): Promise { try { - const result = await this.networkSubgraph.query( + const result = await this.networkSubgraph.checkedQuery( gql` query epochs($epochs: [Int!]!) { epoches(where: { id_in: $epochs }, first: 1000) { @@ -194,38 +200,37 @@ export class NetworkMonitor { range: number, ): Promise { try { - const result = await this.networkSubgraph.query( + this.logger.debug('Fetch recently closed allocations') + const result = await this.networkSubgraph.checkedQuery( gql` query allocations($indexer: String!, $closedAtEpochThreshold: Int!) { - indexer(id: $indexer) { - allocations: totalAllocations( - where: { - indexer: $indexer - status: Closed - closedAtEpoch_gte: $closedAtEpochThreshold - } - first: 1000 - ) { + allocations( + where: { + indexer: $indexer + status: Closed + closedAtEpoch_gte: $closedAtEpochThreshold + } + first: 1000 + ) { + id + indexer { id - indexer { - id - } - allocatedTokens - createdAtEpoch - closedAtEpoch - createdAtBlockHash - subgraphDeployment { - id - stakedTokens - signalledTokens - queryFeesAmount - } + } + allocatedTokens + createdAtEpoch + closedAtEpoch + createdAtBlockHash + subgraphDeployment { + id + stakedTokens + signalledTokens + queryFeesAmount } } } `, { - indexer: this.indexer.toLocaleLowerCase(), + indexer: this.indexerOptions.address.toLocaleLowerCase(), closedAtEpochThreshold: currentEpoch - range, }, ) @@ -234,15 +239,18 @@ export class NetworkMonitor { throw result.error } - if (!result.data) { - throw new Error(`No data / indexer not found on chain`) - } - - if (!result.data.indexer) { - throw new Error(`Indexer not found on chain`) + if ( + !result.data.allocations || + result.data.length === 0 || + result.data.allocations.length === 0 + ) { + this.logger.warn( + `No recently closed allocations found for indexer '${this.indexerOptions.address}'`, + ) + return [] } - return result.data.indexer.allocations.map(parseGraphQLAllocation) + return result.data.allocations.map(parseGraphQLAllocation) } catch (error) { const err = indexerError(IndexerErrorCode.IE010, error) this.logger.error(`Failed to query indexer's recently closed allocations`, { @@ -256,41 +264,39 @@ export class NetworkMonitor { subgraphDeploymentId: SubgraphDeploymentID, ): Promise { try { - const result = await this.networkSubgraph.query( + const result = await this.networkSubgraph.checkedQuery( gql` query allocations($indexer: String!, $subgraphDeploymentId: String!) { - indexer(id: $indexer) { - allocations: totalAllocations( - where: { - indexer: $indexer - status: Closed - subgraphDeployment: $subgraphDeploymentId - } - first: 5 - orderBy: closedAtBlockNumber - orderDirection: desc - ) { + allocations( + where: { + indexer: $indexer + status: Closed + subgraphDeployment: $subgraphDeploymentId + } + first: 5 + orderBy: closedAtBlockNumber + orderDirection: desc + ) { + id + poi + indexer { id - poi - indexer { - id - } - allocatedTokens - createdAtEpoch - closedAtEpoch - createdAtBlockHash - subgraphDeployment { - id - stakedTokens - signalledTokens - queryFeesAmount - } + } + allocatedTokens + createdAtEpoch + closedAtEpoch + createdAtBlockHash + subgraphDeployment { + id + stakedTokens + signalledTokens + queryFeesAmount } } } `, { - indexer: this.indexer.toLocaleLowerCase(), + indexer: this.indexerOptions.address.toLocaleLowerCase(), subgraphDeploymentId: subgraphDeploymentId.display.bytes32, }, ) @@ -299,15 +305,19 @@ export class NetworkMonitor { throw result.error } - if (!result.data) { - throw new Error(`No data / indexer not found on chain`) - } - - if (!result.data.indexer) { - throw new Error(`Indexer not found on chain`) + if ( + !result.data.allocations || + result.data.length === 0 || + result.data.allocations.length === 0 + ) { + this.logger.warn('No closed allocations found for deployment', { + id: subgraphDeploymentId.display.bytes32, + ipfsHash: subgraphDeploymentId.display, + }) + return [] } - return result.data.indexer.allocations.map(parseGraphQLAllocation) + return result.data.allocations.map(parseGraphQLAllocation) } catch (error) { const err = indexerError(IndexerErrorCode.IE010, error) this.logger.error( @@ -326,7 +336,7 @@ export class NetworkMonitor { } let subgraphs: Subgraph[] = [] const queryProgress = { - lastId: '', + lastCreatedAt: 0, first: 20, fetched: 0, exhausted: false, @@ -340,16 +350,17 @@ export class NetworkMonitor { subgraphIds: ids, }) try { - const result = await this.networkSubgraph.query( + const result = await this.networkSubgraph.checkedQuery( gql` - query subgraphs($first: Int!, $lastId: String!, $subgraphs: [String!]!) { + query subgraphs($first: Int!, $lastCreatedAt: Int!, $subgraphs: [String!]!) { subgraphs( - where: { id_gt: $lastId, id_in: $subgraphs } - orderBy: id + where: { id_gt: $lastCreatedAt, id_in: $subgraphs } + orderBy: createdAt orderDirection: asc first: $first ) { id + createdAt versionCount versions { version @@ -363,7 +374,7 @@ export class NetworkMonitor { `, { first: queryProgress.first, - lastId: queryProgress.lastId, + lastCreatedAt: queryProgress.lastCreatedAt, subgraphs: ids, }, ) @@ -399,7 +410,7 @@ export class NetworkMonitor { queryProgress.exhausted = results.length < queryProgress.first queryProgress.fetched += results.length - queryProgress.lastId = results[results.length - 1].id + queryProgress.lastCreatedAt = results[results.length - 1].createdAt subgraphs = subgraphs.concat(results) } catch (error) { @@ -425,7 +436,7 @@ export class NetworkMonitor { async subgraphDeployment(ipfsHash: string): Promise { try { - const result = await this.networkSubgraph.query( + const result = await this.networkSubgraph.checkedQuery( gql` query subgraphDeployments($ipfsHash: String!) { subgraphDeployments(where: { ipfsHash: $ipfsHash }) { @@ -440,12 +451,6 @@ export class NetworkMonitor { id } } - versions(first: 1, orderBy: version, orderDirection: desc) { - subgraph { - displayName - creatorAddress - } - } } } `, @@ -470,7 +475,10 @@ export class NetworkMonitor { } // TODO: Make and use parseGraphqlDeployment() function - return parseGraphQLSubgraphDeployment(result.data.subgraphDeployments[0]) + return parseGraphQLSubgraphDeployment( + result.data.subgraphDeployments[0], + this.networkCAIPID, + ) } catch (error) { const err = indexerError(IndexerErrorCode.IE010, error) this.logger.error( @@ -483,22 +491,97 @@ export class NetworkMonitor { } } - // Wrapper function over this.subgraphDeployment that will throw an - // error on missing subgraph deployments - async requireSubgraphDeployment(ipfsHash: string): Promise { - const subgraphDeployment = await this.subgraphDeployment(ipfsHash) - if (!subgraphDeployment) { - const errorMessage = `Failed to locate subgraph deployment with id ${ipfsHash} in the Network Subgraph` - this.logger.error(errorMessage, { ipfsHash }) - throw indexerError(IndexerErrorCode.IE020, errorMessage) + async transferredDeployments(): Promise { + this.logger.debug('Querying the Network for transferred subgraph deployments') + try { + const result = await this.networkSubgraph.checkedQuery( + // TODO: Consider querying for the same time range as the Agent's evaluation, limiting + // results to recent transfers. + gql` + { + subgraphs( + where: { startedTransferToL2: true } + orderBy: startedTransferToL2At + orderDirection: asc + ) { + id + idOnL1 + idOnL2 + startedTransferToL2 + startedTransferToL2At + startedTransferToL2AtBlockNumber + startedTransferToL2AtTx + transferredToL2 + transferredToL2At + transferredToL2AtBlockNumber + transferredToL2AtTx + versions { + subgraphDeployment { + ipfsHash + } + } + } + } + `, + ) + + if (result.error) { + throw result.error + } + + const transferredDeployments = result.data.subgraphs + + // There may be no transferred subgraphs, handle gracefully + if (transferredDeployments.length == 0) { + this.logger.warn( + 'Failed to query subgraph deployments transferred to L2: no deployments found', + ) + throw new Error('No transferred subgraph deployments returned') + } + + // Flatten multiple subgraphDeployment versions into a single `TransferredSubgraphDeployment` object + // TODO: We could use `zod` to parse GraphQL responses into the expected type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return transferredDeployments.flatMap((deployment: any) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return deployment.versions.map((version: any) => { + return { + id: deployment.id, + idOnL1: deployment.idOnL1, + idOnL2: deployment.idOnL2, + startedTransferToL2: deployment.startedTransferToL2, + startedTransferToL2At: BigNumber.from(deployment.startedTransferToL2At), + startedTransferToL2AtBlockNumber: BigNumber.from( + deployment.startedTransferToL2AtBlockNumber, + ), + startedTransferToL2AtTx: deployment.startedTransferToL2AtTx, + transferredToL2: deployment.transferredToL2, + transferredToL2At: deployment.transferredToL2At + ? BigNumber.from(deployment.transferredToL2At) + : null, + transferredToL2AtTx: deployment.transferredToL2AtTx, + transferredToL2AtBlockNumber: deployment.transferredToL2AtBlockNumber + ? BigNumber.from(deployment.transferredToL2AtBlockNumber) + : null, + ipfsHash: version.subgraphDeployment.ipfsHash, + protocolNetwork: this.networkCAIPID, + ready: null, + } + }) + }) + } catch (err) { + const error = indexerError(IndexerErrorCode.IE009, err.message) + this.logger.error(`Failed to query transferred subgraph deployments`, { + error, + }) + throw error } - return subgraphDeployment } async subgraphDeployments(): Promise { const deployments = [] const queryProgress = { - lastId: '', + lastCreatedAt: 0, first: 10, fetched: 0, exhausted: false, @@ -511,36 +594,26 @@ export class NetworkMonitor { queryProgress: queryProgress, }) try { - const result = await this.networkSubgraph.query( + const result = await this.networkSubgraph.checkedQuery( gql` - query subgraphDeployments($first: Int!, $lastId: String!) { + query subgraphDeployments($first: Int!, $lastCreatedAt: Int!) { subgraphDeployments( - where: { id_gt: $lastId } - orderBy: id + where: { createdAt_gt: $lastCreatedAt } + orderBy: createdAt orderDirection: asc first: $first ) { + createdAt id ipfsHash deniedAt stakedTokens signalledTokens queryFeesAmount - indexerAllocations { - indexer { - id - } - } - versions(first: 1, orderBy: version, orderDirection: desc) { - subgraph { - displayName - creatorAddress - } - } } } `, - { first: queryProgress.first, lastId: queryProgress.lastId }, + { first: queryProgress.first, lastCreatedAt: queryProgress.lastCreatedAt }, ) if (result.error) { @@ -559,8 +632,14 @@ export class NetworkMonitor { queryProgress.exhausted = networkDeployments.length < queryProgress.first queryProgress.fetched += networkDeployments.length - queryProgress.lastId = networkDeployments[networkDeployments.length - 1].id - deployments.push(...networkDeployments.map(parseGraphQLSubgraphDeployment)) + queryProgress.lastCreatedAt = + networkDeployments[networkDeployments.length - 1].createdAt + deployments.push( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...networkDeployments.map((x: any) => + parseGraphQLSubgraphDeployment(x, this.networkCAIPID), + ), + ) } catch (err) { queryProgress.retriesRemaining-- this.logger.warn(`Failed to query subgraph deployments`, { @@ -593,7 +672,7 @@ export class NetworkMonitor { const queryEpochSubgraph = async () => { // We know it is non-null because of the check above for a null case that will end execution of fn if true // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const result = await this.epochSubgraph!.query( + const result = await this.epochSubgraph!.checkedQuery( gql` query network($networkID: String!) { network(id: $networkID) { @@ -612,11 +691,6 @@ export class NetworkMonitor { } } } - _meta { - block { - number - } - } } `, { @@ -691,7 +765,7 @@ Please submit an issue at https://github.com/graphprotocol/block-oracle/issues/n if (networkID == this.networkCAIPID) { startBlockHash = (await this.ethereum.getBlock(+validBlock.blockNumber)).hash } else { - startBlockHash = await this.indexingStatusResolver.blockHashFromNumber( + startBlockHash = await this.graphNode.blockHashFromNumber( networkAlias, +validBlock.blockNumber, ) @@ -741,9 +815,9 @@ Please submit an issue at https://github.com/graphprotocol/block-oracle/issues/n async fetchPOIBlockPointer(allocation: Allocation): Promise { try { - const deploymentIndexingStatuses = await this.indexingStatusResolver.indexingStatus( - [allocation.subgraphDeployment.id], - ) + const deploymentIndexingStatuses = await this.graphNode.indexingStatus([ + allocation.subgraphDeployment.id, + ]) if ( deploymentIndexingStatuses.length != 1 || deploymentIndexingStatuses[0].chains.length != 1 || @@ -798,7 +872,7 @@ Please submit an issue at https://github.com/graphprotocol/block-oracle/issues/n return poi! case false: return ( - (await this.indexingStatusResolver.proofOfIndexing( + (await this.graphNode.proofOfIndexing( allocation.subgraphDeployment.id, await this.fetchPOIBlockPointer(allocation), allocation.indexer, @@ -809,7 +883,7 @@ Please submit an issue at https://github.com/graphprotocol/block-oracle/issues/n case false: { const epochStartBlock = await this.fetchPOIBlockPointer(allocation) // Obtain the start block of the current epoch - const generatedPOI = await this.indexingStatusResolver.proofOfIndexing( + const generatedPOI = await this.graphNode.proofOfIndexing( allocation.subgraphDeployment.id, epochStartBlock, allocation.indexer, @@ -817,7 +891,7 @@ Please submit an issue at https://github.com/graphprotocol/block-oracle/issues/n switch (poi == generatedPOI) { case true: if (poi == undefined) { - const deploymentStatus = await this.indexingStatusResolver.indexingStatus([ + const deploymentStatus = await this.graphNode.indexingStatus([ allocation.subgraphDeployment.id, ]) throw indexerError( @@ -855,18 +929,21 @@ Please submit an issue at https://github.com/graphprotocol/block-oracle/issues/n contracts: NetworkContracts, networkSubgraph: NetworkSubgraph, ): Promise> { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const initialPauseValue = await contracts.controller.paused().catch((_) => { + return false + }) return timer(60_000) .reduce(async (currentlyPaused) => { try { - const result = await networkSubgraph.query( - gql` - { - graphNetworks { - isPaused - } + logger.debug('Query network subgraph isPaused state') + const result = await networkSubgraph.checkedQuery(gql` + { + graphNetworks { + isPaused } - `, - ) + } + `) if (result.error) { throw result.error @@ -887,7 +964,7 @@ Please submit an issue at https://github.com/graphprotocol/block-oracle/issues/n }) return currentlyPaused } - }, await contracts.controller.paused()) + }, initialPauseValue) .map((paused) => { logger.info(paused ? `Network paused` : `Network active`) return paused @@ -907,18 +984,22 @@ Please submit an issue at https://github.com/graphprotocol/block-oracle/issues/n return mutable(true) } - return timer(60_000) - .reduce(async (isOperator) => { - try { - return await contracts.staking.isOperator(wallet.address, indexerAddress) - } catch (err) { - logger.warn( - `Failed to check operator status for indexer, assuming it has not changed`, - { err: indexerError(IndexerErrorCode.IE008, err), isOperator }, - ) - return isOperator - } - }, await contracts.staking.isOperator(wallet.address, indexerAddress)) + return timer(300_000) + .reduce( + async (isOperator) => { + try { + logger.debug('Check operator status') + return await contracts.staking.isOperator(wallet.address, indexerAddress) + } catch (err) { + logger.warn( + `Failed to check operator status for indexer, assuming it has not changed`, + { err: indexerError(IndexerErrorCode.IE008, err), isOperator }, + ) + return isOperator + } + }, + await contracts.staking.isOperator(wallet.address, indexerAddress), + ) .map((isOperator) => { logger.info( isOperator @@ -928,4 +1009,244 @@ Please submit an issue at https://github.com/graphprotocol/block-oracle/issues/n return isOperator }) } + + async claimableAllocations(disputableEpoch: number): Promise { + try { + this.logger.debug('Fetch claimable allocations', { + closedAtEpoch_lte: disputableEpoch, + queryFeesCollected_gte: this.indexerOptions.rebateClaimThreshold.toString(), + }) + const result = await this.networkSubgraph.checkedQuery( + gql` + query allocations( + $indexer: String! + $disputableEpoch: Int! + $minimumQueryFeesCollected: BigInt! + ) { + allocations( + where: { + indexer: $indexer + closedAtEpoch_lte: $disputableEpoch + queryFeesCollected_gte: $minimumQueryFeesCollected + status: Closed + } + first: 1000 + ) { + id + indexer { + id + } + queryFeesCollected + allocatedTokens + createdAtEpoch + closedAtEpoch + createdAtBlockHash + closedAtBlockHash + subgraphDeployment { + id + stakedTokens + signalledTokens + queryFeesAmount + } + } + } + `, + { + indexer: this.indexerOptions.address.toLocaleLowerCase(), + disputableEpoch, + minimumQueryFeesCollected: this.indexerOptions.rebateClaimThreshold.toString(), + }, + ) + + if (result.error) { + throw result.error + } + + const totalFees: BigNumber = result.data.allocations.reduce( + (total: BigNumber, rawAlloc: { queryFeesCollected: string }) => { + return total.add(BigNumber.from(rawAlloc.queryFeesCollected)) + }, + BigNumber.from(0), + ) + + const parsedAllocs: Allocation[] = + result.data.allocations.map(parseGraphQLAllocation) + + // If the total fees claimable do not meet the minimum required for batching, return an empty array + if ( + parsedAllocs.length > 0 && + totalFees.lt(this.indexerOptions.rebateClaimBatchThreshold) + ) { + this.logger.info( + `Allocation rebate batch value does not meet minimum for claiming`, + { + batchValueGRT: formatGRT(totalFees), + rebateClaimBatchThreshold: formatGRT( + this.indexerOptions.rebateClaimBatchThreshold, + ), + rebateClaimMaxBatchSize: this.indexerOptions.rebateClaimMaxBatchSize, + batchSize: parsedAllocs.length, + allocations: parsedAllocs.map((allocation) => { + return { + allocation: allocation.id, + deployment: allocation.subgraphDeployment.id.display, + createdAtEpoch: allocation.createdAtEpoch, + closedAtEpoch: allocation.closedAtEpoch, + createdAtBlockHash: allocation.createdAtBlockHash, + } + }), + }, + ) + return [] + } + // Otherwise return the allos for claiming since the batch meets the minimum + return parsedAllocs + } catch (error) { + const err = indexerError(IndexerErrorCode.IE011, error) + this.logger.error(INDEXER_ERROR_MESSAGES[IndexerErrorCode.IE011], { + err, + }) + throw err + } + } + async disputableAllocations( + currentEpoch: number, + deployments: SubgraphDeploymentID[], + minimumAllocation: number, + ): Promise { + const logger = this.logger.child({ component: 'POI Monitor' }) + if (!this.indexerOptions.poiDisputeMonitoring) { + logger.trace('POI monitoring disabled, skipping') + return Promise.resolve([]) + } + + logger.debug('Query network for any potentially disputable allocations') + + let dataRemaining = true + let allocations: Allocation[] = [] + + try { + const zeroPOI = utils.hexlify(Array(32).fill(0)) + const disputableEpoch = currentEpoch - this.indexerOptions.poiDisputableEpochs + let lastCreatedAt = 0 + while (dataRemaining) { + const result = await this.networkSubgraph.checkedQuery( + gql` + query allocations( + $deployments: [String!]! + $minimumAllocation: Int! + $disputableEpoch: Int! + $zeroPOI: String! + $createdAt: Int! + ) { + allocations( + where: { + createdAt_gt: $createdAt + subgraphDeployment_in: $deployments + allocatedTokens_gt: $minimumAllocation + closedAtEpoch_gte: $disputableEpoch + status: Closed + poi_not: $zeroPOI + } + first: 1000 + orderBy: createdAt + orderDirection: asc + ) { + id + createdAt + indexer { + id + } + poi + allocatedTokens + createdAtEpoch + closedAtEpoch + closedAtBlockHash + subgraphDeployment { + id + stakedTokens + signalledTokens + queryFeesAmount + } + } + } + `, + { + deployments: deployments.map((subgraph) => subgraph.bytes32), + minimumAllocation, + disputableEpoch, + createdAt: lastCreatedAt, + zeroPOI, + }, + ) + + if (result.error) { + throw result.error + } + if (result.data.allocations.length == 0) { + dataRemaining = false + } else { + lastCreatedAt = result.data.allocations.slice(-1)[0].createdAt + const parsedResult: Allocation[] = + result.data.allocations.map(parseGraphQLAllocation) + allocations = allocations.concat(parsedResult) + } + } + + // Get the unique set of dispute epochs to reduce the work fetching epoch start block hashes in the next step + const disputableEpochs = await this.epochs([ + ...allocations.reduce((epochNumbers: Set, allocation: Allocation) => { + epochNumbers.add(allocation.closedAtEpoch) + epochNumbers.add(allocation.closedAtEpoch - 1) + return epochNumbers + }, new Set()), + ]) + const availableEpochs = await pMap( + disputableEpochs, + async (epoch) => { + try { + const startBlock = await this.ethereum.getBlock(epoch.startBlock) + epoch.startBlockHash = startBlock?.hash + } catch { + logger.debug('Failed to fetch block hash for startBlock of epoch', { + epoch: epoch.id, + startBlock: epoch.startBlock, + }) + } + + return epoch + }, + { + stopOnError: true, + concurrency: 1, + }, + ) + availableEpochs.filter((epoch) => !!epoch.startBlockHash) + + return await pMap( + allocations, + async (allocation) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + allocation.closedAtEpochStartBlockHash = availableEpochs.find( + (epoch) => epoch.id == allocation.closedAtEpoch, + )?.startBlockHash + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + allocation.previousEpochStartBlockHash = availableEpochs.find( + (epoch) => epoch.id == allocation.closedAtEpoch - 1, + )?.startBlockHash + return allocation + }, + { + stopOnError: true, + concurrency: 1, + }, + ) + } catch (error) { + const err = indexerError(IndexerErrorCode.IE037, error) + logger.error(INDEXER_ERROR_MESSAGES.IE037, { + err, + }) + throw err + } + } } diff --git a/packages/indexer-common/src/indexer-management/resolvers/actions.ts b/packages/indexer-common/src/indexer-management/resolvers/actions.ts index 8f6eb0b26..7baa422bc 100644 --- a/packages/indexer-common/src/indexer-management/resolvers/actions.ts +++ b/packages/indexer-common/src/indexer-management/resolvers/actions.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/ban-types */ - import { IndexerManagementResolverContext } from '../client' import { Logger } from '@graphprotocol/common-ts' import { @@ -12,11 +11,15 @@ import { ActionType, ActionUpdateInput, IndexerManagementModels, + Network, + NetworkMapped, OrderDirection, validateActionInputs, + validateNetworkIdentifier, } from '@graphprotocol/indexer-common' import { literal, Op, Transaction } from 'sequelize' import { ActionManager } from '../actions' +import groupBy from 'lodash.groupby' // Perform insert, update, or no-op depending on existing queue data // INSERT - No item in the queue yet targeting this deploymentID @@ -48,9 +51,12 @@ async function executeQueueOperation( // Check for duplicated actions const duplicateActions = actionsAwaitingExecution.filter( - (a) => a.deploymentID === action.deploymentID, + (a) => + a.deploymentID === action.deploymentID && + a.protocolNetwork === action.protocolNetwork, ) if (duplicateActions.length === 0) { + logger.trace('Inserting Action in database', { action }) return [ await models.Action.create(action, { validate: true, @@ -64,7 +70,7 @@ async function executeQueueOperation( duplicateActions[0].status === action.status ) { // TODO: Log this only when update will actually change existing item - logger.info( + logger.trace( `Action found in queue that effects the same deployment as proposed queue action, updating existing action`, { actionInQueue: duplicateActions, @@ -144,13 +150,31 @@ export default { queueActions: async ( { actions }: { actions: ActionInput[] }, - { actionManager, logger, networkMonitor, models }: IndexerManagementResolverContext, + { actionManager, logger, multiNetworks, models }: IndexerManagementResolverContext, ): Promise => { logger.debug(`Execute 'queueActions' mutation`, { actions, }) - await validateActionInputs(actions, actionManager, networkMonitor) + if (!actionManager || !multiNetworks) { + throw Error('IndexerManagementClient must be in `network` mode to modify actions') + } + + // Sanitize protocol network identifier + actions.forEach((action) => { + try { + action.protocolNetwork = validateNetworkIdentifier(action.protocolNetwork) + } catch (e) { + throw Error(`Invalid value for the field 'protocolNetwork'. ${e}`) + } + }) + + // Let Network Monitors validate actions based on their protocol networks + await multiNetworks.mapNetworkMapped( + groupBy(actions, (action) => action.protocolNetwork), + (network: Network, actions: ActionInput[]) => + validateActionInputs(actions, network.networkMonitor), + ) const alreadyQueuedActions = await ActionManager.fetchActions(models, { status: ActionStatus.QUEUED, @@ -316,15 +340,40 @@ export default { executeApprovedActions: async ( _: unknown, - { logger, actionManager }: IndexerManagementResolverContext, - ): Promise => { - logger.debug(`Execute 'executeApprovedActions' mutation`) - return await actionManager.executeApprovedActions() + { logger: parentLogger, actionManager }: IndexerManagementResolverContext, + ): Promise => { + const logger = parentLogger.child({ function: 'executeApprovedActions' }) + logger.trace(`Begin executing 'executeApprovedActions' mutation`) + if (!actionManager) { + throw Error('IndexerManagementClient must be in `network` mode to modify actions') + } + const result: NetworkMapped = await actionManager.multiNetworks.map( + async (network: Network) => { + logger.debug(`Execute 'executeApprovedActions' mutation`, { + protocolNetwork: network.specification.networkIdentifier, + }) + try { + return await actionManager.executeApprovedActions(network) + } catch (error) { + logger.error('Failed to execute approved actions for network', { + protocolNetwork: network.specification.networkIdentifier, + error, + }) + return [] + } + }, + ) + return Object.values(result).flat() }, } // Helper function to assess equality among a enqueued and a proposed actions function compareActions(enqueued: Action, proposed: ActionInput): boolean { + // actions are not the same if they target different protocol networks + if (enqueued.protocolNetwork !== proposed.protocolNetwork) { + return false + } + // actions are not the same if they target different deployments if (enqueued.deploymentID !== proposed.deploymentID) { return false diff --git a/packages/indexer-common/src/indexer-management/resolvers/allocations.ts b/packages/indexer-common/src/indexer-management/resolvers/allocations.ts index 6f2fa9891..3bd945a46 100644 --- a/packages/indexer-common/src/indexer-management/resolvers/allocations.ts +++ b/packages/indexer-common/src/indexer-management/resolvers/allocations.ts @@ -1,7 +1,7 @@ import { NetworkMonitor, epochElapsedBlocks, - formatDeploymentName, + Network, } from '@graphprotocol/indexer-common' /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/ban-types */ @@ -14,7 +14,6 @@ import { Address, formatGRT, Logger, - NetworkContracts, parseGRT, SubgraphDeploymentID, toAddress, @@ -30,25 +29,25 @@ import { IndexerManagementResolverContext, IndexingDecisionBasis, IndexingRuleAttributes, - IndexingStatusResolver, + GraphNode, NetworkSubgraph, ReallocateAllocationResult, SubgraphIdentifierType, - TransactionManager, uniqueAllocationID, } from '@graphprotocol/indexer-common' +import { extractNetwork } from './utils' interface AllocationFilter { - status: 'active' | 'closed' | 'claimable' + status: 'active' | 'closed' allocation: string | null subgraphDeployment: string | null + protocolNetwork: string | null } enum AllocationQuery { all = 'all', active = 'active', closed = 'closed', - claimable = 'claimable', allocation = 'allocation', } @@ -68,6 +67,7 @@ interface AllocationInfo { indexingRewards: string queryFeesCollected: string status: string + protocolNetwork: string } const ALLOCATION_QUERIES = { @@ -113,30 +113,6 @@ const ALLOCATION_QUERIES = { } } `, - [AllocationQuery.claimable]: gql` - query allocations($indexer: String!, $disputableEpoch: Int!) { - allocations( - where: { indexer: $indexer, closedAtEpoch_lte: $disputableEpoch, status: Closed } - first: 1000 - ) { - id - subgraphDeployment { - id - stakedTokens - signalledTokens - } - indexer { - id - } - allocatedTokens - createdAtEpoch - closedAtEpoch - indexingRewards - queryFeesCollected - status - } - } - `, [AllocationQuery.closed]: gql` query allocations($indexer: String!) { allocations(where: { indexer: $indexer, status: Closed }, first: 1000) { @@ -184,12 +160,10 @@ const ALLOCATION_QUERIES = { async function queryAllocations( logger: Logger, networkSubgraph: NetworkSubgraph, - contracts: NetworkContracts, variables: { indexer: Address - disputableEpoch: number allocation: Address | null - status: 'active' | 'closed' | 'claimable' | null + status: 'active' | 'closed' | null }, context: { currentEpoch: number @@ -198,6 +172,7 @@ async function queryAllocations( maxAllocationEpochs: number blocksPerEpoch: number avgBlockTime: number + protocolNetwork: string }, ): Promise { logger.trace('Query Allocations', { @@ -217,12 +192,6 @@ async function queryAllocations( filterVars = { indexer: variables.indexer.toLowerCase(), } - } else if (variables.status == 'claimable') { - filterType = AllocationQuery.claimable - filterVars = { - indexer: variables.indexer.toLowerCase(), - disputableEpoch: variables.disputableEpoch, - } } else if (variables.status == 'active') { filterType = AllocationQuery.active filterVars = { @@ -240,9 +209,13 @@ async function queryAllocations( ) } - const result = await networkSubgraph.query(ALLOCATION_QUERIES[filterType], filterVars) + const result = await networkSubgraph.checkedQuery( + ALLOCATION_QUERIES[filterType], + filterVars, + ) if (result.data.allocations.length == 0) { + // TODO: Is 'Claimable' still the correct term here, after Exponential Rebates? logger.info(`No 'Claimable' allocations found`) return [] } @@ -285,6 +258,7 @@ async function queryAllocations( indexingRewards: allocation.indexingRewards, queryFeesCollected: allocation.queryFeesCollected, status: allocation.status, + protocolNetwork: context.protocolNetwork, } }, ) @@ -292,8 +266,7 @@ async function queryAllocations( async function resolvePOI( networkMonitor: NetworkMonitor, - transactionManager: TransactionManager, - indexingStatusResolver: IndexingStatusResolver, + graphNode: GraphNode, allocation: Allocation, poi: string | undefined, force: boolean, @@ -310,7 +283,7 @@ async function resolvePOI( return poi! case false: return ( - (await indexingStatusResolver.proofOfIndexing( + (await graphNode.proofOfIndexing( allocation.subgraphDeployment.id, await networkMonitor.fetchPOIBlockPointer(allocation), allocation.indexer, @@ -320,7 +293,7 @@ async function resolvePOI( break case false: { const currentEpochStartBlock = await networkMonitor.fetchPOIBlockPointer(allocation) - const generatedPOI = await indexingStatusResolver.proofOfIndexing( + const generatedPOI = await graphNode.proofOfIndexing( allocation.subgraphDeployment.id, currentEpochStartBlock, allocation.indexer, @@ -328,7 +301,7 @@ async function resolvePOI( switch (poi == generatedPOI) { case true: if (poi == undefined) { - const deploymentStatus = await indexingStatusResolver.indexingStatus([ + const deploymentStatus = await graphNode.indexingStatus([ allocation.subgraphDeployment.id, ]) throw indexerError( @@ -352,8 +325,8 @@ async function resolvePOI( } throw indexerError( IndexerErrorCode.IE068, - `User provided POI does not match reference fetched from the graph-node. Use '--force' to bypass this POI accuracy check. - POI: ${poi}, + `User provided POI does not match reference fetched from the graph-node. Use '--force' to bypass this POI accuracy check. + POI: ${poi}, referencePOI: ${generatedPOI}`, ) } @@ -364,74 +337,108 @@ async function resolvePOI( export default { allocations: async ( { filter }: { filter: AllocationFilter }, - { - address, - contracts, - logger, - networkSubgraph, - networkMonitor, - }: IndexerManagementResolverContext, - ): Promise => { + { multiNetworks, logger }: IndexerManagementResolverContext, + ): Promise => { logger.debug('Execute allocations() query', { filter, }) - - const allocations: AllocationInfo[] = [] - - const currentEpoch = await networkMonitor.networkCurrentEpoch() - const disputeEpochs = await contracts.staking.channelDisputeEpochs() - const variables = { - indexer: toAddress(address), - disputableEpoch: currentEpoch.epochNumber - disputeEpochs, - allocation: filter.allocation - ? filter.allocation === 'all' - ? null - : toAddress(filter.allocation) - : null, - status: filter.status, - } - const context = { - currentEpoch: currentEpoch.epochNumber, - currentEpochStartBlock: currentEpoch.startBlockNumber, - currentEpochElapsedBlocks: epochElapsedBlocks(currentEpoch), - latestBlock: currentEpoch.latestBlock, - maxAllocationEpochs: await contracts.staking.maxAllocationEpochs(), - blocksPerEpoch: (await contracts.epochManager.epochLength()).toNumber(), - avgBlockTime: 13_000, + if (!multiNetworks) { + throw Error( + 'IndexerManagementClient must be in `network` mode to fetch allocations', + ) } - allocations.push( - ...(await queryAllocations(logger, networkSubgraph, contracts, variables, context)), + const allocationsByNetwork = await multiNetworks.map( + async (network: Network): Promise => { + // Return early if a different protocol network is specifically requested + if ( + filter.protocolNetwork && + filter.protocolNetwork !== network.specification.networkIdentifier + ) { + return [] + } + + const { + networkMonitor, + networkSubgraph, + contracts, + specification: { + indexerOptions: { address }, + }, + } = network + + const [currentEpoch, maxAllocationEpochs, epochLength] = await Promise.all([ + networkMonitor.networkCurrentEpoch(), + contracts.staking.maxAllocationEpochs(), + contracts.epochManager.epochLength(), + ]) + + const allocation = filter.allocation + ? filter.allocation === 'all' + ? null + : toAddress(filter.allocation) + : null + + const variables = { + indexer: toAddress(address), + allocation, + status: filter.status, + } + + const context = { + currentEpoch: currentEpoch.epochNumber, + currentEpochStartBlock: currentEpoch.startBlockNumber, + currentEpochElapsedBlocks: epochElapsedBlocks(currentEpoch), + latestBlock: currentEpoch.latestBlock, + maxAllocationEpochs, + blocksPerEpoch: epochLength.toNumber(), + avgBlockTime: 13000, + protocolNetwork: network.specification.networkIdentifier, + } + + return queryAllocations(logger, networkSubgraph, variables, context) + }, ) - return allocations + + return Object.values(allocationsByNetwork).flat() }, createAllocation: async ( { deployment, amount, - indexNode, - }: { deployment: string; amount: string; indexNode: string | undefined }, - { - address, - contracts, - subgraphManager, - logger, - models, - networkMonitor, - transactionManager, - }: IndexerManagementResolverContext, + protocolNetwork, + }: { + deployment: string + amount: string + protocolNetwork: string + }, + { multiNetworks, graphNode, logger, models }: IndexerManagementResolverContext, ): Promise => { - logger.debug('Execute createAllocation() mutation', { deployment, amount }) + logger.debug('Execute createAllocation() mutation', { + deployment, + amount, + protocolNetwork, + }) + if (!multiNetworks) { + throw Error( + 'IndexerManagementClient must be in `network` mode to fetch allocations', + ) + } + const network = extractNetwork(protocolNetwork, multiNetworks) + const networkMonitor = network.networkMonitor + const contracts = network.contracts + const transactionManager = network.transactionManager + const address = network.specification.indexerOptions.address const allocationAmount = parseGRT(amount) - const subgraphDeploymentID = new SubgraphDeploymentID(deployment) + const subgraphDeployment = new SubgraphDeploymentID(deployment) const activeAllocations = await networkMonitor.allocations(AllocationStatus.ACTIVE) const allocation = activeAllocations.find( (allocation) => - allocation.subgraphDeployment.id.toString() === subgraphDeploymentID.toString(), + allocation.subgraphDeployment.id.toString() === subgraphDeployment.toString(), ) if (allocation) { logger.warn('Already allocated to deployment', { @@ -479,25 +486,10 @@ export default { ) } - const subgraphDeployment = await networkMonitor.requireSubgraphDeployment( - subgraphDeploymentID.ipfsHash, - ) - - // Ensure grafting dependencies are resolved - await subgraphManager.resolveGrafting( - logger, - models, - subgraphDeploymentID, - indexNode, - 0, - ) // Ensure subgraph is deployed before allocating - await subgraphManager.ensure( - logger, - models, - formatDeploymentName(subgraphDeployment), - subgraphDeploymentID, - indexNode, + await graphNode.ensure( + `indexer-agent/${subgraphDeployment.ipfsHash.slice(-10)}`, + subgraphDeployment, ) logger.debug('Obtain a unique Allocation ID') @@ -506,7 +498,7 @@ export default { const { allocationSigner, allocationId } = uniqueAllocationID( transactionManager.wallet.mnemonic.phrase, currentEpoch.toNumber(), - subgraphDeploymentID, + subgraphDeployment, activeAllocations.map((allocation) => allocation.id), ) @@ -514,7 +506,7 @@ export default { // avoid unnecessary transactions. // Note: We're checking the allocation state here, which is defined as // - // enum AllocationState { Null, Active, Closed, Finalized, Claimed } + // enum AllocationState { Null, Active, Closed, Finalized } // // in the contracts. const state = await contracts.staking.getAllocationState(allocationId) @@ -544,17 +536,18 @@ export default { logger.debug(`Sending allocateFrom transaction`, { indexer: address, - subgraphDeployment: subgraphDeploymentID.ipfsHash, + subgraphDeployment: subgraphDeployment.ipfsHash, amount: formatGRT(allocationAmount), allocation: allocationId, proof, + protocolNetwork, }) const receipt = await transactionManager.executeTransaction( async () => contracts.staking.estimateGas.allocateFrom( address, - subgraphDeploymentID.bytes32, + subgraphDeployment.bytes32, allocationAmount, allocationId, utils.hexlify(Array(32).fill(0)), @@ -563,7 +556,7 @@ export default { async (gasLimit) => contracts.staking.allocateFrom( address, - subgraphDeploymentID.bytes32, + subgraphDeployment.bytes32, allocationAmount, allocationId, utils.hexlify(Array(32).fill(0)), @@ -582,38 +575,38 @@ export default { ) } - const events = receipt.events || receipt.logs - const event = - // eslint-disable-next-line @typescript-eslint/no-explicit-any - events.find((event: any) => - event.topics.includes( - contracts.staking.interface.getEventTopic('AllocationCreated'), - ), - ) - if (!event) { - throw indexerError(IndexerErrorCode.IE014, `Allocation was never mined`) - } - - const createEvent = contracts.staking.interface.decodeEventLog( + const createAllocationEventLogs = network.transactionManager.findEvent( 'AllocationCreated', - event.data, - event.topics, + network.contracts.staking.interface, + 'subgraphDeploymentID', + subgraphDeployment.toString(), + receipt, + logger, ) + if (!createAllocationEventLogs) { + throw indexerError( + IndexerErrorCode.IE014, + `Allocation create transaction was never mined`, + ) + } + logger.info(`Successfully allocated to subgraph deployment`, { - amountGRT: formatGRT(createEvent.tokens), - allocation: createEvent.allocationID, - epoch: createEvent.epoch.toString(), + amountGRT: formatGRT(createAllocationEventLogs.tokens), + allocation: createAllocationEventLogs.allocationID, + epoch: createAllocationEventLogs.epoch.toString(), + transaction: receipt.transactionHash, }) logger.debug( `Updating indexing rules, so indexer-agent will now manage the active allocation`, ) const indexingRule = { - identifier: subgraphDeploymentID.ipfsHash, + identifier: subgraphDeployment.ipfsHash, allocationAmount: allocationAmount.toString(), identifierType: SubgraphIdentifierType.DEPLOYMENT, decisionBasis: IndexingDecisionBasis.ALWAYS, + protocolNetwork, } as Partial await models.IndexingRule.upsert(indexingRule) @@ -632,8 +625,9 @@ export default { type: 'allocate', transactionID: receipt.transactionHash, deployment, - allocation: createEvent.allocationID, + allocation: createAllocationEventLogs.allocationID, allocatedTokens: formatGRT(allocationAmount.toString()), + protocolNetwork, } } catch (error) { logger.error(`Failed to allocate`, { @@ -649,21 +643,29 @@ export default { allocation, poi, force, - }: { allocation: string; poi: string | undefined; force: boolean }, - { - contracts, - indexingStatusResolver, - logger, - models, - networkMonitor, - transactionManager, - receiptCollector, - }: IndexerManagementResolverContext, + protocolNetwork, + }: { + allocation: string + poi: string | undefined + force: boolean + protocolNetwork: string + }, + { graphNode, logger, models, multiNetworks }: IndexerManagementResolverContext, ): Promise => { logger.debug('Execute closeAllocation() mutation', { allocationID: allocation, poi: poi || 'none provided', }) + if (!multiNetworks) { + throw Error( + 'IndexerManagementClient must be in `network` mode to fetch allocations', + ) + } + const network = extractNetwork(protocolNetwork, multiNetworks) + const networkMonitor = network.networkMonitor + const contracts = network.contracts + const transactionManager = network.transactionManager + const receiptCollector = network.receiptCollector const allocationData = await networkMonitor.allocation(allocation) @@ -681,20 +683,13 @@ export default { ) } - poi = await resolvePOI( - networkMonitor, - transactionManager, - indexingStatusResolver, - allocationData, - poi, - force, - ) + poi = await resolvePOI(networkMonitor, graphNode, allocationData, poi, force) // Double-check whether the allocation is still active on chain, to // avoid unnecessary transactions. // Note: We're checking the allocation state here, which is defined as // - // enum AllocationState { Null, Active, Closed, Finalized, Claimed } + // enum AllocationState { Null, Active, Closed, Finalized } // // in the contracts. const state = await contracts.staking.getAllocationState(allocationData.id) @@ -721,41 +716,32 @@ export default { ) } - const events = receipt.events || receipt.logs + const closeAllocationEventLogs = transactionManager.findEvent( + 'AllocationClosed', + contracts.staking.interface, + 'allocationID', + allocation, + receipt, + logger, + ) - const closeEvent = - // eslint-disable-next-line @typescript-eslint/no-explicit-any - events.find((event: any) => - event.topics.includes( - contracts.staking.interface.getEventTopic('AllocationClosed'), - ), - ) - if (!closeEvent) { + if (!closeAllocationEventLogs) { throw indexerError( - IndexerErrorCode.IE014, + IndexerErrorCode.IE015, `Allocation close transaction was never successfully mined`, ) } - const closeAllocationEventLogs = contracts.staking.interface.decodeEventLog( - 'AllocationClosed', - closeEvent.data, - closeEvent.topics, + + const rewardsEventLogs = transactionManager.findEvent( + 'RewardsAssigned', + contracts.rewardsManager.interface, + 'allocationID', + allocation, + receipt, + logger, ) - const rewardsEvent = - // eslint-disable-next-line @typescript-eslint/no-explicit-any - events.find((event: any) => - event.topics.includes( - contracts.rewardsManager.interface.getEventTopic('RewardsAssigned'), - ), - ) - const rewardsAssigned = rewardsEvent - ? contracts.rewardsManager.interface.decodeEventLog( - 'RewardsAssigned', - rewardsEvent.data, - rewardsEvent.topics, - ).amount - : 0 + const rewardsAssigned = rewardsEventLogs ? rewardsEventLogs.amount : 0 if (rewardsAssigned == 0) { logger.warn('No rewards were distributed upon closing the allocation') @@ -787,6 +773,7 @@ export default { `Updating indexing rules, so indexer-agent keeps the deployment synced but doesn't reallocate to it`, ) const offchainIndexingRule = { + protocolNetwork: network.specification.networkIdentifier, identifier: allocationData.subgraphDeployment.id.ipfsHash, identifierType: SubgraphIdentifierType.DEPLOYMENT, decisionBasis: IndexingDecisionBasis.OFFCHAIN, @@ -811,6 +798,7 @@ export default { allocatedTokens: formatGRT(closeAllocationEventLogs.tokens), indexingRewards: formatGRT(rewardsAssigned), receiptsWorthCollecting: isCollectingQueryFees, + protocolNetwork: network.specification.networkIdentifier, } } catch (error) { logger.error(error.toString()) @@ -824,17 +812,15 @@ export default { poi, amount, force, - }: { allocation: string; poi: string | undefined; amount: string; force: boolean }, - { - address, - contracts, - indexingStatusResolver, - logger, - models, - networkMonitor, - transactionManager, - receiptCollector, - }: IndexerManagementResolverContext, + protocolNetwork, + }: { + allocation: string + poi: string | undefined + amount: string + force: boolean + protocolNetwork: string + }, + { graphNode, logger, models, multiNetworks }: IndexerManagementResolverContext, ): Promise => { logger = logger.child({ component: 'reallocateAllocationResolver', @@ -847,6 +833,20 @@ export default { force, }) + if (!multiNetworks) { + throw Error( + 'IndexerManagementClient must be in `network` mode to fetch allocations', + ) + } + + // Obtain the Network object and its associated components and data + const network = extractNetwork(protocolNetwork, multiNetworks) + const networkMonitor = network.networkMonitor + const contracts = network.contracts + const transactionManager = network.transactionManager + const receiptCollector = network.receiptCollector + const address = network.specification.indexerOptions.address + const allocationAmount = parseGRT(amount) const activeAllocations = await networkMonitor.allocations(AllocationStatus.ACTIVE) @@ -880,8 +880,7 @@ export default { logger.debug('Resolving POI') const allocationPOI = await resolvePOI( networkMonitor, - transactionManager, - indexingStatusResolver, + graphNode, allocationData, poi, force, @@ -895,7 +894,7 @@ export default { // avoid unnecessary transactions. // Note: We're checking the allocation state here, which is defined as // - // enum AllocationState { Null, Active, Closed, Finalized, Claimed } + // enum AllocationState { Null, Active, Closed, Finalized } // // in the contracts. const state = await contracts.staking.getAllocationState(allocationData.id) @@ -958,12 +957,11 @@ export default { // avoid unnecessary transactions. // Note: We're checking the allocation state here, which is defined as // - // enum AllocationState { Null, Active, Closed, Finalized, Claimed } + // enum AllocationState { Null, Active, Closed, Finalized } // // in the contracts. - const newAllocationState = await contracts.staking.getAllocationState( - newAllocationId, - ) + const newAllocationState = + await contracts.staking.getAllocationState(newAllocationId) if (newAllocationState !== 0) { logger.warn(`Skipping Allocation as it already exists onchain`, { indexer: address, @@ -983,7 +981,7 @@ export default { allocationIDProof: proof, }) - logger.info(`Sending closeAndAllocate transaction`, { + logger.info(`Sending close and allocate multicall transaction`, { indexer: address, amount: formatGRT(allocationAmount), oldAllocation: allocationData.id, @@ -995,31 +993,27 @@ export default { epoch: currentEpoch.toString(), }) + const callData = [ + await contracts.staking.populateTransaction.closeAllocation( + allocationData.id, + allocationPOI, + ), + await contracts.staking.populateTransaction.allocateFrom( + address, + allocationData.subgraphDeployment.id.bytes32, + allocationAmount, + newAllocationId, + utils.hexlify(Array(32).fill(0)), // metadata + proof, + ), + ].map((tx) => tx.data as string) + const receipt = await transactionManager.executeTransaction( - async () => - contracts.staking.estimateGas.closeAndAllocate( - allocationData.id, - allocationPOI, - address, - allocationData.subgraphDeployment.id.bytes32, - allocationAmount, - newAllocationId, - utils.hexlify(Array(32).fill(0)), // metadata - proof, - ), - async (gasLimit) => - contracts.staking.closeAndAllocate( - allocationData.id, - allocationPOI, - address, - allocationData.subgraphDeployment.id.bytes32, - allocationAmount, - newAllocationId, - utils.hexlify(Array(32).fill(0)), // metadata - proof, - { gasLimit }, - ), - logger.child({ action: 'closeAndAllocate' }), + async () => contracts.staking.estimateGas.multicall(callData), + async (gasLimit) => contracts.staking.multicall(callData, { gasLimit }), + logger.child({ + function: 'closeAndAllocate', + }), ) if (receipt === 'paused' || receipt === 'unauthorized') { @@ -1029,57 +1023,45 @@ export default { ) } - const events = receipt.events || receipt.logs - const createEvent = - // eslint-disable-next-line @typescript-eslint/no-explicit-any - events.find((event: any) => - event.topics.includes( - contracts.staking.interface.getEventTopic('AllocationCreated'), - ), - ) - if (!createEvent) { + const createAllocationEventLogs = transactionManager.findEvent( + 'AllocationCreated', + contracts.staking.interface, + 'subgraphDeploymentID', + allocationData.subgraphDeployment.id.toString(), + receipt, + logger, + ) + + if (!createAllocationEventLogs) { throw indexerError(IndexerErrorCode.IE014, `Allocation was never mined`) } - const createAllocationEventLogs = contracts.staking.interface.decodeEventLog( - 'AllocationCreated', - createEvent.data, - createEvent.topics, + const closeAllocationEventLogs = transactionManager.findEvent( + 'AllocationClosed', + contracts.staking.interface, + 'allocationID', + allocation, + receipt, + logger, ) - const closeEvent = - // eslint-disable-next-line @typescript-eslint/no-explicit-any - events.find((event: any) => - event.topics.includes( - contracts.staking.interface.getEventTopic('AllocationClosed'), - ), - ) - if (!closeEvent) { + if (!closeAllocationEventLogs) { throw indexerError( - IndexerErrorCode.IE014, + IndexerErrorCode.IE015, `Allocation close transaction was never successfully mined`, ) } - const closeAllocationEventLogs = contracts.staking.interface.decodeEventLog( - 'AllocationClosed', - closeEvent.data, - closeEvent.topics, + + const rewardsEventLogs = transactionManager.findEvent( + 'RewardsAssigned', + contracts.rewardsManager.interface, + 'allocationID', + allocation, + receipt, + logger, ) - const rewardsEvent = - // eslint-disable-next-line @typescript-eslint/no-explicit-any - events.find((event: any) => - event.topics.includes( - contracts.rewardsManager.interface.getEventTopic('RewardsAssigned'), - ), - ) - const rewardsAssigned = rewardsEvent - ? contracts.rewardsManager.interface.decodeEventLog( - 'RewardsAssigned', - rewardsEvent.data, - rewardsEvent.topics, - ).amount - : 0 + const rewardsAssigned = rewardsEventLogs ? rewardsEventLogs.amount : 0 if (rewardsAssigned == 0) { logger.warn('No rewards were distributed upon closing the allocation') @@ -1117,6 +1099,7 @@ export default { allocationAmount: allocationAmount.toString(), identifierType: SubgraphIdentifierType.DEPLOYMENT, decisionBasis: IndexingDecisionBasis.ALWAYS, + protocolNetwork, } as Partial await models.IndexingRule.upsert(indexingRule) @@ -1139,6 +1122,7 @@ export default { receiptsWorthCollecting: isCollectingQueryFees, createdAllocation: createAllocationEventLogs.allocationID, createdAllocationStake: formatGRT(createAllocationEventLogs.tokens), + protocolNetwork, } } catch (error) { logger.error(error.toString()) diff --git a/packages/indexer-common/src/indexer-management/resolvers/cost-models.ts b/packages/indexer-common/src/indexer-management/resolvers/cost-models.ts index 0e3aad244..c8a9393d1 100644 --- a/packages/indexer-common/src/indexer-management/resolvers/cost-models.ts +++ b/packages/indexer-common/src/indexer-management/resolvers/cost-models.ts @@ -95,8 +95,12 @@ export default { setCostModel: async ( { costModel }: { deployment: string; costModel: GraphQLCostModel }, - { models, features, dai }: IndexerManagementResolverContext, + { models, multiNetworks, dai }: IndexerManagementResolverContext, ): Promise => { + if (!multiNetworks) { + throw Error('IndexerManagementClient must be in `network` mode to set cost models') + } + const update = parseGraphQLCostModel(costModel) // Validate cost model @@ -107,7 +111,13 @@ export default { } catch (err) { throw new Error(`Invalid cost model or variables: ${err.message}`) } - + const network = multiNetworks.inner['eip155:1'] + if (!network) { + throw new Error( + `Can't set cost model: Indexer Agent does not have Ethereum Mainnet network configured.`, + ) + } + const injectDai = !!network.specification.dai.inject const [model] = await models.CostModel.findOrBuild({ where: { deployment: update.deployment }, }) @@ -120,7 +130,7 @@ export default { // Update the model variables (fall back to current value if unchanged) let variables = update.variables || model.variables - if (features.injectDai) { + if (injectDai) { const oldDai = getVariable(model.variables, 'DAI') const newDai = getVariable(update.variables, 'DAI') diff --git a/packages/indexer-common/src/indexer-management/resolvers/indexer-status.ts b/packages/indexer-common/src/indexer-management/resolvers/indexer-status.ts index 23fb4d3a0..3564fe605 100644 --- a/packages/indexer-common/src/indexer-management/resolvers/indexer-status.ts +++ b/packages/indexer-common/src/indexer-management/resolvers/indexer-status.ts @@ -4,8 +4,13 @@ import geohash from 'ngeohash' import gql from 'graphql-tag' import { IndexerManagementResolverContext } from '../client' import { SubgraphDeploymentID } from '@graphprotocol/common-ts' -import { indexerError, IndexerErrorCode } from '@graphprotocol/indexer-common' - +import { + indexerError, + IndexerErrorCode, + Network, + validateNetworkIdentifier, +} from '@graphprotocol/indexer-common' +import { extractNetwork } from './utils' interface Test { test: (url: string) => string run: (url: string) => Promise @@ -57,15 +62,26 @@ const URL_VALIDATION_TEST: Test = { export default { indexerRegistration: async ( - _: {}, - { address, contracts }: IndexerManagementResolverContext, + { protocolNetwork: unvalidatedProtocolNetwork }: { protocolNetwork: string }, + { multiNetworks }: IndexerManagementResolverContext, ): Promise => { + if (!multiNetworks) { + throw Error( + 'IndexerManagementClient must be in `network` mode to fetch indexer registration information', + ) + } + + const network = extractNetwork(unvalidatedProtocolNetwork, multiNetworks) + const protocolNetwork = network.specification.networkIdentifier + const address = network.specification.indexerOptions.address + const contracts = network.contracts const registered = await contracts.serviceRegistry.isRegistered(address) if (registered) { const service = await contracts.serviceRegistry.services(address) return { address, + protocolNetwork, url: service.url, location: geohash.decode(service.geohash), registered, @@ -76,6 +92,7 @@ export default { address, url: null, registered, + protocolNetwork, location: null, __typename: 'IndexerRegistration', } @@ -84,9 +101,9 @@ export default { indexerDeployments: async ( _: {}, - { indexingStatusResolver }: IndexerManagementResolverContext, + { graphNode }: IndexerManagementResolverContext, ): Promise => { - const result = await indexingStatusResolver.indexingStatus([]) + const result = await graphNode.indexingStatus([]) return result.map((status) => ({ ...status, subgraphDeployment: status.subgraphDeployment.ipfsHash, @@ -94,11 +111,20 @@ export default { }, indexerAllocations: async ( - _: {}, - { address, networkSubgraph, logger }: IndexerManagementResolverContext, + { protocolNetwork }: { protocolNetwork: string }, + { multiNetworks, logger }: IndexerManagementResolverContext, ): Promise => { + if (!multiNetworks) { + throw Error( + 'IndexerManagementClient must be in `network` mode to fetch indexer allocations', + ) + } + + const network = extractNetwork(protocolNetwork, multiNetworks) + const address = network.specification.indexerOptions.address + try { - const result = await networkSubgraph.query( + const result = await network.networkSubgraph.checkedQuery( gql` query allocations($indexer: String!) { allocations( @@ -130,6 +156,7 @@ export default { .ipfsHash, signalledTokens: allocation.subgraphDeployment.signalledTokens, stakedTokens: allocation.subgraphDeployment.stakedTokens, + protocolNetwork: network.specification.networkIdentifier, })) } catch (error) { const err = indexerError(IndexerErrorCode.IE010, error) @@ -141,98 +168,150 @@ export default { }, indexerEndpoints: async ( - _: {}, - { address, contracts, logger }: IndexerManagementResolverContext, - ): Promise => { - const endpoints = { - service: { - url: null as string | null, - healthy: false, - tests: [] as TestResult[], - }, - status: { - url: null as string | null, - healthy: false, - tests: [] as TestResult[], - }, + { protocolNetwork: unvalidatedProtocolNetwork }: { protocolNetwork: string | null }, + { multiNetworks, logger }: IndexerManagementResolverContext, + ): Promise => { + if (!multiNetworks) { + throw Error( + 'IndexerManagementClient must be in `network` mode to fetch indexer endpoints', + ) } + const endpoints: Endpoints[] = [] + let networkIdentifier: string | null = null + // Validate protocol network try { - const service = await contracts.serviceRegistry.services(address) - - if (service) { - { - const { url, tests, ok } = await testURL(service.url, [ - URL_VALIDATION_TEST, - { - test: (url) => `http get ${url}`, - run: async (url) => { - const response = await fetch(url) - if (!response.ok) { - throw new Error( - `Returned status ${response.status}: ${ - response.body ? response.body.toString() : 'No data returned' - }`, - ) - } - }, - possibleActions: (url) => [ - `Make sure ${url} can be resolved and reached from this machine`, - `Make sure the port of ${url} is set up correctly`, - `Make sure the test command returns an HTTP status code < 400`, - ], - }, - ]) - - endpoints.service.url = url - endpoints.service.healthy = ok - endpoints.service.tests = tests - } - - { - const statusURL = endpoints.service.url.endsWith('/') - ? endpoints.service.url.substring(0, endpoints.service.url.length - 1) + - '/status' - : endpoints.service.url + '/status' - - const { url, tests, ok } = await testURL(statusURL, [ - URL_VALIDATION_TEST, - { - test: (url) => `http post ${url} query="{ indexingStatuses { subgraph } }"`, - run: async (url) => { - const response = await fetch(url, { - method: 'POST', - headers: { 'content-type': 'application/json' }, - body: JSON.stringify({ query: '{ indexingStatuses { subgraph } }' }), - }) - if (!response.ok) { - throw new Error( - `Returned status ${response.status}: ${ - response.body ? response.body.toString() : 'No data returned' - }`, - ) - } - }, - possibleActions: (url) => [ - `Make sure ${url} can be reached from this machine`, - `Make sure the port of ${url} is set up correctly`, - `Make sure ${url} is the /status endpoint of indexer-service`, - `Make sure the test command returns an HTTP status code < 400`, - `Make sure the test command returns a valid GraphQL response`, - ], - }, - ]) - - endpoints.status.url = url - endpoints.status.healthy = ok - endpoints.status.tests = tests - } + if (unvalidatedProtocolNetwork) { + networkIdentifier = validateNetworkIdentifier(unvalidatedProtocolNetwork) } - } catch (err) { - // Return empty endpoints - logger?.warn(`Failed to detect service endpoints`, { err }) + } catch (parseError) { + throw new Error( + `Invalid protocol network identifier: '${unvalidatedProtocolNetwork}'. Error: ${parseError}`, + ) } + await multiNetworks.map(async (network: Network) => { + // Skip if this query asks for another protocol network + if ( + networkIdentifier && + networkIdentifier !== network.specification.networkIdentifier + ) { + return + } + try { + const networkEndpoints = await endpointForNetwork(network) + endpoints.push(networkEndpoints) + } catch (err) { + // Ignore endpoints for this network + logger?.warn(`Failed to detect service endpoints for network`, { + err, + protocolNetwork: network.specification.networkIdentifier, + }) + } + }) return endpoints }, } + +interface Endpoint { + url: string | null + healthy: boolean + protocolNetwork: string + // eslint-disable-next-line @typescript-eslint/no-explicit-any + tests: any[] +} + +interface Endpoints { + service: Endpoint + status: Endpoint +} + +function defaultEndpoint(protocolNetwork: string): Endpoint { + return { + url: null as string | null, + healthy: false, + protocolNetwork, + tests: [] as TestResult[], + } +} +function defaultEndpoints(protocolNetwork: string): Endpoints { + return { + service: defaultEndpoint(protocolNetwork), + status: defaultEndpoint(protocolNetwork), + } +} + +async function endpointForNetwork(network: Network): Promise { + const contracts = network.contracts + const address = network.specification.indexerOptions.address + const endpoints = defaultEndpoints(network.specification.networkIdentifier) + const service = await contracts.serviceRegistry.services(address) + if (service) { + { + const { url, tests, ok } = await testURL(service.url, [ + URL_VALIDATION_TEST, + { + test: (url) => `http get ${url}`, + run: async (url) => { + const response = await fetch(url) + if (!response.ok) { + throw new Error( + `Returned status ${response.status}: ${ + response.body ? response.body.toString() : 'No data returned' + }`, + ) + } + }, + possibleActions: (url) => [ + `Make sure ${url} can be resolved and reached from this machine`, + `Make sure the port of ${url} is set up correctly`, + `Make sure the test command returns an HTTP status code < 400`, + ], + }, + ]) + + endpoints.service.url = url + endpoints.service.healthy = ok + endpoints.service.tests = tests + } + + { + const statusURL = endpoints.service.url.endsWith('/') + ? endpoints.service.url.substring(0, endpoints.service.url.length - 1) + '/status' + : endpoints.service.url + '/status' + + const { url, tests, ok } = await testURL(statusURL, [ + URL_VALIDATION_TEST, + { + test: (url) => `http post ${url} query="{ indexingStatuses { subgraph } }"`, + run: async (url) => { + const response = await fetch(url, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ query: '{ indexingStatuses { subgraph } }' }), + }) + if (!response.ok) { + throw new Error( + `Returned status ${response.status}: ${ + response.body ? response.body.toString() : 'No data returned' + }`, + ) + } + }, + possibleActions: (url) => [ + `Make sure ${url} can be reached from this machine`, + `Make sure the port of ${url} is set up correctly`, + `Make sure ${url} is the /status endpoint of indexer-service`, + `Make sure the test command returns an HTTP status code < 400`, + `Make sure the test command returns a valid GraphQL response`, + ], + }, + ]) + + endpoints.status.url = url + endpoints.status.healthy = ok + endpoints.status.tests = tests + } + } + return endpoints +} diff --git a/packages/indexer-common/src/indexer-management/resolvers/indexing-rules.ts b/packages/indexer-common/src/indexer-management/resolvers/indexing-rules.ts index b333c3804..b9a4c32d1 100644 --- a/packages/indexer-common/src/indexer-management/resolvers/indexing-rules.ts +++ b/packages/indexer-common/src/indexer-management/resolvers/indexing-rules.ts @@ -3,14 +3,18 @@ import { IndexerManagementModels, INDEXING_RULE_GLOBAL, + IndexingRuleIdentifier, IndexingRuleCreationAttributes, } from '../models' import { IndexerManagementDefaults, IndexerManagementResolverContext } from '../client' import { Transaction } from 'sequelize/types' -import { fetchIndexingRules, processIdentifier } from '@graphprotocol/indexer-common' +import { fetchIndexingRules } from '../rules' +import { processIdentifier } from '../../' +import { validateNetworkIdentifier } from '../../parsers' +import groupBy from 'lodash.groupby' const resetGlobalRule = async ( - identifier: string, + ruleIdentifier: IndexingRuleIdentifier, defaults: IndexerManagementDefaults['globalIndexingRule'], models: IndexerManagementModels, transaction: Transaction, @@ -18,8 +22,8 @@ const resetGlobalRule = async ( await models.IndexingRule.upsert( { ...defaults, + ...ruleIdentifier, allocationAmount: defaults.allocationAmount.toString(), - identifier, }, { transaction }, ) @@ -27,12 +31,24 @@ const resetGlobalRule = async ( export default { indexingRule: async ( - { identifier, merged }: { identifier: string; merged: boolean }, + { + identifier: indexingRuleIdentifier, + merged, + }: { identifier: IndexingRuleIdentifier; merged: boolean }, { models }: IndexerManagementResolverContext, ): Promise => { - ;[identifier] = await processIdentifier(identifier, { all: false, global: true }) + const [identifier] = await processIdentifier(indexingRuleIdentifier.identifier, { + all: false, + global: true, + }) + + // Sanitize protocol network identifier + const protocolNetwork = validateNetworkIdentifier( + indexingRuleIdentifier.protocolNetwork, + ) + const rule = await models.IndexingRule.findOne({ - where: { identifier }, + where: { identifier, protocolNetwork }, }) if (rule && merged) { return rule.mergeToGraphQL( @@ -46,10 +62,17 @@ export default { }, indexingRules: async ( - { merged }: { merged: boolean }, + { + merged, + protocolNetwork: uncheckedProtocolNetwork, + }: { merged: boolean; protocolNetwork: string | undefined }, { models }: IndexerManagementResolverContext, ): Promise => { - return await fetchIndexingRules(models, merged) + // Convert the input `protocolNetwork` value to a CAIP2-ID + const protocolNetwork = uncheckedProtocolNetwork + ? validateNetworkIdentifier(uncheckedProtocolNetwork) + : undefined + return await fetchIndexingRules(models, merged, protocolNetwork) }, setIndexingRule: async ( @@ -60,40 +83,57 @@ export default { throw Error('Cannot set indexingRule without identifier') } + if (!rule.protocolNetwork) { + throw Error("Cannot set an indexing rule without the field 'protocolNetwork'") + } else { + try { + rule.protocolNetwork = validateNetworkIdentifier(rule.protocolNetwork) + } catch (e) { + throw Error(`Invalid value for the field 'protocolNetwork'. ${e}`) + } + } + const [identifier] = await processIdentifier(rule.identifier, { all: false, global: true, }) rule.identifier = identifier - await models.IndexingRule.upsert(rule) - - // Since upsert succeeded, we _must_ have a rule - const updatedRule = await models.IndexingRule.findOne({ - where: { identifier: rule.identifier }, - }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return updatedRule!.toGraphQL() + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [updatedRule, _created] = await models.IndexingRule.upsert(rule) + return updatedRule.toGraphQL() }, deleteIndexingRule: async ( - { identifier }: { identifier: string }, + { identifier: indexingRuleIdentifier }: { identifier: IndexingRuleIdentifier }, { models, defaults }: IndexerManagementResolverContext, ): Promise => { - ;[identifier] = await processIdentifier(identifier, { all: false, global: true }) + const [identifier] = await processIdentifier(indexingRuleIdentifier.identifier, { + all: false, + global: true, + }) + + // Sanitize protocol network identifier + const protocolNetwork = validateNetworkIdentifier( + indexingRuleIdentifier.protocolNetwork, + ) + + const validatedRuleIdentifier = { + protocolNetwork, + identifier, + } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return await models.IndexingRule.sequelize!.transaction(async (transaction) => { const numDeleted = await models.IndexingRule.destroy({ - where: { - identifier, - }, + where: validatedRuleIdentifier, transaction, }) // Reset the global rule - if (identifier === 'global') { + if (validatedRuleIdentifier.identifier === 'global') { await resetGlobalRule( - identifier, + validatedRuleIdentifier, defaults.globalIndexingRule, models, transaction, @@ -105,32 +145,53 @@ export default { }, deleteIndexingRules: async ( - { identifiers }: { identifiers: string[] }, + { identifiers: indexingRuleIdentifiers }: { identifiers: IndexingRuleIdentifier[] }, { models, defaults }: IndexerManagementResolverContext, ): Promise => { - identifiers = await Promise.all( - identifiers.map( - async (identifier) => - ( - await processIdentifier(identifier, { all: false, global: true }) - )[0], - ), - ) + let totalNumDeleted = 0 - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return await models.IndexingRule.sequelize!.transaction(async (transaction) => { - const numDeleted = await models.IndexingRule.destroy({ - where: { - identifier: identifiers, - }, - transaction, - }) + // Sanitize protocol network identifiers + for (const identifier of indexingRuleIdentifiers) { + identifier.protocolNetwork = validateNetworkIdentifier(identifier.protocolNetwork) + } - if (identifiers.includes('global')) { - await resetGlobalRule('global', defaults.globalIndexingRule, models, transaction) - } + // Batch deletions by the `IndexingRuleIdentifier.protocolNetwork` attribute . + const batches = groupBy( + indexingRuleIdentifiers, + (x: IndexingRuleIdentifier) => x.protocolNetwork, + ) - return numDeleted > 0 - }) + for (const protocolNetwork in batches) { + const batch = batches[protocolNetwork] + const identifiers = await Promise.all( + batch.map( + async ({ identifier }: IndexingRuleIdentifier) => + (await processIdentifier(identifier, { all: false, global: true }))[0], + ), + ) + // Execute deletion batch + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await models.IndexingRule.sequelize!.transaction(async (transaction) => { + const numDeleted = await models.IndexingRule.destroy({ + where: { + identifier: identifiers, + protocolNetwork: protocolNetwork, + }, + transaction, + }) + + if (identifiers.includes('global')) { + const globalIdentifier = { identifier: 'global', protocolNetwork } + await resetGlobalRule( + globalIdentifier, + defaults.globalIndexingRule, + models, + transaction, + ) + } + totalNumDeleted += numDeleted + }) + } + return totalNumDeleted > 0 }, } diff --git a/packages/indexer-common/src/indexer-management/resolvers/poi-disputes.ts b/packages/indexer-common/src/indexer-management/resolvers/poi-disputes.ts index 0556858a3..faf200fcd 100644 --- a/packages/indexer-common/src/indexer-management/resolvers/poi-disputes.ts +++ b/packages/indexer-common/src/indexer-management/resolvers/poi-disputes.ts @@ -1,35 +1,47 @@ /* eslint-disable @typescript-eslint/ban-types */ -import { POIDispute, POIDisputeCreationAttributes } from '../models' +import { POIDispute, POIDisputeIdentifier, POIDisputeCreationAttributes } from '../models' import { IndexerManagementResolverContext } from '../client' -import { Op } from 'sequelize' +import { validateNetworkIdentifier } from '../../parsers' +import { Op, WhereOptions } from 'sequelize' +import groupBy from 'lodash.groupby' export default { dispute: async ( - { allocationID }: { allocationID: number }, + { identifier }: { identifier: POIDisputeIdentifier }, { models }: IndexerManagementResolverContext, ): Promise => { const dispute = await models.POIDispute.findOne({ - where: { allocationID }, + where: { ...identifier }, }) return dispute?.toGraphQL() || dispute }, disputes: async ( - { status, minClosedEpoch }: { status: string; minClosedEpoch: number }, + { + status, + minClosedEpoch, + protocolNetwork: uncheckedProtocolNetwork, + }: { status: string; minClosedEpoch: number; protocolNetwork: string | undefined }, { models }: IndexerManagementResolverContext, ): Promise => { + // Sanitize protocol network identifier + const protocolNetwork = uncheckedProtocolNetwork + ? validateNetworkIdentifier(uncheckedProtocolNetwork) + : undefined + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const sqlAndExpression: WhereOptions = [ + { status }, + { closedEpoch: { [Op.gte]: minClosedEpoch } }, + ] + + if (protocolNetwork) { + sqlAndExpression.push({ protocolNetwork }) + } + const disputes = await models.POIDispute.findAll({ - where: { - [Op.and]: [ - { status }, - { - closedEpoch: { - [Op.gte]: minClosedEpoch, - }, - }, - ], - }, + where: { [Op.and]: sqlAndExpression }, order: [['allocationAmount', 'DESC']], }) return disputes.map((dispute) => dispute.toGraphQL()) @@ -39,6 +51,14 @@ export default { { disputes }: { disputes: POIDisputeCreationAttributes[] }, { models }: IndexerManagementResolverContext, ): Promise => { + // Sanitize protocol network identifiers + for (const dispute of disputes) { + if (!dispute.protocolNetwork) { + throw new Error(`Dispute is missing the attribute 'protocolNetwork'`) + } + dispute.protocolNetwork = validateNetworkIdentifier(dispute.protocolNetwork) + } + const createdDisputes = await models.POIDispute.bulkCreate(disputes, { returning: true, validate: true, @@ -47,20 +67,38 @@ export default { 'previousEpochReferenceProof', 'status', ], + conflictAttributes: ['allocationID', 'protocolNetwork'], }) return createdDisputes.map((dispute: POIDispute) => dispute.toGraphQL()) }, deleteDisputes: async ( - { allocationIDs }: { allocationIDs: string[] }, + { identifiers }: { identifiers: POIDisputeIdentifier[] }, { models }: IndexerManagementResolverContext, ): Promise => { - const numDeleted = await models.POIDispute.destroy({ - where: { - allocationID: allocationIDs, - }, - force: true, - }) - return numDeleted + let totalNumDeleted = 0 + + // Sanitize protocol network identifiers + for (const identifier of identifiers) { + if (!identifier.protocolNetwork) { + throw new Error(`Dispute is missing the attribute 'protocolNetwork'`) + } + identifier.protocolNetwork = validateNetworkIdentifier(identifier.protocolNetwork) + } + + // Batch by protocolNetwork + const batches = groupBy(identifiers, (x: POIDisputeIdentifier) => x.protocolNetwork) + + for (const protocolNetwork in batches) { + const batch = batches[protocolNetwork] + const numDeleted = await models.POIDispute.destroy({ + where: { + allocationID: batch.map((x) => x.allocationID), + }, + force: true, + }) + totalNumDeleted += numDeleted + } + return totalNumDeleted }, } diff --git a/packages/indexer-common/src/indexer-management/resolvers/utils.ts b/packages/indexer-common/src/indexer-management/resolvers/utils.ts new file mode 100644 index 000000000..baa061fdf --- /dev/null +++ b/packages/indexer-common/src/indexer-management/resolvers/utils.ts @@ -0,0 +1,26 @@ +import { + MultiNetworks, + Network, + validateNetworkIdentifier, +} from '@graphprotocol/indexer-common' + +export function extractNetwork( + unvalidatedNetworkIdentifier: string, + multiNetworks: MultiNetworks, +): Network { + let networkIdentifier: string + try { + networkIdentifier = validateNetworkIdentifier(unvalidatedNetworkIdentifier) + } catch (parseError) { + throw new Error( + `Invalid protocol network identifier: '${unvalidatedNetworkIdentifier}'. Error: ${parseError}`, + ) + } + const network = multiNetworks.inner[networkIdentifier] + if (!network) { + throw new Error( + `Could not find a configured protocol network named ${networkIdentifier}`, + ) + } + return network +} diff --git a/packages/indexer-common/src/indexer-management/rules.ts b/packages/indexer-common/src/indexer-management/rules.ts index 4020b1567..656dd532b 100644 --- a/packages/indexer-common/src/indexer-management/rules.ts +++ b/packages/indexer-common/src/indexer-management/rules.ts @@ -6,22 +6,33 @@ import { IndexingRuleAttributes, } from '@graphprotocol/indexer-common' import { parseIndexingRule } from '../rules' +import groupBy from 'lodash.groupby' export const fetchIndexingRules = async ( models: IndexerManagementModels, merged: boolean, + protocolNetwork?: string, ): Promise => { + // If unspecified, select indexing rules from all protocol networks + const whereClause = protocolNetwork ? { protocolNetwork } : {} const rules = await models.IndexingRule.findAll({ + where: whereClause, order: [ ['identifierType', 'DESC'], ['identifier', 'ASC'], ], }) if (merged) { - const global = await models.IndexingRule.findOne({ - where: { identifier: INDEXING_RULE_GLOBAL }, - }) - return rules.map((rule) => rule.mergeGlobal(global)) + // Merge rules by protocol network + return Object.entries(groupBy(rules, (rule) => rule.protocolNetwork)) + .map(([protocolNetwork, rules]) => { + const global = rules.find((rule) => rule.identifier === INDEXING_RULE_GLOBAL) + if (!global) { + throw Error(`Could not find global rule for network '${protocolNetwork}'`) + } + return rules.map((rule) => rule.mergeGlobal(global)) + }) + .flat() } else { return rules } @@ -33,12 +44,8 @@ export const upsertIndexingRule = async ( newRule: Partial, ): Promise => { const indexingRule = parseIndexingRule(newRule) - await models.IndexingRule.upsert(indexingRule) - - // Since upsert succeeded, we _must_ have a rule - const updatedRule = await models.IndexingRule.findOne({ - where: { identifier: indexingRule.identifier }, - }) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [updatedRule, _created] = await models.IndexingRule.upsert(indexingRule) logger.debug( `DecisionBasis.${indexingRule.decisionBasis} rule merged into indexing rules`, diff --git a/packages/indexer-common/src/indexer-management/types.ts b/packages/indexer-common/src/indexer-management/types.ts index 873ed81c1..a6a0e2ef1 100644 --- a/packages/indexer-common/src/indexer-management/types.ts +++ b/packages/indexer-common/src/indexer-management/types.ts @@ -1,6 +1,12 @@ -import { Address, SubgraphDeploymentID, toAddress } from '@graphprotocol/common-ts' +import { + Address, + Logger, + SubgraphDeploymentID, + toAddress, +} from '@graphprotocol/common-ts' import { BigNumber } from 'ethers' import { Allocation } from '../allocations' +import { GraphNode } from '../graph-node' import { SubgraphDeployment } from '../types' export interface CreateAllocationResult { @@ -10,6 +16,7 @@ export interface CreateAllocationResult { allocation: string deployment: string allocatedTokens: string + protocolNetwork: string } export interface CloseAllocationResult { @@ -20,6 +27,7 @@ export interface CloseAllocationResult { allocatedTokens: string indexingRewards: string receiptsWorthCollecting: boolean + protocolNetwork: string } export interface ReallocateAllocationResult { @@ -31,12 +39,14 @@ export interface ReallocateAllocationResult { receiptsWorthCollecting: boolean createdAllocation: string createdAllocationStake: string + protocolNetwork: string } export interface ActionFailure { actionID: number transactionID?: string failureReason: string + protocolNetwork: string } /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -52,19 +62,21 @@ export type AllocationResult = /* eslint-disable @typescript-eslint/no-explicit-any */ export const parseGraphQLSubgraphDeployment = ( subgraphDeployment: any, + protocolNetwork: string, ): SubgraphDeployment => ({ id: new SubgraphDeploymentID(subgraphDeployment.id), deniedAt: subgraphDeployment.deniedAt, stakedTokens: BigNumber.from(subgraphDeployment.stakedTokens), signalledTokens: BigNumber.from(subgraphDeployment.signalledTokens), queryFeesAmount: BigNumber.from(subgraphDeployment.queryFeesAmount), - activeAllocations: subgraphDeployment.indexerAllocations.length, - name: subgraphDeployment.versions[0].subgraph.displayName, - creatorAddress: toAddress(subgraphDeployment.versions[0].subgraph.creatorAddress), + protocolNetwork, }) /* eslint-disable @typescript-eslint/no-explicit-any */ -export const parseGraphQLAllocation = (allocation: any): Allocation => ({ +export const parseGraphQLAllocation = ( + allocation: any, + protocolNetwork: string, +): Allocation => ({ // Ensure the allocation ID (an address) is checksummed id: toAddress(allocation.id), status: allocation.status, @@ -74,9 +86,7 @@ export const parseGraphQLAllocation = (allocation: any): Allocation => ({ stakedTokens: BigNumber.from(allocation.subgraphDeployment.stakedTokens), signalledTokens: BigNumber.from(allocation.subgraphDeployment.signalledTokens), queryFeesAmount: BigNumber.from(allocation.subgraphDeployment.queryFeesAmount), - activeAllocations: allocation.subgraphDeployment.indexerAllocations - ? allocation.subgraphDeployment.indexerAllocations.length - : 0, + protocolNetwork, }, indexer: toAddress(allocation.indexer.id), allocatedTokens: BigNumber.from(allocation.allocatedTokens), @@ -156,7 +166,7 @@ export function epochElapsedBlocks(networkEpoch: NetworkEpoch): number { return networkEpoch.startBlockNumber - networkEpoch.latestBlock } -const Caip2ByChainAlias: { [key: string]: string } = { +export const Caip2ByChainAlias: { [key: string]: string } = { mainnet: 'eip155:1', goerli: 'eip155:5', gnosis: 'eip155:100', @@ -164,12 +174,13 @@ const Caip2ByChainAlias: { [key: string]: string } = { 'arbitrum-one': 'eip155:42161', 'arbitrum-goerli': 'eip155:421613', avalanche: 'eip155:43114', - polygon: 'eip155:137', + matic: 'eip155:137', celo: 'eip155:42220', optimism: 'eip155:10', + fantom: 'eip155:250', } -const Caip2ByChainId: { [key: number]: string } = { +export const Caip2ByChainId: { [key: number]: string } = { 1: 'eip155:1', 5: 'eip155:5', 100: 'eip155:100', @@ -180,6 +191,7 @@ const Caip2ByChainId: { [key: number]: string } = { 137: 'eip155:137', 42220: 'eip155:42220', 10: 'eip155:10', + 250: 'eip155:250', } /// Unified entrypoint to resolve CAIP ID based either on chain aliases (strings) @@ -191,9 +203,14 @@ export function resolveChainId(key: number | string): string { if (chainId !== undefined) { return chainId } - } else { - // If chain is a string, it must be a chain alias - const chainId = Caip2ByChainAlias[key] + } else if (typeof key === 'string') { + const splitKey = key.split(':') + let chainId + if (splitKey.length === 2) { + chainId = Caip2ByChainId[+splitKey[1]] + } else { + chainId = Caip2ByChainAlias[key] + } if (chainId !== undefined) { return chainId } @@ -217,3 +234,51 @@ export function resolveChainAlias(id: string): string { ) } } + +// Compares the CAIP-2 chain ID between the Ethereum provider and the Network Subgraph and requires +// they are equal. +export async function validateProviderNetworkIdentifier( + providerNetworkIdentifier: string, + networkSubgraphDeploymentIpfsHash: string, + graphNode: GraphNode, + logger: Logger, +) { + const subgraphNetworkId = new SubgraphDeploymentID(networkSubgraphDeploymentIpfsHash) + const { network: subgraphNetworkChainName } = + await graphNode.subgraphFeatures(subgraphNetworkId) + + if (!subgraphNetworkChainName) { + // This is unlikely to happen because we expect that the Network Subgraph manifest is valid. + const errorMsg = 'Failed to fetch the networkId for the Network Subgraph' + logger.error(errorMsg, { networkSubgraphDeploymentIpfsHash }) + throw new Error(errorMsg) + } + + const providerChainId = resolveChainId(providerNetworkIdentifier) + const networkSubgraphChainId = resolveChainId(subgraphNetworkChainName) + if (providerChainId !== networkSubgraphChainId) { + const errorMsg = + 'The configured provider and the Network Subgraph have different CAIP-2 chain IDs. ' + + 'Please ensure that both Network Subgraph and the Ethereum provider are correctly configured.' + logger.error(errorMsg, { + networkSubgraphDeploymentIpfsHash, + networkSubgraphChainId, + providerChainId, + }) + throw new Error(errorMsg) + } +} + +// Convenience function to check if a given network identifier is a supported Layer-1 protocol network +export function networkIsL1(networkIdentifier: string): boolean { + // Normalize network identifier + networkIdentifier = resolveChainId(networkIdentifier) + return networkIdentifier === 'eip155:1' || networkIdentifier === 'eip155:5' +} + +// Convenience function to check if a given network identifier is a supported Layer-2 protocol network +export function networkIsL2(networkIdentifier: string): boolean { + // Normalize network identifier + networkIdentifier = resolveChainId(networkIdentifier) + return networkIdentifier === 'eip155:42161' || networkIdentifier === 'eip155:421613' +} diff --git a/packages/indexer-common/src/indexing-status.ts b/packages/indexer-common/src/indexing-status.ts deleted file mode 100644 index 6c80774f9..000000000 --- a/packages/indexer-common/src/indexing-status.ts +++ /dev/null @@ -1,357 +0,0 @@ -import fetch from 'isomorphic-fetch' -import gql from 'graphql-tag' -import { Client, createClient } from '@urql/core' -import { Logger, SubgraphDeploymentID } from '@graphprotocol/common-ts' -import { BlockPointer, ChainIndexingStatus, IndexingStatus } from './types' -import { indexerError, IndexerErrorCode, INDEXER_ERROR_MESSAGES } from './errors' -import pRetry from 'p-retry' - -export interface IndexingStatusFetcherOptions { - logger: Logger - statusEndpoint: string -} - -export interface SubgraphDeploymentAssignment { - id: SubgraphDeploymentID - node: string -} - -export interface SubgraphFeatures { - // `null` is only expected when Graph Node detects validation errors in the Subgraph Manifest. - network: string | null -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const parseGraphQLIndexingStatus = (indexingStatus: any): IndexingStatus => ({ - subgraphDeployment: new SubgraphDeploymentID(indexingStatus.subgraphDeployment), - synced: indexingStatus.synced, - health: indexingStatus.health, - fatalError: indexingStatus.fatalError, - node: indexingStatus.node, - chains: indexingStatus.chains.map(parseGraphQLChain), -}) - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const parseGraphQLChain = (chain: any): ChainIndexingStatus => ({ - network: chain.network, - latestBlock: parseGraphQLBlockPointer(chain.latestBlock), - chainHeadBlock: parseGraphQLBlockPointer(chain.chainHeadBlock), - earliestBlock: parseGraphQLBlockPointer(chain.earliestBlock), -}) - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const parseGraphQLBlockPointer = (block: any): BlockPointer | null => - block - ? { - number: +block.number, - hash: block.hash, - } - : null - -export class IndexingStatusResolver { - logger: Logger - statuses: Client - - constructor(options: IndexingStatusFetcherOptions) { - this.logger = options.logger.child({ component: 'IndexingStatusFetcher' }) - this.statuses = createClient({ - url: options.statusEndpoint, - fetch, - requestPolicy: 'network-only', - }) - } - - public async indexingStatus( - deployments: SubgraphDeploymentID[], - ): Promise { - const indexingStatusesQueryBody = ` - subgraphDeployment: subgraph - synced - health - fatalError { - handler - message - } - node - chains { - network - ... on EthereumIndexingStatus { - latestBlock { - number - hash - } - chainHeadBlock { - number - hash - } - earliestBlock { - number - hash - } - } - }` - const query = - deployments.length > 0 - ? `query indexingStatuses($deployments: [String!]!) { - indexingStatuses(subgraphs: $deployments) { - ${indexingStatusesQueryBody} - } - }` - : `query indexingStatuses { - indexingStatuses { - ${indexingStatusesQueryBody} - } - }` - - const queryIndexingStatuses = async () => { - const result = await this.statuses - .query(query, { deployments: deployments.map((id) => id.ipfsHash) }) - .toPromise() - - return ( - result.data.indexingStatuses - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .map((status: any) => ({ - ...status, - subgraphDeployment: new SubgraphDeploymentID(status.subgraphDeployment), - })) - ) - } - - try { - return await pRetry(queryIndexingStatuses, { - retries: 5, - maxTimeout: 10000, - onFailedAttempt: (err) => { - this.logger.warn(`Indexing statuses could not be queried`, { - attempt: err.attemptNumber, - retriesLeft: err.retriesLeft, - deployments, - err: err.message, - }) - }, - } as pRetry.Options) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE018, error) - this.logger.error(`Failed to query indexing status API`, { - deployments, - err, - }) - throw err - } - } - - public async proofOfIndexing( - deployment: SubgraphDeploymentID, - block: BlockPointer, - indexerAddress: string, - ): Promise { - try { - return await pRetry( - async (attempt) => { - const result = await this.statuses - .query( - gql` - query proofOfIndexing( - $subgraph: String! - $blockNumber: Int! - $blockHash: String! - $indexer: String! - ) { - proofOfIndexing( - subgraph: $subgraph - blockNumber: $blockNumber - blockHash: $blockHash - indexer: $indexer - ) - } - `, - { - subgraph: deployment.ipfsHash, - blockNumber: +block.number, - blockHash: block.hash, - indexer: indexerAddress, - }, - ) - .toPromise() - - if (result.error) { - if ( - result.error.message && - result.error.message.includes('DeploymentNotFound') - ) { - return undefined - } - throw result.error - } - this.logger.trace('Reference POI generated', { - indexer: indexerAddress, - subgraph: deployment.ipfsHash, - block: block, - proof: result.data.proofOfIndexing, - attempt, - }) - - return result.data.proofOfIndexing - }, - { - retries: 5, - maxTimeout: 10000, - onFailedAttempt: (err) => { - this.logger.warn(`Proof of indexing could not be queried`, { - attempt: err.attemptNumber, - retriesLeft: err.retriesLeft, - err: err.message, - }) - }, - } as pRetry.Options, - ) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE019, error) - this.logger.error(`Failed to query proof of indexing`, { - subgraph: deployment.ipfsHash, - blockHash: block, - indexer: indexerAddress, - err: err, - }) - return undefined - } - } - - public async blockHashFromNumber( - networkAlias: string, - blockNumber: number, - ): Promise { - this.logger.trace(`Querying blockHashFromNumber`, { networkAlias, blockNumber }) - try { - return await pRetry( - async (attempt) => { - const result = await this.statuses - .query( - gql` - query blockHashFromNumber($network: String!, $blockNumber: Int!) { - blockHashFromNumber(network: $network, blockNumber: $blockNumber) - } - `, - { - network: networkAlias, - blockNumber, - }, - ) - .toPromise() - - if (!result.data || !result.data.blockHashFromNumber || result.error) { - throw new Error( - `Failed to query graph node for blockHashFromNumber: ${ - result.error ?? 'no data returned' - }`, - ) - } - - this.logger.trace('Resolved block hash', { - networkAlias, - blockNumber, - blockHash: result.data.blockHashFromNumber, - attempt, - }) - - return `0x${result.data.blockHashFromNumber}` - }, - { - retries: 5, - maxTimeout: 10000, - onFailedAttempt: (err) => { - this.logger.warn(`Block hash could not be queried`, { - networkAlias, - blockNumber, - attempt: err.attemptNumber, - retriesLeft: err.retriesLeft, - err: err.message, - }) - }, - } as pRetry.Options, - ) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE070, error) - this.logger.error(`Failed to query block hash`, { - networkAlias, - blockNumber, - error: error.message, - }) - throw err - } - } - - public async subgraphDeployments(): Promise { - return (await this.subgraphDeploymentsAssignments()).map((details) => details.id) - } - - public async subgraphDeploymentsAssignments(): Promise { - try { - const result = await this.statuses - .query( - gql` - { - indexingStatuses { - subgraphDeployment: subgraph - node - } - } - `, - ) - .toPromise() - - if (result.error) { - throw result.error - } - - type QueryResult = { subgraphDeployment: string; node: string } - - return result.data.indexingStatuses - .filter((status: QueryResult) => status.node && status.node !== 'removed') - .map((status: QueryResult) => { - return { - id: new SubgraphDeploymentID(status.subgraphDeployment), - node: status.node, - } - }) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE018, error) - this.logger.error(`Failed to query indexing status API`, { err }) - throw err - } - } - - public async subgraphFeatures( - subgraphDeploymentId: SubgraphDeploymentID, - ): Promise { - const subgraphId = subgraphDeploymentId.ipfsHash - try { - const result = await this.statuses - .query( - gql` - query subgraphFeatures($subgraphId: String!) { - subgraphFeatures(subgraphId: $subgraphId) { - network - } - } - `, - { subgraphId }, - ) - .toPromise() - - if (result.error) { - throw result.error - } - if (!result.data) { - throw new Error('Subgraph Deployment Not Found') - } - return result.data.subgraphFeatures as SubgraphFeatures - } catch (error) { - const errorCode = IndexerErrorCode.IE073 - const err = indexerError(errorCode, error) - this.logger.error(INDEXER_ERROR_MESSAGES[errorCode], { err, subgraphId }) - throw err - } - } -} diff --git a/packages/indexer-common/src/multi-networks.ts b/packages/indexer-common/src/multi-networks.ts new file mode 100644 index 000000000..f305d2087 --- /dev/null +++ b/packages/indexer-common/src/multi-networks.ts @@ -0,0 +1,111 @@ +import pReduce from 'p-reduce' +import isEqual from 'lodash.isequal' +import xor from 'lodash.xor' + +// A mapping of different values of type T keyed by their network identifiers +export type NetworkMapped = Record + +// Function to extract the network identifier from a value of type T +type NetworkIdentity = (element: T) => string + +// Wrapper type for performing calls over multiple values of any type, most notably +// Network and Operator instances. +// All public-facing methods should return a `NetworkMapped` or `void`. +export class MultiNetworks { + inner: NetworkMapped + constructor(elements: T[], networkIdentity: NetworkIdentity) { + if (elements.length === 0) { + throw new Error('MultiNetworks component was initialized with empty values') + } + + function reducer(accumulator: NetworkMapped, current: T): NetworkMapped { + const key = networkIdentity(current) + if (key in accumulator) { + throw new Error( + `Duplicate network identifier found while mapping value's network: ${key}`, + ) + } + // TODO: parse and validate network identifiers to standardize them + accumulator[key] = current + return accumulator + } + this.inner = elements.reduce(reducer, {}) + } + + private checkEqualKeys(a: NetworkMapped, b: NetworkMapped) { + const aKeys = Object.keys(a) + const bKeys = Object.keys(b) + if (!isEqual(aKeys, bKeys)) { + const differentKeys = xor(aKeys, bKeys) + throw new Error(`Network Mapped objects have different keys: ${differentKeys}`) + } + } + + async map(func: (value: T) => Promise): Promise> { + const entries: [string, T][] = Object.entries(this.inner) + return pReduce( + entries, + async (acc, pair) => { + const [networkIdentifier, element]: [string, T] = pair + const result = await func(element) + acc[networkIdentifier] = result + return acc + }, + {} as NetworkMapped, + ) + } + + zip(a: NetworkMapped, b: NetworkMapped): NetworkMapped<[U, V]> { + this.checkEqualKeys(a, b) + const result = {} as NetworkMapped<[U, V]> + for (const key in a) { + result[key] = [a[key], b[key]] + } + return result + } + + zip3( + a: NetworkMapped, + b: NetworkMapped, + c: NetworkMapped, + ): NetworkMapped<[U, V, W]> { + this.checkEqualKeys(a, b) + const result = {} as NetworkMapped<[U, V, W]> + for (const key in a) { + result[key] = [a[key], b[key], c[key]] + } + return result + } + + zip4( + a: NetworkMapped, + b: NetworkMapped, + c: NetworkMapped, + d: NetworkMapped, + ): NetworkMapped<[U, V, W, Y]> { + this.checkEqualKeys(a, b) + const result = {} as NetworkMapped<[U, V, W, Y]> + for (const key in a) { + result[key] = [a[key], b[key], c[key], d[key]] + } + return result + } + + async mapNetworkMapped( + nmap: NetworkMapped, + func: (inner: T, value: U) => Promise, + ): Promise> { + return pReduce( + Object.entries(nmap), + async (acc, [networkIdentifier, value]: [string, U]) => { + const inner = this.inner[networkIdentifier] + if (!inner) { + throw new Error(`Network identifier not found: ${networkIdentifier}`) + } + acc[networkIdentifier] = await func(inner, value) + return acc + }, + {} as NetworkMapped, + ) + } +} diff --git a/packages/indexer-common/src/network-specification.ts b/packages/indexer-common/src/network-specification.ts new file mode 100644 index 000000000..620c7a39a --- /dev/null +++ b/packages/indexer-common/src/network-specification.ts @@ -0,0 +1,174 @@ +import { toAddress, parseGRT } from '@graphprotocol/common-ts' +import { BigNumber } from 'ethers' +import { validateNetworkIdentifier, validateIpfsHash } from './parsers' +import { AllocationManagementMode } from './types' +import { z } from 'zod' +import { utils } from 'ethers' + +// TODO: make sure those values are always in sync with the AllocationManagementMode enum. Can we do this in compile time? +const ALLOCATION_MANAGEMENT_MODE = ['auto', 'manual', 'oversight'] as const + +function positiveNumber(): z.ZodNumber { + return z.number().positive().finite() +} + +function GRT(): z.ZodEffects { + return z + .number() + .nonnegative() + .finite() + .transform((x) => parseGRT(x.toString())) +} + +// Gateway endpoints +export const Gateway = z + .object({ + url: z.string().url(), + }) + .strict() +export type Gateway = z.infer + +// Indexer identification and network behavior options +export const IndexerOptions = z + .object({ + address: z + .string() + .refine((val) => utils.isAddress(val), { + message: 'Invalid contract address', + }) + .transform(toAddress), + mnemonic: z.string(), + url: z.string().url(), + geoCoordinates: z.number().array().length(2).default([31.780715, -41.179504]), + restakeRewards: z.boolean().default(true), + rebateClaimThreshold: GRT().default(200), + rebateClaimBatchThreshold: GRT().default(200), + rebateClaimMaxBatchSize: positiveNumber().default(100), + poiDisputeMonitoring: z.boolean().default(false), + poiDisputableEpochs: positiveNumber().default(1), + defaultAllocationAmount: GRT().default(0.1), + voucherRedemptionThreshold: GRT().default(200), + voucherRedemptionBatchThreshold: GRT().default(2000), + voucherRedemptionMaxBatchSize: positiveNumber().default(100), + allocationManagementMode: z + .enum(ALLOCATION_MANAGEMENT_MODE) + .default('auto') + .transform((x) => x as AllocationManagementMode), + autoAllocationMinBatchSize: positiveNumber().default(1), + allocateOnNetworkSubgraph: z.boolean().default(false), + register: z.boolean().default(true), + }) + .strict() +export type IndexerOptions = z.infer + +// Transaction handling options +export const TransactionMonitoring = z + .object({ + gasIncreaseTimeout: positiveNumber() + .default(240) + .transform((x) => x * 10 ** 3), + gasIncreaseFactor: positiveNumber().default(1.2), + gasPriceMax: positiveNumber() + .default(100) + .transform((x) => x * 10 ** 9), + baseFeePerGasMax: positiveNumber() + .default(100) + .transform((x) => x * 10 ** 9) + .optional(), + maxTransactionAttempts: z.number().nonnegative().finite().default(0), + }) + .strict() + .default({}) // defaults will be used for instantiation when the TransactionMonitoring group is absent. + +export type TransactionMonitoring = z.infer + +// Generic subgraph specification +export const Subgraph = z + .object({ + url: z.string().url().optional(), + deployment: z.string().transform(transformIpfsHash).optional(), + }) + .strict() + .refine((obj) => !(!obj.url && !obj.deployment), { + message: 'At least one of `url` or `deployment` must be set', + }) +export type Subgraph = z.infer + +// All pertinent subgraphs in the protocol +export const ProtocolSubgraphs = z + .object({ + maxBlockDistance: z.number().nonnegative().finite().default(0), + freshnessSleepMilliseconds: positiveNumber().default(5_000), + networkSubgraph: Subgraph, + epochSubgraph: Subgraph, + }) + .strict() + // TODO: Ensure the `url` property is always defined until Epoch Subgraph + // indexing is supported. + .refine((subgraphs) => subgraphs.epochSubgraph.url, { + message: 'Epoch Subgraph endpoint must be defined', + path: ['epochSubgraph', 'url'], + }) +export type ProtocolSubgraphs = z.infer + +export const NetworkProvider = z + .object({ + url: z.string().url(), + pollingInterval: positiveNumber().default(4 * 10 ** 3), + }) + .strict() +export type NetworkProvider = z.infer + +export const Dai = z + .object({ + contractAddress: z + .string() + .default('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48') + .refine((val) => utils.isAddress(val), { + message: 'Invalid contract address', + }) + .transform(toAddress), + inject: z.boolean().default(true), + }) + .strict() + .default({}) // defaults will be used for instantiation when the Dai group is absent. +export type Dai = z.infer + +// All necessary information to describe a Protocol Network +export const NetworkSpecification = z + .object({ + networkIdentifier: z.string().transform(transformNetworkIdentifier), + gateway: Gateway, + indexerOptions: IndexerOptions, + transactionMonitoring: TransactionMonitoring, + subgraphs: ProtocolSubgraphs, + networkProvider: NetworkProvider, + dai: Dai, + }) + .strict() + +export type NetworkSpecification = z.infer + +function transformNetworkIdentifier(input: string, ctx: z.RefinementCtx): string { + try { + return validateNetworkIdentifier(input) + } catch (e) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Invalid network identifier', + }) + return z.NEVER + } +} + +function transformIpfsHash(input: string, ctx: z.RefinementCtx): string { + try { + return validateIpfsHash(input) + } catch (e) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Invalid IPFS hash', + }) + return z.NEVER + } +} diff --git a/packages/indexer-common/src/network-subgraph.ts b/packages/indexer-common/src/network-subgraph.ts index b3fe18997..589732272 100644 --- a/packages/indexer-common/src/network-subgraph.ts +++ b/packages/indexer-common/src/network-subgraph.ts @@ -3,16 +3,17 @@ import { Eventual, Logger, SubgraphDeploymentID, timer } from '@graphprotocol/co import { DocumentNode, print } from 'graphql' import { OperationResult, CombinedError } from '@urql/core' import { BlockPointer, IndexingError } from './types' -import { IndexingStatusResolver } from './indexing-status' +import { GraphNode } from './graph-node' +import { SubgraphFreshnessChecker } from './subgraphs' export interface NetworkSubgraphCreateOptions { logger: Logger endpoint?: string deployment?: { - indexingStatusResolver: IndexingStatusResolver - graphNodeQueryEndpoint: string + graphNode: GraphNode deployment: SubgraphDeploymentID } + subgraphFreshnessChecker: SubgraphFreshnessChecker } interface DeploymentStatus { @@ -29,8 +30,9 @@ interface NetworkSubgraphOptions { deployment?: { id: SubgraphDeploymentID status: Eventual - graphNodeQueryEndpoint: string + graphNode: GraphNode } + subgraphFreshnessChecker: SubgraphFreshnessChecker } export type QueryResult = Pick< @@ -40,17 +42,18 @@ export type QueryResult = Pick< export class NetworkSubgraph { logger: Logger - + freshnessChecker: SubgraphFreshnessChecker endpointClient?: AxiosInstance public readonly deployment?: { id: SubgraphDeploymentID - client: AxiosInstance status: Eventual + endpointClient: AxiosInstance } private constructor(options: NetworkSubgraphOptions) { this.logger = options.logger + this.freshnessChecker = options.subgraphFreshnessChecker if (options.endpoint) { this.endpointClient = axios.create({ @@ -66,26 +69,16 @@ export class NetworkSubgraph { } if (options.deployment) { - const client = axios.create({ - baseURL: new URL( - `/subgraphs/id/${options.deployment.id.ipfsHash}`, - options.deployment.graphNodeQueryEndpoint, - ).toString(), - - headers: { 'content-type': 'application/json' }, - - // Don't parse responses as JSON - responseType: 'text', - - // Don't transform responses - transformResponse: (data) => data, - }) const status = options.deployment.status + const graphNodeEndpointClient = options.deployment.graphNode.getQueryClient( + options.deployment.id.ipfsHash, + ) + this.deployment = { id: options.deployment.id, - client, status, + endpointClient: graphNodeEndpointClient, } } } @@ -94,6 +87,7 @@ export class NetworkSubgraph { logger: parentLogger, endpoint, deployment, + subgraphFreshnessChecker, }: NetworkSubgraphCreateOptions): Promise { // Either an endpoint or a deployment needs to be provided; the CLI // validation should already guarantee that but we're asserting this again @@ -110,21 +104,21 @@ export class NetworkSubgraph { | { id: SubgraphDeploymentID status: Eventual - graphNodeQueryEndpoint: string + graphNode: GraphNode } | undefined if (deployment) { const status = await monitorDeployment({ logger, - indexingStatusResolver: deployment.indexingStatusResolver, + graphNode: deployment.graphNode, deployment: deployment.deployment, }) deploymentInfo = { id: deployment.deployment, status, - graphNodeQueryEndpoint: deployment.graphNodeQueryEndpoint, + graphNode: deployment.graphNode, } } @@ -133,6 +127,7 @@ export class NetworkSubgraph { logger, endpoint, deployment: deploymentInfo, + subgraphFreshnessChecker, }) // If we don't have a network subgraph endpoint configured, we @@ -152,7 +147,7 @@ export class NetworkSubgraph { if (healthy) { this.logger.trace('Use own deployment for network subgraph query') - return this.deployment.client + return this.deployment.endpointClient } else if (this.endpointClient) { this.logger.trace('Use provided endpoint for network subgraph query') return this.endpointClient @@ -171,6 +166,15 @@ export class NetworkSubgraph { } } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async checkedQuery( + query: DocumentNode, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + variables?: Record, + ): Promise> { + return this.freshnessChecker.checkedQuery(this, query, variables) + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any async query( query: DocumentNode, @@ -194,11 +198,11 @@ export class NetworkSubgraph { const monitorDeployment = async ({ logger, - indexingStatusResolver, + graphNode, deployment, }: { logger: Logger - indexingStatusResolver: IndexingStatusResolver + graphNode: GraphNode deployment: SubgraphDeploymentID }): Promise> => { const initialStatus: DeploymentStatus = { @@ -213,7 +217,7 @@ const monitorDeployment = async ({ try { logger.trace(`Checking the network subgraph deployment status`) - const indexingStatuses = await indexingStatusResolver.indexingStatus([deployment]) + const indexingStatuses = await graphNode.indexingStatus([deployment]) const indexingStatus = indexingStatuses.pop() if (!indexingStatus) { throw `No indexing status found` diff --git a/packages/indexer-common/src/network.ts b/packages/indexer-common/src/network.ts index 45cb343e5..d8fd63d91 100644 --- a/packages/indexer-common/src/network.ts +++ b/packages/indexer-common/src/network.ts @@ -1,168 +1,254 @@ import { - Address, - Eventual, - formatGRT, Logger, Metrics, - mutable, NetworkContracts, SubgraphDeploymentID, - timer, - toAddress, + connectContracts, + Eventual, } from '@graphprotocol/common-ts' import { - Allocation, - Epoch, INDEXER_ERROR_MESSAGES, indexerError, IndexerErrorCode, NetworkSubgraph, - parseGraphQLAllocation, - parseGraphQLEpochs, TransactionManager, -} from '@graphprotocol/indexer-common' -import { BigNumber, providers, utils, Wallet } from 'ethers' + specification as spec, + GraphNode, + EpochSubgraph, + NetworkMonitor, + AllocationReceiptCollector, + SubgraphFreshnessChecker, +} from '.' +import { providers, Wallet } from 'ethers' import { strict as assert } from 'assert' -import gql from 'graphql-tag' import geohash from 'ngeohash' -import pFilter from 'p-filter' -import pRetry from 'p-retry' -interface IndexerConfig { - url: string - geoCoordinates: [string, string] - restakeRewards: boolean - rebateClaimThreshold: BigNumber - rebateClaimBatchThreshold: BigNumber - rebateClaimMaxBatchSize: number - poiDisputeMonitoring: boolean - poiDisputableEpochs: number -} +import pRetry from 'p-retry' +import { resolveChainId } from './indexer-management' +import { monitorEthBalance } from './utils' +import { QueryFeeModels } from './query-fees' export class Network { logger: Logger networkSubgraph: NetworkSubgraph contracts: NetworkContracts - indexerAddress: Address - ethereum: providers.StaticJsonRpcProvider + wallet: Wallet + networkProvider: providers.StaticJsonRpcProvider transactionManager: TransactionManager - indexerConfigs: IndexerConfig - indexerUrl: string - indexerGeoCoordinates: [string, string] + networkMonitor: NetworkMonitor + receiptCollector: AllocationReceiptCollector + specification: spec.NetworkSpecification + paused: Eventual + isOperator: Eventual private constructor( logger: Logger, - wallet: Wallet, - indexerAddress: Address, - indexerUrl: string, - geoCoordinates: [string, string], contracts: NetworkContracts, + wallet: Wallet, networkSubgraph: NetworkSubgraph, - ethereum: providers.StaticJsonRpcProvider, + networkProvider: providers.StaticJsonRpcProvider, + transactionManager: TransactionManager, + networkMonitor: NetworkMonitor, + receiptCollector: AllocationReceiptCollector, + specification: spec.NetworkSpecification, paused: Eventual, isOperator: Eventual, - restakeRewards: boolean, - rebateClaimThreshold: BigNumber, - rebateClaimBatchThreshold: BigNumber, - rebateClaimMaxBatchSize: number, - poiDisputeMonitoring: boolean, - poiDisputableEpochs: number, - gasIncreaseTimeout: number, - gasIncreaseFactor: number, - baseFeePerGasMax: number, - maxTransactionAttempts: number, ) { this.logger = logger - this.indexerAddress = indexerAddress - this.indexerUrl = indexerUrl - this.indexerGeoCoordinates = geoCoordinates this.contracts = contracts + this.wallet = wallet this.networkSubgraph = networkSubgraph - this.ethereum = ethereum - this.indexerConfigs = { - url: indexerUrl, - geoCoordinates: geoCoordinates, - restakeRewards: restakeRewards, - rebateClaimThreshold: rebateClaimThreshold, - rebateClaimBatchThreshold: rebateClaimBatchThreshold, - rebateClaimMaxBatchSize: rebateClaimMaxBatchSize, - poiDisputeMonitoring: poiDisputeMonitoring, - poiDisputableEpochs: poiDisputableEpochs, - } - - this.transactionManager = new TransactionManager( - ethereum, - wallet, - paused, - isOperator, - gasIncreaseTimeout, - gasIncreaseFactor, - baseFeePerGasMax, - maxTransactionAttempts, - ) + this.networkProvider = networkProvider + this.transactionManager = transactionManager + this.networkMonitor = networkMonitor + this.receiptCollector = receiptCollector + this.specification = specification + this.paused = paused + this.isOperator = isOperator } static async create( parentLogger: Logger, - ethereum: providers.StaticJsonRpcProvider, - contracts: NetworkContracts, - wallet: Wallet, - indexerAddress: Address, - indexerUrl: string, - geoCoordinates: [string, string], - networkSubgraph: NetworkSubgraph, - restakeRewards: boolean, - rebateClaimThreshold: BigNumber, - rebateClaimBatchThreshold: BigNumber, - rebateClaimMaxBatchSize: number, - poiDisputeMonitoring: boolean, - poiDisputableEpochs: number, - gasIncreaseTimeout: number, - gasIncreaseFactor: number, - baseFeePerGasMax: number, - maxTransactionAttempts: number, + specification: spec.NetworkSpecification, + queryFeeModels: QueryFeeModels, + graphNode: GraphNode, + metrics: Metrics, ): Promise { - const logger = parentLogger.child({ + // Incomplete logger for initial operations, will be replaced as new labels emerge. + let logger = parentLogger.child({ component: 'Network', - indexer: indexerAddress.toString(), + indexer: specification.indexerOptions.address, + protocolNetwork: specification.networkIdentifier, + }) + + // * ----------------------------------------------------------------------- + // * Network Provider + // * ----------------------------------------------------------------------- + const networkProvider = await Network.provider( + logger, + metrics, + specification.networkIdentifier, + specification.networkProvider.url, + specification.networkProvider.pollingInterval, + ) + + // * ----------------------------------------------------------------------- + // * Network Subgraph + // * ----------------------------------------------------------------------- + const networkSubgraphFreshnessChecker = new SubgraphFreshnessChecker( + 'Network Subgraph', + networkProvider, + specification.subgraphs.maxBlockDistance, + specification.subgraphs.freshnessSleepMilliseconds, + logger.child({ component: 'FreshnessChecker' }), + Infinity, + ) + + const networkSubgraphDeploymentId = specification.subgraphs.networkSubgraph.deployment + ? new SubgraphDeploymentID(specification.subgraphs.networkSubgraph.deployment) + : undefined + + const networkSubgraph = await NetworkSubgraph.create({ + logger, + endpoint: specification.subgraphs.networkSubgraph.url, + deployment: + networkSubgraphDeploymentId !== undefined + ? { + graphNode, + deployment: networkSubgraphDeploymentId, + } + : undefined, + subgraphFreshnessChecker: networkSubgraphFreshnessChecker, + }) + + // * ----------------------------------------------------------------------- + // * Contracts + // * ----------------------------------------------------------------------- + const wallet = await connectWallet( + networkProvider, + specification.networkIdentifier, + specification.indexerOptions.mnemonic, + logger, + ) + + // Include wallet address in this logger + logger = logger.child({ operator: wallet.address, }) - const paused = await monitorNetworkPauses(logger, contracts, networkSubgraph) - const isOperator = await monitorIsOperator(logger, contracts, indexerAddress, wallet) + // Monitor ETH balance of the operator and write the latest value to a metric + await monitorEthBalance(logger, wallet, metrics, specification.networkIdentifier) - return new Network( + const contracts = await connectToProtocolContracts( + wallet, + specification.networkIdentifier, + logger, + ) + + // * ----------------------------------------------------------------------- + // * Epoch Subgraph + // * ----------------------------------------------------------------------- + const epochSubgraphFreshnessChecker = new SubgraphFreshnessChecker( + 'Epoch Subgraph', + networkProvider, + specification.subgraphs.maxBlockDistance, + specification.subgraphs.freshnessSleepMilliseconds, + logger.child({ component: 'FreshnessChecker' }), + Infinity, + ) + + const epochSubgraph = new EpochSubgraph( + /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- + * Accept the non-null `url` property of the Epoch Subgraph, as it has + * already been validated during parsing. Once indexing is supported, + * initialize it in the same way as the NetworkSubgraph + */ + specification.subgraphs.epochSubgraph.url!, + epochSubgraphFreshnessChecker, + logger.child({ component: 'EpochSubgraph' }), + ) + + // * ----------------------------------------------------------------------- + // * Network Monitor + // * ----------------------------------------------------------------------- + const networkMonitor = new NetworkMonitor( + specification.networkIdentifier, + contracts, + specification.indexerOptions, + logger.child({ + component: 'NetworkMonitor', + protocolNetwork: specification.networkIdentifier, + }), + graphNode, + networkSubgraph, + networkProvider, + epochSubgraph, + ) + + // * ----------------------------------------------------------------------- + // * Transaction Manager + // * ----------------------------------------------------------------------- + const paused = await networkMonitor.monitorNetworkPauses( logger, + contracts, + networkSubgraph, + ) + + const isOperator = await networkMonitor.monitorIsOperator( + logger, + contracts, + specification.indexerOptions.address, wallet, - indexerAddress, - indexerUrl, - geoCoordinates, + ) + + const transactionManager = new TransactionManager( + networkProvider, + wallet, + paused, + isOperator, + specification.transactionMonitoring, + ) + + // -------------------------------------------------------------------------------- + // * Allocation Receipt Collector + // -------------------------------------------------------------------------------- + const receiptCollector = await AllocationReceiptCollector.create({ + logger, + metrics, + transactionManager: transactionManager, + models: queryFeeModels, + allocationExchange: contracts.allocationExchange, + networkSpecification: specification, + }) + + // -------------------------------------------------------------------------------- + // * Network + // -------------------------------------------------------------------------------- + return new Network( + logger, contracts, + wallet, networkSubgraph, - ethereum, + networkProvider, + transactionManager, + networkMonitor, + receiptCollector, + specification, paused, isOperator, - restakeRewards, - rebateClaimThreshold, - rebateClaimBatchThreshold, - rebateClaimMaxBatchSize, - poiDisputeMonitoring, - poiDisputableEpochs, - gasIncreaseTimeout, - gasIncreaseFactor, - baseFeePerGasMax, - maxTransactionAttempts, ) } static async provider( logger: Logger, metrics: Metrics, + networkIdentifier: string, networkURL: string, pollingInterval: number, ): Promise { logger.info(`Connect to Network chain`, { provider: networkURL, + pollingInterval, }) let providerUrl @@ -176,9 +262,11 @@ export class Network { process.exit(1) } + const provider_requests_metric_name = `eth_provider_requests_${networkIdentifier}` + logger.info('Metric name: ', { provider_requests_metric_name }) const ethProviderMetrics = { requests: new metrics.client.Counter({ - name: 'eth_provider_requests', + name: provider_requests_metric_name, help: 'Ethereum provider requests', registers: [metrics.registry], labelNames: ['method'], @@ -239,279 +327,27 @@ export class Network { return networkProvider } - // TODO: Move to NetworkMonitor - async claimableAllocations(disputableEpoch: number): Promise { - try { - const result = await this.networkSubgraph.query( - gql` - query allocations( - $indexer: String! - $disputableEpoch: Int! - $minimumQueryFeesCollected: BigInt! - ) { - allocations( - where: { - indexer: $indexer - closedAtEpoch_lte: $disputableEpoch - queryFeesCollected_gte: $minimumQueryFeesCollected - status: Closed - } - first: 1000 - ) { - id - indexer { - id - } - queryFeesCollected - allocatedTokens - createdAtEpoch - closedAtEpoch - createdAtBlockHash - closedAtBlockHash - subgraphDeployment { - id - stakedTokens - signalledTokens - queryFeesAmount - } - } - } - `, - { - indexer: this.indexerAddress.toLocaleLowerCase(), - disputableEpoch, - minimumQueryFeesCollected: this.indexerConfigs.rebateClaimThreshold.toString(), - }, - ) - - if (result.error) { - throw result.error - } - - const totalFees: BigNumber = result.data.allocations.reduce( - (total: BigNumber, rawAlloc: { queryFeesCollected: string }) => { - return total.add(BigNumber.from(rawAlloc.queryFeesCollected)) - }, - BigNumber.from(0), - ) - - const parsedAllocs: Allocation[] = - result.data.allocations.map(parseGraphQLAllocation) - - // If the total fees claimable do not meet the minimum required for batching, return an empty array - if ( - parsedAllocs.length > 0 && - totalFees.lt(this.indexerConfigs.rebateClaimBatchThreshold) - ) { - this.logger.info( - `Allocation rebate batch value does not meet minimum for claiming`, - { - batchValueGRT: formatGRT(totalFees), - rebateClaimBatchThreshold: formatGRT( - this.indexerConfigs.rebateClaimBatchThreshold, - ), - rebateClaimMaxBatchSize: this.indexerConfigs.rebateClaimMaxBatchSize, - batchSize: parsedAllocs.length, - allocations: parsedAllocs.map((allocation) => { - return { - allocation: allocation.id, - deployment: allocation.subgraphDeployment.id.display, - createdAtEpoch: allocation.createdAtEpoch, - closedAtEpoch: allocation.closedAtEpoch, - createdAtBlockHash: allocation.createdAtBlockHash, - } - }), - }, - ) - return [] - } - // Otherwise return the allos for claiming since the batch meets the minimum - return parsedAllocs - } catch (error) { - const err = indexerError(IndexerErrorCode.IE011, error) - this.logger.error(INDEXER_ERROR_MESSAGES[IndexerErrorCode.IE011], { - err, - }) - throw err - } - } - - // TODO: Move to NetworkMonitor - async disputableAllocations( - currentEpoch: number, - deployments: SubgraphDeploymentID[], - minimumAllocation: number, - ): Promise { - const logger = this.logger.child({ component: 'POI Monitor' }) - if (!this.indexerConfigs.poiDisputeMonitoring) { - logger.trace('POI monitoring disabled, skipping') - return Promise.resolve([]) - } - - logger.debug( - 'Query network for any newly closed allocations for deployment this indexer is syncing (available reference POIs)', - ) - - let dataRemaining = true - let allocations: Allocation[] = [] - - try { - const zeroPOI = utils.hexlify(Array(32).fill(0)) - const disputableEpoch = currentEpoch - this.indexerConfigs.poiDisputableEpochs - let lastCreatedAt = 0 - while (dataRemaining) { - const result = await this.networkSubgraph.query( - gql` - query allocations( - $deployments: [String!]! - $minimumAllocation: Int! - $disputableEpoch: Int! - $zeroPOI: String! - $createdAt: Int! - ) { - allocations( - where: { - createdAt_gt: $createdAt - subgraphDeployment_in: $deployments - allocatedTokens_gt: $minimumAllocation - closedAtEpoch_gte: $disputableEpoch - status: Closed - poi_not: $zeroPOI - } - first: 1000 - orderBy: createdAt - orderDirection: asc - ) { - id - createdAt - indexer { - id - } - poi - allocatedTokens - createdAtEpoch - closedAtEpoch - closedAtBlockHash - subgraphDeployment { - id - stakedTokens - signalledTokens - queryFeesAmount - } - } - } - `, - { - deployments: deployments.map((subgraph) => subgraph.bytes32), - minimumAllocation, - disputableEpoch, - createdAt: lastCreatedAt, - zeroPOI, - }, - ) - - if (result.error) { - throw result.error - } - if (result.data.allocations.length == 0) { - dataRemaining = false - } else { - lastCreatedAt = result.data.allocations.slice(-1)[0].createdAt - const parsedResult: Allocation[] = - result.data.allocations.map(parseGraphQLAllocation) - allocations = allocations.concat(parsedResult) - } - } - - // Get the unique set of dispute epochs to reduce the work fetching epoch start block hashes in the next step - let disputableEpochs = await this.epochs([ - ...allocations.reduce((epochNumbers: Set, allocation: Allocation) => { - epochNumbers.add(allocation.closedAtEpoch) - epochNumbers.add(allocation.closedAtEpoch - 1) - return epochNumbers - }, new Set()), - ]) - - disputableEpochs = await Promise.all( - disputableEpochs.map(async (epoch: Epoch): Promise => { - // TODO: May need to retry or skip epochs where obtaining start block fails - epoch.startBlockHash = (await this.ethereum.getBlock(epoch.startBlock))?.hash - return epoch - }), - ) - - return await Promise.all( - allocations.map(async (allocation) => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - allocation.closedAtEpochStartBlockHash = disputableEpochs.find( - (epoch) => epoch.id == allocation.closedAtEpoch, - )!.startBlockHash - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - allocation.previousEpochStartBlockHash = disputableEpochs.find( - (epoch) => epoch.id == allocation.closedAtEpoch - 1, - )!.startBlockHash - return allocation - }), - ) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE037, error) - logger.error(INDEXER_ERROR_MESSAGES.IE037, { - err, - }) - throw err - } - } - - async epochs(epochNumbers: number[]): Promise { - try { - const result = await this.networkSubgraph.query( - gql` - query epochs($epochs: [Int!]!) { - epoches(where: { id_in: $epochs }, first: 1000) { - id - startBlock - endBlock - signalledTokens - stakeDeposited - queryFeeRebates - totalRewards - totalIndexerRewards - totalDelegatorRewards - } - } - `, - { - epochs: epochNumbers, - }, - ) - - if (result.error) { - throw result.error - } - return result.data.epoches.map(parseGraphQLEpochs) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE038, error) - this.logger.error(INDEXER_ERROR_MESSAGES[IndexerErrorCode.IE038], { - err, - }) - throw err - } - } - // Start of SEND functions async register(): Promise { const geoHash = geohash.encode( - +this.indexerConfigs.geoCoordinates[0], - +this.indexerConfigs.geoCoordinates[1], + +this.specification.indexerOptions.geoCoordinates[0], + +this.specification.indexerOptions.geoCoordinates[1], ) const logger = this.logger.child({ - address: this.indexerAddress, - url: this.indexerConfigs.url, - geoCoordinates: this.indexerConfigs.geoCoordinates, + address: this.specification.indexerOptions.address, + url: this.specification.indexerOptions.url, + geoCoordinates: this.specification.indexerOptions.geoCoordinates, geoHash, }) + if (!this.specification.indexerOptions.register) { + logger.debug( + "Indexer was not registered because it was explicitly disabled in this Network's configuration.", + ) + return + } + await pRetry( async () => { try { @@ -520,32 +356,35 @@ export class Network { // Register the indexer (only if it hasn't been registered yet or // if its URL is different from what is registered on chain) const isRegistered = await this.contracts.serviceRegistry.isRegistered( - this.indexerAddress, + this.specification.indexerOptions.address, ) if (isRegistered) { const service = await this.contracts.serviceRegistry.services( - this.indexerAddress, + this.specification.indexerOptions.address, ) - if (service.url === this.indexerConfigs.url && service.geohash === geoHash) { + if ( + service.url === this.specification.indexerOptions.url && + service.geohash === geoHash + ) { if (await this.transactionManager.isOperator.value()) { logger.info(`Indexer already registered, operator status already granted`) - return } else { logger.info(`Indexer already registered, operator status not yet granted`) } + return } } const receipt = await this.transactionManager.executeTransaction( () => this.contracts.serviceRegistry.estimateGas.registerFor( - this.indexerAddress, - this.indexerConfigs.url, + this.specification.indexerOptions.address, + this.specification.indexerOptions.url, geoHash, ), (gasLimit) => this.contracts.serviceRegistry.registerFor( - this.indexerAddress, - this.indexerConfigs.url, + this.specification.indexerOptions.address, + this.specification.indexerOptions.url, geoHash, { gasLimit, @@ -576,199 +415,57 @@ export class Network { { retries: 5 } as pRetry.Options, ) } - - async claimMany(allocations: Allocation[]): Promise { - const logger = this.logger.child({ - action: 'ClaimMany', - }) - try { - logger.info( - `${allocations.length} allocations are eligible for rebate pool claims`, - { - allocations: allocations.map((allocation) => { - return { - allocation: allocation.id, - deployment: allocation.subgraphDeployment.id.display, - createdAtEpoch: allocation.createdAtEpoch, - closedAtEpoch: allocation.closedAtEpoch, - createdAtBlockHash: allocation.createdAtBlockHash, - } - }), - restakeRewards: this.indexerConfigs.restakeRewards, - }, - ) - - // Filter out already-claimed and still-active allocations - allocations = await pFilter(allocations, async (allocation: Allocation) => { - // Double-check whether the allocation is claimed to - // avoid unnecessary transactions. - // Note: We're checking the allocation state here, which is defined as - // - // enum AllocationState { Null, Active, Closed, Finalized, Claimed } - // - // in the contracts. - const state = await this.contracts.staking.getAllocationState(allocation.id) - if (state === 4) { - logger.trace( - `Allocation rebate rewards already claimed, ignoring ${allocation.id}.`, - ) - return false - } - if (state === 1) { - logger.trace(`Allocation still active, ignoring ${allocation.id}.`) - return false - } - return true - }) - - // Max claims per batch should roughly be equal to average gas per claim / block gas limit - // On-chain data shows an average of 120k gas per claim and the block gas limit is 15M - // We get at least 21k gas savings per inclusion of a claim in a batch - // A reasonable upper bound for this value is 200 assuming the system has the memory - // requirements to construct the transaction - const maxClaimsPerBatch = this.indexerConfigs.rebateClaimMaxBatchSize - - // When we construct the batch, we sort desc by query fees collected - // in order to maximise the value of the truncated batch - // more query fees collected should mean higher value rebates - const allocationIds = allocations - .sort((x, y) => - y.queryFeesCollected instanceof BigNumber - ? y.queryFeesCollected.gt(x.queryFeesCollected || 0) - ? 1 - : -1 - : -1, - ) - .map((allocation) => allocation.id) - .slice(0, maxClaimsPerBatch) - - if (allocationIds.length === 0) { - logger.info(`No allocation rebates to claim`) - return true - } else { - logger.info( - `Claim tokens from the rebate pool for ${allocationIds.length} allocations`, - { allocationIds }, - ) - } - - // Claim the earned value from the rebate pool, returning it to the indexers stake - const receipt = await this.transactionManager.executeTransaction( - () => - this.contracts.staking.estimateGas.claimMany( - allocationIds, - this.indexerConfigs.restakeRewards, - ), - (gasLimit) => - this.contracts.staking.claimMany( - allocationIds, - this.indexerConfigs.restakeRewards, - { - gasLimit, - }, - ), - logger.child({ function: 'staking.claimMany' }), - ) - if (receipt === 'paused' || receipt === 'unauthorized') { - return false - } - logger.info(`Successfully claimed ${allocationIds.length} allocations`, { - claimedAllocations: allocationIds, - }) - return true - } catch (err) { - logger.warn(`Failed to claim allocations`, { - err: indexerError(IndexerErrorCode.IE016, err), - }) - return false - } - } } -// TODO: Move to NetworkMonitor -async function monitorNetworkPauses( +async function connectWallet( + networkProvider: providers.Provider, + networkIdentifier: string, + mnemonic: string, logger: Logger, - contracts: NetworkContracts, - networkSubgraph: NetworkSubgraph, -): Promise> { - let networkPaused: boolean - try { - networkPaused = await contracts.controller.paused() - } catch (error) { - logger.error(`Failed to check for network pause with contracts controller`, { - suberror: IndexerErrorCode.IE007, - cause: error.message, - }) - throw indexerError(IndexerErrorCode.IE007, `Failed to check for network pause`) - } - return timer(60_000) - .reduce(async (currentlyPaused) => { - try { - const result = await networkSubgraph.query( - gql` - { - graphNetworks { - isPaused - } - } - `, - ) - - if (result.error) { - throw result.error - } - - if (!result.data || result.data.length === 0) { - throw new Error(`No data returned by network subgraph`) - } - - return result.data.graphNetworks[0].isPaused - } catch (err) { - logger.warn(`Failed to check for network pause, assuming it has not changed`, { - err: indexerError(IndexerErrorCode.IE007, err), - paused: currentlyPaused, - }) - return currentlyPaused - } - }, networkPaused) - .map((paused) => { - logger.info(paused ? `Network paused` : `Network active`) - return paused - }) +): Promise { + logger.info(`Connect wallet`, { + networkIdentifier: networkIdentifier, + }) + let wallet = Wallet.fromMnemonic(mnemonic) + wallet = wallet.connect(networkProvider) + logger.info(`Connected wallet`) + return wallet } -// TODO: Move to NetworkMonitor -async function monitorIsOperator( - logger: Logger, - contracts: NetworkContracts, - indexerAddress: Address, +async function connectToProtocolContracts( wallet: Wallet, -): Promise> { - // If indexer and operator address are identical, operator status is - // implicitly granted => we'll never have to check again - if (indexerAddress === toAddress(wallet.address)) { - logger.info(`Indexer and operator are identical, operator status granted`) - return mutable(true) + networkIdentifier: string, + logger: Logger, +): Promise { + const numericNetworkId = parseInt(networkIdentifier.split(':')[1]) + + // Confidence check: Should be unreachable since NetworkSpecification was validated before + if (resolveChainId(numericNetworkId) !== networkIdentifier) { + throw new Error(`Invalid network identifier: ${networkIdentifier}`) } - return timer(60_000) - .reduce(async (isOperator) => { - try { - return await contracts.staking.isOperator(wallet.address, indexerAddress) - } catch (err) { - logger.warn( - `Failed to check operator status for indexer, assuming it has not changed`, - { err: indexerError(IndexerErrorCode.IE008, err), isOperator }, - ) - return isOperator - } - }, await contracts.staking.isOperator(wallet.address, indexerAddress)) - .map((isOperator) => { - logger.info( - isOperator - ? `Have operator status for indexer` - : `No operator status for indexer`, - ) - return isOperator - }) + logger.info(`Connect to contracts`, { + network: networkIdentifier, + }) + + let contracts + try { + contracts = await connectContracts(wallet, numericNetworkId) + } catch (error) { + const errorMessage = + 'Failed to connect to contracts, please ensure you are using the intended protocol network.' + logger.error(errorMessage, { error, networkIdentifier, numericNetworkId }) + throw new Error(`${errorMessage} Error: ${error}`) + } + logger.info(`Successfully connected to contracts`, { + curation: contracts.curation.address, + disputeManager: contracts.disputeManager.address, + epochManager: contracts.epochManager.address, + gns: contracts.gns.address, + rewardsManager: contracts.rewardsManager.address, + serviceRegistry: contracts.serviceRegistry.address, + staking: contracts.staking.address, + token: contracts.token.address, + }) + return contracts } diff --git a/packages/indexer-common/src/operator.ts b/packages/indexer-common/src/operator.ts new file mode 100644 index 000000000..1769d5b16 --- /dev/null +++ b/packages/indexer-common/src/operator.ts @@ -0,0 +1,583 @@ +import { + ActionFilter, + ActionItem, + ActionResult, + ActionStatus, + ActionType, + Allocation, + AllocationDecision, + AllocationManagementMode, + INDEXING_RULE_GLOBAL, + IndexerErrorCode, + IndexerManagementClient, + IndexingRuleAttributes, + SubgraphIdentifierType, + indexerError, + specification as spec, + Action, + POIDisputeAttributes, +} from '@graphprotocol/indexer-common' +import { Logger, formatGRT } from '@graphprotocol/common-ts' +import { BigNumber, utils } from 'ethers' +import gql from 'graphql-tag' +import pMap from 'p-map' +import { CombinedError } from '@urql/core' + +const POI_DISPUTES_CONVERTERS_FROM_GRAPHQL: Record< + keyof POIDisputeAttributes, + (x: never) => string | BigNumber | number | null +> = { + allocationID: (x) => x, + subgraphDeploymentID: (x) => x, + allocationIndexer: (x) => x, + allocationAmount: (x) => x, + allocationProof: (x) => x, + closedEpoch: (x) => +x, + closedEpochStartBlockHash: (x) => x, + closedEpochStartBlockNumber: (x) => +x, + closedEpochReferenceProof: (x) => x, + previousEpochStartBlockHash: (x) => x, + previousEpochStartBlockNumber: (x) => +x, + previousEpochReferenceProof: (x) => x, + status: (x) => x, + protocolNetwork: (x) => x, +} + +/** + * Parses a POI dispute returned from the indexer management GraphQL + * API into normalized form. + */ +const disputeFromGraphQL = ( + dispute: Partial, +): POIDisputeAttributes => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const obj = {} as any + for (const [key, value] of Object.entries(dispute)) { + if (key === '__typename') { + continue + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + obj[key] = (POI_DISPUTES_CONVERTERS_FROM_GRAPHQL as any)[key](value) + } + return obj as POIDisputeAttributes +} + +// This component is responsible for managing indexing rules, actions, allocations, and +// POI disputes. +export class Operator { + logger: Logger + indexerManagement: IndexerManagementClient + specification: spec.NetworkSpecification + + constructor( + logger: Logger, + indexerManagement: IndexerManagementClient, + specification: spec.NetworkSpecification, + ) { + this.logger = logger.child({ + component: 'Operator', + protocolNetwork: specification.networkIdentifier, + }) + this.indexerManagement = indexerManagement + this.specification = specification + } + + // -------------------------------------------------------------------------------- + // * Indexing Rules + // -------------------------------------------------------------------------------- + // Retrieves the indexing rules from the indexer management API. + async indexingRules(merged: boolean): Promise { + try { + this.logger.debug('Fetching indexing rules') + const result = await this.indexerManagement + .query( + gql` + query indexingRules($merged: Boolean!, $protocolNetwork: String) { + indexingRules(merged: $merged, protocolNetwork: $protocolNetwork) { + identifier + identifierType + allocationAmount + allocationLifetime + autoRenewal + parallelAllocations + maxAllocationPercentage + minSignal + maxSignal + minStake + minAverageQueryFees + custom + decisionBasis + requireSupported + protocolNetwork + } + } + `, + { merged, protocolNetwork: this.specification.networkIdentifier }, + ) + .toPromise() + + if (result.error) { + throw result.error + } + this.logger.trace('Fetched indexing rules', { + count: result.data.indexingRules.length, + rules: result.data.indexingRules.map((rule: IndexingRuleAttributes) => { + return { + identifier: rule.identifier, + identifierType: rule.identifierType, + decisionBasis: rule.decisionBasis, + protocolNetwork: rule.protocolNetwork, + } + }), + }) + return result.data.indexingRules + } catch (error) { + const err = indexerError(IndexerErrorCode.IE025, error) + this.logger.error(`Failed to query indexer management API`, { err }) + throw err + } + } + + async ensureGlobalIndexingRule(): Promise { + const identifier = { + identifier: INDEXING_RULE_GLOBAL, + protocolNetwork: this.specification.networkIdentifier, + } + try { + const globalRule = await this.indexerManagement + .query( + gql` + query indexingRule($identifier: IndexingRuleIdentifier!) { + indexingRule(identifier: $identifier, merged: false) { + identifier + identifierType + allocationAmount + decisionBasis + requireSupported + protocolNetwork + } + } + `, + { identifier }, + ) + .toPromise() + + if (!globalRule.data.indexingRule) { + this.logger.info(`Creating default "global" indexing rule`) + + const defaults = { + ...identifier, + identifierType: SubgraphIdentifierType.GROUP, + allocationAmount: + this.specification.indexerOptions.defaultAllocationAmount.toString(), + parallelAllocations: 1, + decisionBasis: 'rules', + requireSupported: true, + safety: true, + } + + const defaultGlobalRule = await this.indexerManagement + .mutation( + gql` + mutation setIndexingRule($rule: IndexingRuleInput!) { + setIndexingRule(rule: $rule) { + identifier + identifierType + allocationAmount + allocationLifetime + autoRenewal + parallelAllocations + maxAllocationPercentage + minSignal + maxSignal + minStake + minAverageQueryFees + custom + decisionBasis + requireSupported + safety + protocolNetwork + } + } + `, + { rule: defaults }, + ) + .toPromise() + + if (defaultGlobalRule.error) { + throw defaultGlobalRule.error + } + + this.logger.info(`Created default "global" indexing rule`, { + rule: defaultGlobalRule.data.setIndexingRule, + }) + } + } catch (error) { + const err = indexerError(IndexerErrorCode.IE017, error) + this.logger.warn('Failed to ensure default "global" indexing rule', { + err, + }) + throw err + } + } + + // -------------------------------------------------------------------------------- + // * Actions + // -------------------------------------------------------------------------------- + + async fetchActions(actionFilter: ActionFilter): Promise { + const result = await this.indexerManagement + .query( + gql` + query actions($filter: ActionFilter!) { + actions(filter: $filter) { + id + type + allocationID + deploymentID + amount + poi + force + source + reason + priority + transaction + status + failureReason + } + } + `, + { filter: actionFilter }, + ) + .toPromise() + + if (result.error) { + throw result.error + } + + return result.data.actions + } + + async queueAction(action: ActionItem): Promise { + let status = ActionStatus.QUEUED + switch (this.specification.indexerOptions.allocationManagementMode) { + case AllocationManagementMode.MANUAL: + throw Error(`Cannot queue actions when AllocationManagementMode = 'MANUAL'`) + case AllocationManagementMode.AUTO: + status = ActionStatus.APPROVED + break + case AllocationManagementMode.OVERSIGHT: + status = ActionStatus.QUEUED + } + + const actionInput = { + ...action.params, + status, + type: action.type, + source: 'indexerAgent', + reason: action.reason, + priority: 0, + protocolNetwork: action.protocolNetwork, + } + this.logger.trace(`Queueing action input`, { + actionInput, + }) + const actionResult = await this.indexerManagement + .mutation( + gql` + mutation queueActions($actions: [ActionInput!]!) { + queueActions(actions: $actions) { + id + type + deploymentID + source + reason + priority + status + protocolNetwork + } + } + `, + { actions: [actionInput] }, + ) + .toPromise() + + if (actionResult.error) { + if (actionResult.error instanceof CombinedError) { + if (actionResult.error.message.includes('Duplicate')) { + this.logger.warn( + `Action not queued: Already a queued action targeting ${actionInput.deploymentID} from another source`, + { action }, + ) + } else if (actionResult.error.message.includes('Recently executed')) { + this.logger.warn( + `Action not queued: A recently executed action was found targeting ${actionInput.deploymentID}`, + { action }, + ) + } + return [] + } + throw actionResult.error + } + + if (actionResult.data.queueActions.length > 0) { + this.logger.info(`Queued ${action.type} action for execution`, { + queuedAction: actionResult.data.queueActions, + }) + } + + return actionResult.data.queueActions + } + + // -------------------------------------------------------------------------------- + // * Allocations + // -------------------------------------------------------------------------------- + async createAllocation( + logger: Logger, + deploymentAllocationDecision: AllocationDecision, + mostRecentlyClosedAllocation: Allocation | undefined, + ): Promise { + const desiredAllocationAmount = deploymentAllocationDecision.ruleMatch.rule + ?.allocationAmount + ? BigNumber.from(deploymentAllocationDecision.ruleMatch.rule.allocationAmount) + : this.specification.indexerOptions.defaultAllocationAmount + + logger.info(`No active allocation for deployment, creating one now`, { + allocationAmount: formatGRT(desiredAllocationAmount), + }) + + // Skip allocating if the previous allocation for this deployment was closed with 0x00 POI but rules set to un-safe + if ( + deploymentAllocationDecision.ruleMatch.rule?.safety && + mostRecentlyClosedAllocation && + mostRecentlyClosedAllocation.poi === utils.hexlify(Array(32).fill(0)) + ) { + logger.warn( + `Skipping allocation to this deployment as the last allocation to it was closed with a zero POI`, + { + notSafe: !deploymentAllocationDecision.ruleMatch.rule?.safety, + deployment: deploymentAllocationDecision.deployment, + closedAllocation: mostRecentlyClosedAllocation.id, + }, + ) + return + } + + // Send AllocateAction to the queue + await this.queueAction({ + params: { + deploymentID: deploymentAllocationDecision.deployment.ipfsHash, + amount: formatGRT(desiredAllocationAmount), + }, + type: ActionType.ALLOCATE, + reason: deploymentAllocationDecision.reasonString(), + protocolNetwork: deploymentAllocationDecision.protocolNetwork, + }) + + return + } + + async closeEligibleAllocations( + logger: Logger, + deploymentAllocationDecision: AllocationDecision, + activeDeploymentAllocations: Allocation[], + epoch: number, + ): Promise { + const activeDeploymentAllocationsEligibleForClose = activeDeploymentAllocations + .filter((allocation) => allocation.createdAtEpoch < epoch) + .map((allocation) => allocation.id) + // Make sure to close all active allocations on the way out + if (activeDeploymentAllocationsEligibleForClose.length > 0) { + logger.info( + `Deployment is not (or no longer) worth allocating towards, close allocations`, + { + eligibleForClose: activeDeploymentAllocationsEligibleForClose, + reason: deploymentAllocationDecision.reasonString(), + }, + ) + await pMap( + // We can only close allocations from a previous epoch; + // try the others again later + activeDeploymentAllocationsEligibleForClose, + async (allocation) => { + // Send unallocate action to the queue + await this.queueAction({ + params: { + allocationID: allocation, + deploymentID: deploymentAllocationDecision.deployment.ipfsHash, + poi: undefined, + force: false, + }, + type: ActionType.UNALLOCATE, + reason: deploymentAllocationDecision.reasonString(), + protocolNetwork: deploymentAllocationDecision.protocolNetwork, + } as ActionItem) + }, + { concurrency: 1 }, + ) + } + } + + async refreshExpiredAllocations( + logger: Logger, + deploymentAllocationDecision: AllocationDecision, + expiredAllocations: Allocation[], + ): Promise { + if (deploymentAllocationDecision.ruleMatch.rule?.autoRenewal) { + logger.info(`Reallocating expired allocations`, { + number: expiredAllocations.length, + expiredAllocations: expiredAllocations.map((allocation) => allocation.id), + }) + + const desiredAllocationAmount = deploymentAllocationDecision.ruleMatch.rule + ?.allocationAmount + ? BigNumber.from(deploymentAllocationDecision.ruleMatch.rule.allocationAmount) + : this.specification.indexerOptions.defaultAllocationAmount + + // Queue reallocate actions to be picked up by the worker + await pMap( + expiredAllocations, + async (allocation) => { + await this.queueAction({ + params: { + allocationID: allocation.id, + deploymentID: deploymentAllocationDecision.deployment.ipfsHash, + amount: formatGRT(desiredAllocationAmount), + }, + type: ActionType.REALLOCATE, + reason: `${deploymentAllocationDecision.reasonString()}:allocationExpiring`, // Need to update to include 'ExpiringSoon' + protocolNetwork: deploymentAllocationDecision.protocolNetwork, + }) + }, + { + stopOnError: false, + concurrency: 1, + }, + ) + } else { + logger.info( + `Skipping reallocating expired allocation since the corresponding rule has 'autoRenewal' = False`, + { + number: expiredAllocations.length, + expiredAllocations: expiredAllocations.map((allocation) => allocation.id), + }, + ) + } + return + } + // -------------------------------------------------------------------------------- + // POI Disputes + // -------------------------------------------------------------------------------- + + async storePoiDisputes( + disputes: POIDisputeAttributes[], + ): Promise { + try { + const result = await this.indexerManagement + .mutation( + gql` + mutation storeDisputes($disputes: [POIDisputeInput!]!) { + storeDisputes(disputes: $disputes) { + allocationID + subgraphDeploymentID + allocationIndexer + allocationAmount + allocationProof + closedEpoch + closedEpochStartBlockHash + closedEpochStartBlockNumber + closedEpochReferenceProof + previousEpochStartBlockHash + previousEpochStartBlockNumber + previousEpochReferenceProof + status + protocolNetwork + } + } + `, + { disputes: disputes }, + ) + .toPromise() + + if (result.error) { + throw result.error + } + + return result.data.storeDisputes.map( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (dispute: Record) => { + return disputeFromGraphQL(dispute) + }, + ) + } catch (error) { + const err = indexerError(IndexerErrorCode.IE039, error) + this.logger.error('Failed to store potential POI disputes', { + err, + }) + throw err + } + } + + async fetchPOIDisputes( + status: string, + minClosedEpoch: number, + protocolNetwork: string | undefined, + ): Promise { + try { + const result = await this.indexerManagement + .query( + gql` + query disputes( + $status: String! + $minClosedEpoch: Int! + $protocolNetwork: String! + ) { + disputes( + status: $status + minClosedEpoch: $minClosedEpoch + protocolNetwork: $protocolNetwork + ) { + allocationID + subgraphDeploymentID + allocationIndexer + allocationAmount + allocationProof + closedEpoch + closedEpochStartBlockHash + closedEpochStartBlockNumber + closedEpochReferenceProof + previousEpochStartBlockHash + previousEpochStartBlockNumber + previousEpochReferenceProof + status + protocolNetwork + } + } + `, + { + status, + minClosedEpoch, + protocolNetwork, + }, + ) + .toPromise() + + if (result.error) { + throw result.error + } + + return result.data.disputes.map( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (dispute: Record) => { + return disputeFromGraphQL(dispute) + }, + ) + } catch (error) { + const err = indexerError(IndexerErrorCode.IE039, error) + this.logger.error('Failed to fetch POI disputes', { + err, + }) + throw err + } + } +} diff --git a/packages/indexer-common/src/parsers/__tests__/parsers.ts b/packages/indexer-common/src/parsers/__tests__/parsers.ts new file mode 100644 index 000000000..4a7595270 --- /dev/null +++ b/packages/indexer-common/src/parsers/__tests__/parsers.ts @@ -0,0 +1,10 @@ +import { validateNetworkIdentifier } from '../validators' + +describe('validateNetworkIdentifier tests', () => { + it('should parse valid network identifiers', () => { + expect(validateNetworkIdentifier('goerli')).toBe('eip155:5') + expect(validateNetworkIdentifier('mainnet')).toBe('eip155:1') + expect(validateNetworkIdentifier('eip155:1')).toBe('eip155:1') + expect(validateNetworkIdentifier('eip155:5')).toBe('eip155:5') + }) +}) diff --git a/packages/indexer-common/src/parsers/basic-types.ts b/packages/indexer-common/src/parsers/basic-types.ts new file mode 100644 index 000000000..68b945b5e --- /dev/null +++ b/packages/indexer-common/src/parsers/basic-types.ts @@ -0,0 +1,34 @@ +// Parser combinators for basic types + +import P from 'parsimmon' +import { resolveChainId } from '../indexer-management/types' + +// Checks if the provided network identifier is supported by the Indexer Agent. +function validateNetworkIdentifier(n: string): P.Parser { + try { + const valid = resolveChainId(n) + return P.succeed(valid) + } catch (e) { + return P.fail('a supported network identifier') + } +} + +// A basic URL parser. +export const url = P.regex(/^https?:.*/) + .map((x) => new URL(x)) + .desc('a valid URL') + +// Source: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-2.md +export const caip2IdRegex = /^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}$/ +const caip2Id = P.regex(caip2IdRegex).chain(validateNetworkIdentifier) + +// A valid human friendly network name / alias. +const networkAlias = P.regex(/[a-z-]+/).chain(validateNetworkIdentifier) + +// Either a CAIP-2 or an alias. +export const networkIdentifier = P.alt(caip2Id, networkAlias) + +// A basic `base58btc` parser for CIDv0 (IPFS Hashes) +export const base58 = P.regex(/^Qm[1-9A-HJ-NP-Za-km-z]{44,}$/).desc( + 'An IPFS Content Identifer (Qm...)', +) diff --git a/packages/indexer-common/src/parsers/index.ts b/packages/indexer-common/src/parsers/index.ts new file mode 100644 index 000000000..2ec2291fc --- /dev/null +++ b/packages/indexer-common/src/parsers/index.ts @@ -0,0 +1 @@ +export * from './validators' diff --git a/packages/indexer-common/src/parsers/validators.ts b/packages/indexer-common/src/parsers/validators.ts new file mode 100644 index 000000000..cf81e70a2 --- /dev/null +++ b/packages/indexer-common/src/parsers/validators.ts @@ -0,0 +1,30 @@ +// Final parsers (validators) that use parser combinators defined in the 'parsers' module but don't +// expose their internal parsing interface. + +import P from 'parsimmon' + +import { networkIdentifier, base58 } from './basic-types' +export { caip2IdRegex } from './basic-types' + +// Generic function that takes a parser of type T and attempts to parse it from a string. If it +// fails, then it will throw an error with an explanation of what was expected, as well as the +// portion of the input that was parsed and what's remaining to parse. +function parse(parser: P.Parser, input: string): T { + const parseResult = parser.parse(input) + if (parseResult.status) { + return parseResult.value + } + const expected = parseResult.expected[0] + const parsed = input.slice(0, parseResult.index.offset) + const remaining = input.slice(parseResult.index.offset) + throw new Error( + `Failed to parse "${input}". Expected: ${expected}. Parsed up to: "${parsed}". Remaining: "${remaining}"`, + ) +} +export function validateNetworkIdentifier(input: string): string { + return parse(networkIdentifier, input) +} + +export function validateIpfsHash(input: string): string { + return parse(base58, input) +} diff --git a/packages/indexer-common/src/query-fees/allocation-utils.ts b/packages/indexer-common/src/query-fees/allocation-utils.ts index d43ce5841..659ae696e 100644 --- a/packages/indexer-common/src/query-fees/allocation-utils.ts +++ b/packages/indexer-common/src/query-fees/allocation-utils.ts @@ -6,6 +6,7 @@ export const ensureAllocationSummary = async ( models: QueryFeeModels, allocation: Address, transaction: Transaction, + protocolNetwork: string, ): Promise<[AllocationSummary, boolean]> => { const [summary, isNew] = await models.allocationSummaries.findOrBuild({ where: { allocation }, @@ -18,6 +19,7 @@ export const ensureAllocationSummary = async ( openTransfers: 0, collectedFees: '0', withdrawnFees: '0', + protocolNetwork, }, transaction, }) diff --git a/packages/indexer-common/src/query-fees/models.ts b/packages/indexer-common/src/query-fees/models.ts index 01b1458db..37bd4b827 100644 --- a/packages/indexer-common/src/query-fees/models.ts +++ b/packages/indexer-common/src/query-fees/models.ts @@ -1,11 +1,13 @@ import { DataTypes, Sequelize, Model, Association } from 'sequelize' import { Address } from '@graphprotocol/common-ts' +import { caip2IdRegex } from '../parsers' export interface AllocationReceiptAttributes { id: string allocation: Address fees: string signature: string + protocolNetwork: string } export class AllocationReceipt @@ -16,6 +18,7 @@ export class AllocationReceipt public allocation!: Address public fees!: string public signature!: string + public protocolNetwork!: string public readonly createdAt!: Date public readonly updatedAt!: Date @@ -25,6 +28,7 @@ export interface VoucherAttributes { allocation: Address amount: string signature: string + protocolNetwork: string } export class Voucher extends Model implements VoucherAttributes { @@ -34,6 +38,7 @@ export class Voucher extends Model implements VoucherAttribut public readonly createdAt!: Date public readonly updatedAt!: Date + public protocolNetwork!: string public readonly allocationSummary?: AllocationSummary @@ -47,6 +52,7 @@ export interface TransferReceiptAttributes { signer: Address fees: string signature: string + protocolNetwork: string } export class TransferReceipt @@ -57,6 +63,7 @@ export class TransferReceipt public signer!: Address public fees!: string public signature!: string + public protocolNetwork!: string public readonly createdAt!: Date public readonly updatedAt!: Date @@ -81,6 +88,7 @@ export interface TransferAttributes { signer: Address allocationClosedAt: Date | null status: TransferStatus + protocolNetwork: string } export class Transfer extends Model implements TransferAttributes { @@ -89,6 +97,7 @@ export class Transfer extends Model implements TransferAttri public signer!: Address public allocationClosedAt!: Date | null public status!: TransferStatus + public protocolNetwork!: string public readonly createdAt!: Date public readonly updatedAt!: Date @@ -109,6 +118,7 @@ export interface AllocationSummaryAttributes { openTransfers: number collectedFees: string withdrawnFees: string + protocolNetwork: string } export class AllocationSummary @@ -123,6 +133,7 @@ export class AllocationSummary public openTransfers!: number public collectedFees!: string public withdrawnFees!: string + public protocolNetwork!: string public readonly createdAt!: Date public readonly updatedAt!: Date @@ -174,6 +185,14 @@ export function defineQueryFeeModels(sequelize: Sequelize): QueryFeeModels { min: 0.0, }, }, + protocolNetwork: { + type: DataTypes.STRING, + primaryKey: true, + allowNull: false, + validate: { + is: caip2IdRegex, + }, + }, }, { sequelize, tableName: 'allocation_receipts' }, ) @@ -196,6 +215,14 @@ export function defineQueryFeeModels(sequelize: Sequelize): QueryFeeModels { type: DataTypes.STRING, allowNull: false, }, + protocolNetwork: { + type: DataTypes.STRING, + primaryKey: true, + allowNull: false, + validate: { + is: caip2IdRegex, + }, + }, }, { sequelize, tableName: 'vouchers' }, ) @@ -223,6 +250,14 @@ export function defineQueryFeeModels(sequelize: Sequelize): QueryFeeModels { min: 0.0, }, }, + protocolNetwork: { + type: DataTypes.STRING, + primaryKey: true, + allowNull: false, + validate: { + is: caip2IdRegex, + }, + }, }, { sequelize, tableName: 'transfer_receipts' }, ) @@ -256,6 +291,14 @@ export function defineQueryFeeModels(sequelize: Sequelize): QueryFeeModels { ), allowNull: false, }, + protocolNetwork: { + type: DataTypes.STRING, + primaryKey: true, + allowNull: false, + validate: { + is: caip2IdRegex, + }, + }, }, { sequelize, tableName: 'transfers' }, ) @@ -295,6 +338,14 @@ export function defineQueryFeeModels(sequelize: Sequelize): QueryFeeModels { type: DataTypes.DECIMAL, allowNull: false, }, + protocolNetwork: { + type: DataTypes.STRING, + primaryKey: true, + allowNull: false, + validate: { + is: caip2IdRegex, + }, + }, }, { sequelize, tableName: 'allocation_summaries' }, ) diff --git a/packages/indexer-common/src/rules.ts b/packages/indexer-common/src/rules.ts index b1a37d495..e03bfd40e 100644 --- a/packages/indexer-common/src/rules.ts +++ b/packages/indexer-common/src/rules.ts @@ -1,6 +1,7 @@ import { IndexingDecisionBasis, IndexingRuleAttributes } from './indexer-management' import { nullPassThrough, parseBoolean } from './utils' import { parseGRT } from '@graphprotocol/common-ts' +import { validateNetworkIdentifier } from './parsers' export const parseDecisionBasis = (s: string): IndexingDecisionBasis => { if (!['always', 'never', 'rules', 'offchain'].includes(s)) { @@ -34,6 +35,7 @@ const INDEXING_RULE_READABLE_TO_MODEL_PARSERS: Record< custom: nullPassThrough(JSON.parse), requireSupported: (x) => parseBoolean(x), safety: (x) => parseBoolean(x), + protocolNetwork: (x: string) => validateNetworkIdentifier(x), } export const parseIndexingRule = ( diff --git a/packages/indexer-common/src/subgraphs.ts b/packages/indexer-common/src/subgraphs.ts index 3cf2524d4..f06e03bd0 100644 --- a/packages/indexer-common/src/subgraphs.ts +++ b/packages/indexer-common/src/subgraphs.ts @@ -7,6 +7,10 @@ import { IndexingDecisionBasis, IndexingRuleAttributes, } from './indexer-management' +import { DocumentNode, print } from 'graphql' +import gql from 'graphql-tag' +import { QueryResult } from './network-subgraph' +import { mergeSelectionSets, sleep } from './utils' export enum SubgraphIdentifierType { DEPLOYMENT = 'deployment', @@ -141,7 +145,7 @@ export async function processIdentifier( // SUBGRAPH, // } -enum ActivationCriteria { +export enum ActivationCriteria { NA = 'na', NONE = 'none', ALWAYS = 'always', @@ -152,6 +156,7 @@ enum ActivationCriteria { NEVER = 'never', OFFCHAIN = 'offchain', INVALID_ALLOCATION_AMOUNT = 'invalid_allocation_amount', + L2_TRANSFER_SUPPORT = 'l2_transfer_support', } interface RuleMatch { @@ -163,12 +168,14 @@ export class AllocationDecision { declare deployment: SubgraphDeploymentID declare toAllocate: boolean declare ruleMatch: RuleMatch + declare protocolNetwork: string constructor( deployment: SubgraphDeploymentID, matchingRule: IndexingRuleAttributes | undefined, toAllocate: boolean, ruleActivator: ActivationCriteria, + protocolNetwork: string, ) { this.deployment = deployment this.toAllocate = toAllocate @@ -176,6 +183,7 @@ export class AllocationDecision { rule: matchingRule, activationCriteria: ruleActivator, } + this.protocolNetwork = protocolNetwork } public reasonString(): string { return `${this.ruleMatch.rule?.identifierType ?? 'none'}:${ @@ -223,6 +231,7 @@ export function isDeploymentWorthAllocatingTowards( deploymentRule, false, ActivationCriteria.INVALID_ALLOCATION_AMOUNT, + deployment.protocolNetwork, ) } @@ -233,6 +242,7 @@ export function isDeploymentWorthAllocatingTowards( deploymentRule, false, ActivationCriteria.UNSUPPORTED, + deployment.protocolNetwork, ) } @@ -243,13 +253,16 @@ export function isDeploymentWorthAllocatingTowards( undefined, false, ActivationCriteria.NA, + deployment.protocolNetwork, ) + case IndexingDecisionBasis.ALWAYS: return new AllocationDecision( deployment.id, deploymentRule, true, ActivationCriteria.ALWAYS, + deployment.protocolNetwork, ) case IndexingDecisionBasis.NEVER: return new AllocationDecision( @@ -257,6 +270,7 @@ export function isDeploymentWorthAllocatingTowards( deploymentRule, false, ActivationCriteria.NEVER, + deployment.protocolNetwork, ) case IndexingDecisionBasis.OFFCHAIN: return new AllocationDecision( @@ -264,6 +278,7 @@ export function isDeploymentWorthAllocatingTowards( deploymentRule, false, ActivationCriteria.OFFCHAIN, + deployment.protocolNetwork, ) case IndexingDecisionBasis.RULES: { const stakedTokens = BigNumber.from(deployment.stakedTokens) @@ -276,6 +291,7 @@ export function isDeploymentWorthAllocatingTowards( deploymentRule, true, ActivationCriteria.MIN_STAKE, + deployment.protocolNetwork, ) } else if ( deploymentRule.minSignal && @@ -286,6 +302,7 @@ export function isDeploymentWorthAllocatingTowards( deploymentRule, true, ActivationCriteria.SIGNAL_THRESHOLD, + deployment.protocolNetwork, ) } else if ( deploymentRule.minAverageQueryFees && @@ -296,6 +313,7 @@ export function isDeploymentWorthAllocatingTowards( deploymentRule, true, ActivationCriteria.MIN_AVG_QUERY_FEES, + deployment.protocolNetwork, ) } else { return new AllocationDecision( @@ -303,8 +321,204 @@ export function isDeploymentWorthAllocatingTowards( deploymentRule, false, ActivationCriteria.NONE, + deployment.protocolNetwork, ) } } } } + +export interface ProviderInterface { + getBlockNumber(): Promise +} + +/* eslint-disable @typescript-eslint/no-explicit-any */ +export interface LoggerInterface { + trace(msg: string, o?: object, ...args: any[]): void + error(msg: string, o?: object, ...args: any[]): void + warn(msg: string, o?: object, ...args: any[]): void +} + +export interface SubgraphQueryInterface { + query( + query: DocumentNode, + variables?: Record, + ): Promise> +} +/* eslint-enable @typescript-eslint/no-explicit-any */ + +const blockNumberQuery = gql` + { + _meta { + block { + number + } + } + } +` + +interface BlockNumberInterface { + data: { _meta: { block: { number: number } } } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + errors?: any +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Variables = Record + +export class SubgraphFreshnessChecker { + subgraphName: string + provider: ProviderInterface + threshold: number + logger: LoggerInterface + sleepDurationMillis: number + retries: number + + constructor( + subgraphName: string, + provider: ProviderInterface, + freshnessThreshold: number, + sleepDurationMillis: number, + logger: LoggerInterface, + retries: number, + ) { + this.subgraphName = subgraphName + this.provider = provider + this.threshold = freshnessThreshold + this.sleepDurationMillis = sleepDurationMillis + this.logger = logger + this.retries = retries + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public async checkedQuery( + subgraph: SubgraphQueryInterface, + query: DocumentNode, + variables?: Variables, + ): Promise> { + // Try to inject the latest block number into the original query. + let updatedQuery = query + try { + updatedQuery = mergeSelectionSets(query, blockNumberQuery) + } catch (err) { + const errorMsg = `Failed to append block number into ${this.subgraphName} query` + this.logger.error(errorMsg, { subgraph: this.subgraphName, query: print(query) }) + throw new Error(errorMsg) + } + + // Try obtaining a fresh subgraph query at most `this.retry` times + return this.checkedQueryRecursive( + updatedQuery, + subgraph, + this.retries, + variables, + ) + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private async checkedQueryRecursive( + updatedQuery: DocumentNode, + subgraph: SubgraphQueryInterface, + retriesLeft: number, + variables?: Variables, + ): Promise> { + if (retriesLeft < 0) { + const errorMsg = `Max retries reached for ${this.subgraphName} freshness check` + this.logger.error(errorMsg, { + subgraph: this.subgraphName, + query: print(updatedQuery), + }) + throw new Error(errorMsg) + } + + // Obtain the latest network block number and subgraph query in parallel. + const subgraphQueryPromise = subgraph.query(updatedQuery, variables) as Promise< + QueryResult & BlockNumberInterface + > + const latestNetworkBlockPromise = this.provider.getBlockNumber() + const [subgraphQueryResult, latestNetworkBlock] = await Promise.all([ + subgraphQueryPromise, + latestNetworkBlockPromise, + ]) + + // Return it early if query results contains errors + if (subgraphQueryResult.errors || subgraphQueryResult.error) { + return subgraphQueryResult + } + + // Check for missing block metadata + const queryShapeError = this.checkMalformedQueryResult(subgraphQueryResult) + if (queryShapeError) { + const errorMsg = `Failed to infer block number for ${this.subgraphName} query: ${queryShapeError}` + this.logger.error(errorMsg, { + query: print(updatedQuery), + subgraph: this.subgraphName, + error: queryShapeError, + subgraphQueryResult, + }) + throw new Error(errorMsg) + } + + // At this point we have validated that this value exists and is numeric. + const latestIndexedBlock: number = subgraphQueryResult.data._meta.block.number + + // Check subgraph freshness + const blockDistance = latestNetworkBlock - latestIndexedBlock + const logInfo = { + latestIndexedBlock, + latestNetworkBlock, + blockDistance, + freshnessThreshold: this.threshold, + subgraph: this.subgraphName, + retriesLeft, + } + this.logger.trace('Performing subgraph freshness check', logInfo) + + if (blockDistance < 0) { + // Invariant violated: Subgraph can't be ahead of network latest block + const errorMsg = `${this.subgraphName}'s latest indexed block (${latestIndexedBlock}) is higher than Network's latest block (${latestNetworkBlock})` + console.warn(errorMsg, logInfo) + } + + if (blockDistance > this.threshold) { + // Reenter function + this.logger.warn( + `${this.subgraphName} is not fresh. Sleeping for ${this.sleepDurationMillis} ms before retrying`, + logInfo, + ) + await sleep(this.sleepDurationMillis) + return this.checkedQueryRecursive( + updatedQuery, + subgraph, + retriesLeft - 1, + variables, + ) + } else { + this.logger.trace(`${this.subgraphName} is fresh`, logInfo) + } + return subgraphQueryResult + } + + // Checks if the query result has the expecte + // eslint-disable-next-line @typescript-eslint/no-explicit-any + checkMalformedQueryResult(subgraphQueryResult: any): string | undefined { + if (!subgraphQueryResult) { + return 'Subgraph query result is null or undefined' + } + if (!subgraphQueryResult.data) { + return 'Subgraph query data is null or undefined' + } + if (!subgraphQueryResult.data._meta) { + return 'Query metadata is null or undefined' + } + if (!subgraphQueryResult.data._meta.block) { + return 'Block metadata is null or undefined' + } + if ( + !subgraphQueryResult.data._meta.block.number && + typeof subgraphQueryResult.data._meta.block.number === 'number' + ) { + return 'Block number is null or undefined' + } + } +} diff --git a/packages/indexer-common/src/transactions.ts b/packages/indexer-common/src/transactions.ts index 2e27e45c1..d120b993c 100644 --- a/packages/indexer-common/src/transactions.ts +++ b/packages/indexer-common/src/transactions.ts @@ -17,6 +17,7 @@ import { toAddress, } from '@graphprotocol/common-ts' import delay from 'delay' +import { TransactionMonitoring } from './network-specification' import { IndexerError, indexerError, IndexerErrorCode } from './errors' import { TransactionConfig, TransactionType } from './types' import { NetworkSubgraph } from './network-subgraph' @@ -27,29 +28,28 @@ export class TransactionManager { wallet: Wallet paused: Eventual isOperator: Eventual - gasIncreaseTimeout: number - gasIncreaseFactor: BigNumber - baseFeePerGasMax: number - maxTransactionAttempts: number + specification: TransactionMonitoring + adjustedGasIncreaseFactor: BigNumber + adjustedBaseFeePerGasMax: number constructor( ethereum: providers.BaseProvider, wallet: Wallet, paused: Eventual, isOperator: Eventual, - gasIncreaseTimeout: number, - gasIncreaseFactor: number, - baseFeePerGasMax: number, - maxTransactionAttempts: number, + specification: TransactionMonitoring, ) { this.ethereum = ethereum this.wallet = wallet this.paused = paused this.isOperator = isOperator - this.gasIncreaseTimeout = gasIncreaseTimeout - this.gasIncreaseFactor = utils.parseUnits(gasIncreaseFactor.toString(), 3) - this.baseFeePerGasMax = baseFeePerGasMax - this.maxTransactionAttempts = maxTransactionAttempts + this.specification = specification + this.adjustedGasIncreaseFactor = utils.parseUnits( + specification.gasIncreaseFactor.toString(), + 3, + ) + this.adjustedBaseFeePerGasMax = + specification.baseFeePerGasMax || specification.gasPriceMax } async executeTransaction( @@ -80,7 +80,7 @@ export class TransactionManager { let txConfig: TransactionConfig = { attempt: 1, type: await this.transactionType(feeData), - gasBump: this.gasIncreaseFactor, + gasBump: this.adjustedGasIncreaseFactor, nonce: tx.nonce, maxFeePerGas: tx.maxFeePerGas, maxPriorityFeePerGas: tx.maxPriorityFeePerGas, @@ -92,8 +92,8 @@ export class TransactionManager { while (pending) { if ( - this.maxTransactionAttempts !== 0 && - txConfig.attempt > this.maxTransactionAttempts + this.specification.maxTransactionAttempts !== 0 && + txConfig.attempt > this.specification.maxTransactionAttempts ) { logger.warn('Transaction retry limit reached, giving up', { txConfig, @@ -127,7 +127,7 @@ export class TransactionManager { const receipt = await this.ethereum.waitForTransaction( tx.hash, 3, - this.gasIncreaseTimeout, + this.specification.gasIncreaseTimeout, ) if (receipt.status == 0) { @@ -203,7 +203,7 @@ export class TransactionManager { // This case typically indicates a successful transaction being retried. // Let's introduce a 30 second delay to ensure the previous transaction has // a chance to be mined and return to the reconciliation loop so the agent can reevaluate. - delay(30000) + await delay(30000) throw indexerError( IndexerErrorCode.IE058, `Original transaction was not confirmed though it may have been successful`, @@ -270,15 +270,15 @@ export class TransactionManager { .maxFeePerGas! // eslint-disable-next-line @typescript-eslint/no-non-null-assertion .sub(feeData.maxPriorityFeePerGas!) .div(2) - if (baseFeePerGas.toNumber() >= this.baseFeePerGasMax) { + if (baseFeePerGas.toNumber() >= this.adjustedBaseFeePerGasMax) { if (attempt == 1) { logger.warning( `Max base fee per gas has been reached, waiting until the base fee falls below to resume transaction execution.`, - { maxBaseFeePerGas: this.baseFeePerGasMax, baseFeePerGas }, + { maxBaseFeePerGas: this.specification.baseFeePerGasMax, baseFeePerGas }, ) } else { logger.info(`Base gas fee per gas estimation still above max threshold`, { - maxBaseFeePerGas: this.baseFeePerGasMax, + maxBaseFeePerGas: this.specification.baseFeePerGasMax, baseFeePerGas, priceEstimateAttempt: attempt, }) @@ -292,18 +292,18 @@ export class TransactionManager { } else if (type === TransactionType.ZERO) { // Legacy transaction type // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (feeData.gasPrice!.toNumber() >= this.baseFeePerGasMax) { + if (feeData.gasPrice!.toNumber() >= this.adjustedBaseFeePerGasMax) { if (attempt == 1) { logger.warning( `Max gas price has been reached, waiting until gas price estimates fall below to resume transaction execution.`, { - baseFeePerGasMax: this.baseFeePerGasMax, + baseFeePerGasMax: this.specification.baseFeePerGasMax, currentGasPriceEstimate: feeData.gasPrice, }, ) } else { logger.info(`Gas price estimation still above max threshold`, { - baseFeePerGasMax: this.baseFeePerGasMax, + baseFeePerGasMax: this.specification.baseFeePerGasMax, currentGasPriceEstimate: feeData.gasPrice, priceEstimateAttempt: attempt, }) @@ -324,35 +324,39 @@ export class TransactionManager { networkSubgraph: NetworkSubgraph, ): Promise> { return timer(60_000) - .reduce(async (currentlyPaused) => { - try { - const result = await networkSubgraph.query( - gql` + .reduce( + async (currentlyPaused) => { + try { + const result = await networkSubgraph.checkedQuery(gql` { graphNetworks { isPaused } } - `, - ) + `) - if (result.error) { - throw result.error - } + if (result.error) { + throw result.error + } - if (!result.data || result.data.length === 0) { - throw new Error(`No data returned by network subgraph`) - } + if (!result.data || result.data.length === 0) { + throw new Error(`No data returned by network subgraph`) + } - return result.data.graphNetworks[0].isPaused - } catch (err) { - logger.warn(`Failed to check for network pause, assuming it has not changed`, { - err: indexerError(IndexerErrorCode.IE007, err), - paused: currentlyPaused, - }) - return currentlyPaused - } - }, await contracts.controller.paused()) + return result.data.graphNetworks[0].isPaused + } catch (err) { + logger.warn( + `Failed to check for network pause, assuming it has not changed`, + { + err: indexerError(IndexerErrorCode.IE007, err), + paused: currentlyPaused, + }, + ) + return currentlyPaused + } + }, + await contracts.controller.paused(), + ) .map((paused) => { logger.info(paused ? `Network paused` : `Network active`) return paused @@ -373,17 +377,20 @@ export class TransactionManager { } return timer(60_000) - .reduce(async (isOperator) => { - try { - return await contracts.staking.isOperator(wallet.address, indexerAddress) - } catch (err) { - logger.warn( - `Failed to check operator status for indexer, assuming it has not changed`, - { err: indexerError(IndexerErrorCode.IE008, err), isOperator }, - ) - return isOperator - } - }, await contracts.staking.isOperator(wallet.address, indexerAddress)) + .reduce( + async (isOperator) => { + try { + return await contracts.staking.isOperator(wallet.address, indexerAddress) + } catch (err) { + logger.warn( + `Failed to check operator status for indexer, assuming it has not changed`, + { err: indexerError(IndexerErrorCode.IE008, err), isOperator }, + ) + return isOperator + } + }, + await contracts.staking.isOperator(wallet.address, indexerAddress), + ) .map((isOperator) => { logger.info( isOperator @@ -393,4 +400,48 @@ export class TransactionManager { return isOperator }) } + + findEvent( + eventType: string, + contractInterface: utils.Interface, + logKey: string, + logValue: string, + receipt: ContractReceipt, + logger: Logger, + ): utils.Result | undefined { + const events: Event[] | providers.Log[] = receipt.events || receipt.logs + const decodedEvents: utils.Result[] = [] + const expectedTopic = contractInterface.getEventTopic(eventType) + + const result = events + .filter((event) => event.topics.includes(expectedTopic)) + .map((event) => { + const decoded = contractInterface.decodeEventLog( + eventType, + event.data, + event.topics, + ) + decodedEvents.push(decoded) + return decoded + }) + .find( + (eventLogs) => + eventLogs[logKey].toLocaleLowerCase() === logValue.toLocaleLowerCase(), + ) + + logger.trace('Searched for event logs', { + function: 'findEvent', + expectedTopic, + events, + decodedEvents, + eventType, + logKey, + logValue, + receipt, + found: !!result, + result, + }) + + return result + } } diff --git a/packages/indexer-common/src/types.ts b/packages/indexer-common/src/types.ts index cc28c0587..cfd62747a 100644 --- a/packages/indexer-common/src/types.ts +++ b/packages/indexer-common/src/types.ts @@ -1,4 +1,4 @@ -import { Address, SubgraphDeploymentID } from '@graphprotocol/common-ts' +import { SubgraphDeploymentID } from '@graphprotocol/common-ts' import { BigNumber, providers } from 'ethers' export enum AllocationManagementMode { @@ -7,6 +7,11 @@ export enum AllocationManagementMode { OVERSIGHT = 'oversight', } +export enum DeploymentManagementMode { + AUTO = 'auto', + MANUAL = 'manual', +} + export enum OrderDirection { ASC = 'asc', DESC = 'desc', @@ -58,58 +63,26 @@ export interface SubgraphDeployment { stakedTokens: BigNumber signalledTokens: BigNumber queryFeesAmount: BigNumber - activeAllocations: number - name?: string - creatorAddress?: Address -} - -export function formatDeploymentName(subgraphDeployment: SubgraphDeployment): string { - const creatorAddress = subgraphDeployment.creatorAddress || 'unknownCreator' - const cleanedName = cleanDeploymentName(subgraphDeployment.name) - return `${cleanedName}/${subgraphDeployment.id.ipfsHash}/${creatorAddress}` + protocolNetwork: string } -export function cleanDeploymentName(subgraphName: undefined | string): string { - const unknownSubgraph = 'unknownSubgraph' - - if (!subgraphName) { - return unknownSubgraph - } - - /* Strip everything out of the string except for ASCII alphanumeric characters, dashes and - underscores. - - We must also limit the name size, as Graph Node enforces a maximum deployment name lenght of 255 - characters. Considering the other parts of the deployment name have fixed sizes (see table - below), we must limit the subgraph name to 165 characters. - - ------------------+----- - Subgraph Name | 165 <--- Size Limit - Slash | 1 - IPFS Qm-Hash | 46 - Slash | 1 - Owner Address | 42 - ------------------+----- - Total Characters | 255 - ------------------+----- */ - let cleaned = subgraphName.replace(/[^\w\d_-]+/g, '').slice(0, 165) - - // 1. Should not start or end with a special character. - const first = cleaned.match(/^[-_]/) ? 1 : undefined - const last = cleaned.slice(-1).match(/[-_]$/) ? cleaned.length - 1 : undefined - cleaned = cleaned.slice(first, last) - - // 2. Must be non-empty. - if (cleaned === '') { - return unknownSubgraph - } - - // 3. To keep URLs unambiguous, reserve the token "graphql". - if (cleaned == 'graphql') { - return 'graphql-subgraph' - } - - return cleaned +// L1 Network Subgraph will always return `null` for the +// `transferredToL2*` set of fields +export interface TransferredSubgraphDeployment { + id: string + idOnL1: string + idOnL2: string + startedTransferToL2L: boolean + startedTransferToL2At: BigNumber + startedTransferToL2AtBlockNumber: BigNumber + startedTransferToL2AtTx: string + transferredToL2: boolean | null + transferredToL2At: BigNumber | null + transferredToL2AtTx: string | null + transferredToL2AtBlockNumber: BigNumber | null + ipfsHash: string + protocolNetwork: string + ready: boolean | null } export enum TransactionType { @@ -122,3 +95,14 @@ export interface TransactionConfig extends providers.TransactionRequest { gasBump: BigNumber type: TransactionType } + +export function parseDeploymentManagementMode(input: string): DeploymentManagementMode { + switch (input) { + case DeploymentManagementMode.AUTO: + return DeploymentManagementMode.AUTO + case DeploymentManagementMode.MANUAL: + return DeploymentManagementMode.MANUAL + default: + throw new Error(`Invalid value for deployment management mode: ${input}`) + } +} diff --git a/packages/indexer-common/src/utils.ts b/packages/indexer-common/src/utils.ts index 5efa58b52..a486424a0 100644 --- a/packages/indexer-common/src/utils.ts +++ b/packages/indexer-common/src/utils.ts @@ -1,8 +1,13 @@ +import { Wallet, utils } from 'ethers' import { BaseProvider, JsonRpcProvider, getDefaultProvider, } from '@ethersproject/providers' +import { Logger, Metrics, timer } from '@graphprotocol/common-ts' +import { indexerError, IndexerErrorCode } from './errors' +import { DocumentNode, SelectionSetNode, Kind } from 'graphql' +import cloneDeep from 'lodash.clonedeep' export const parseBoolean = ( val: string | boolean | number | undefined | null, @@ -23,3 +28,94 @@ export function getTestProvider(network: string): BaseProvider { return getDefaultProvider(network) } } + +const registerMetrics = (metrics: Metrics, networkIdentifier: string) => ({ + operatorEthBalance: new metrics.client.Gauge({ + name: `indexer_agent_operator_eth_balance_${networkIdentifier}`, + help: 'Amount of ETH in the operator wallet; a low amount could cause transactions to fail', + registers: [metrics.registry], + }), +}) + +export async function monitorEthBalance( + logger: Logger, + wallet: Wallet, + metrics: Metrics, + networkIdentifier: string, +): Promise { + logger = logger.child({ component: 'ETHBalanceMonitor' }) + + logger.info('Monitor operator ETH balance (refreshes every 120s)') + + const balanceMetrics = registerMetrics(metrics, networkIdentifier) + + timer(120_000).pipe(async () => { + try { + const balance = await wallet.getBalance() + const eth = parseFloat(utils.formatEther(balance)) + balanceMetrics.operatorEthBalance.set(eth) + logger.info('Current operator ETH balance', { + balance: eth, + }) + } catch (error) { + logger.warn(`Failed to check latest ETH balance`, { + err: indexerError(IndexerErrorCode.IE059), + }) + } + }) +} + +export function mergeSelectionSets( + first: DocumentNode, + second: DocumentNode, +): DocumentNode { + // Work on a copy to avoid mutating inupt + const copy = cloneDeep(first) + const firstSelectionSet = extractSelectionSet(copy) + const secondSelectionSet = extractSelectionSet(second) + firstSelectionSet.selections = [ + ...firstSelectionSet.selections, + ...secondSelectionSet.selections, + ] + return copy +} + +function extractSelectionSet(document: DocumentNode): SelectionSetNode { + // Ensure that the document contains at least one definition + if (document.definitions.length === 0) { + throw new Error('Document must contain at least one definition') + } + // Find the first SelectionSet in the document + const firstDefinition = document.definitions[0] + if (!firstDefinition || firstDefinition.kind !== Kind.OPERATION_DEFINITION) { + throw new Error('Invalid document definition') + } + const selectionSet = findFirstSelectionSet(firstDefinition.selectionSet) + if (!selectionSet) { + throw new Error('No SelectionSet found in the document') + } + if (!selectionSet.selections) { + throw new Error('SelectionSet has no selections') + } + + return selectionSet +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function findFirstSelectionSet(node: any): SelectionSetNode | null { + if (node.kind === Kind.SELECTION_SET) { + return node + } + for (const key of Object.keys(node)) { + const childNode = node[key] + if (childNode && typeof childNode === 'object') { + const result = findFirstSelectionSet(childNode) + if (result !== null) { + return result + } + } + } + return null +} + +export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) diff --git a/packages/indexer-common/tsconfig.json b/packages/indexer-common/tsconfig.json index 1a9b5ce39..7777d9321 100644 --- a/packages/indexer-common/tsconfig.json +++ b/packages/indexer-common/tsconfig.json @@ -16,9 +16,10 @@ "es2020.promise", "es2015.iterable", "es2015.promise" - ] + ], + "resolveJsonModule": true }, "include": ["src/**/*.ts"], - "exclude": ["src/**/__tests__/*.ts"], + "exclude": [], "references": [] } diff --git a/packages/indexer-native/lib/index.js b/packages/indexer-native/lib/index.js index 468859773..5c00fb96c 100644 --- a/packages/indexer-native/lib/index.js +++ b/packages/indexer-native/lib/index.js @@ -8,7 +8,7 @@ function promisify(f) { } else { resolve(result); } - }) + }), ); } @@ -28,18 +28,18 @@ class NativeAttestationSigner { chainId, disputeManagerAddress, privateKey, - subgraphDeploymentId + subgraphDeploymentId, ) { this._native = new addon.NativeAttestationSigner( chainId, disputeManagerAddress, privateKey, - subgraphDeploymentId + subgraphDeploymentId, ); } async createAttestation(request, response) { return await promisify((cb) => - this._native.createAttestation(cb, request, response) + this._native.createAttestation(cb, request, response), ); } } diff --git a/packages/indexer-native/lib/index.test.js b/packages/indexer-native/lib/index.test.js index 95be71ae2..ce514ca88 100644 --- a/packages/indexer-native/lib/index.test.js +++ b/packages/indexer-native/lib/index.test.js @@ -50,7 +50,7 @@ describe("Native Functions", () => { "coyote tattoo slush ball cluster culture bleak news when action cover effort"; const subgraphDeploymentID = utils.hexlify( - bs58.decode("QmTXzATwNfgGVukV1fX2T6xw9f6LAYRVWpsdXyRWzUR2H9").slice(2) + bs58.decode("QmTXzATwNfgGVukV1fX2T6xw9f6LAYRVWpsdXyRWzUR2H9").slice(2), ); const privateKey = Wallet.fromMnemonic(mnemonic).privateKey; @@ -61,7 +61,7 @@ describe("Native Functions", () => { chainId, disputeManagerAddress, privateKey, - subgraphDeploymentID + subgraphDeploymentID, ); const expected = { @@ -76,7 +76,7 @@ describe("Native Functions", () => { s: "0x7b24b529fcf92c9426179146bb7bfed6540043e2c30132e59d994a3cc718f2be", }; await expect( - signer.createAttestation("request", "response") + signer.createAttestation("request", "response"), ).resolves.toEqual(expected); // Ensure throwing errors works at least in one case when a parameter cannot be deserialized @@ -86,8 +86,8 @@ describe("Native Functions", () => { chainId, "0xab", privateKey, - subgraphDeploymentID - ) + subgraphDeploymentID, + ), ).toThrow(); }); }); diff --git a/packages/indexer-native/native/Cargo.lock b/packages/indexer-native/native/Cargo.lock index dc0d562dc..5dd4bfc6b 100644 --- a/packages/indexer-native/native/Cargo.lock +++ b/packages/indexer-native/native/Cargo.lock @@ -4,51 +4,53 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.18" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" dependencies = [ "memchr", ] [[package]] name = "arc-swap" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" -version = "0.5.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "atomic-take" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f65e4fb35ff6a80b3298d1f028649f3a23da141fa3951e9b24dde1d515b67e" +checksum = "a8ab6b55fe97976e46f91ddbed8d147d966475dc29b2032757ba47e02376fbc3" [[package]] name = "base64" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "bitvec" -version = "0.17.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "either", + "funty", "radium", + "tap", + "wyz", ] [[package]] @@ -62,9 +64,9 @@ dependencies = [ [[package]] name = "byte-slice-cast" -version = "0.3.5" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "byteorder" @@ -74,9 +76,12 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" -version = "1.0.72" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -86,18 +91,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clear_on_drop" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9cc5db465b294c3fa986d5bbb0f3017cd850bff6dd6c52f9ccff8b4d21b7b08" +checksum = "38508a63f4979f0048febc9966fadbd48e5dab31fd0ec6a3f151bbf4a74f7423" dependencies = [ "cc", ] [[package]] name = "cpufeatures" -version = "0.2.1" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -118,12 +123,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "cslice" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697c714f50560202b1f4e2e09cd50a421881c83e9025db75d15f276616f04f40" - [[package]] name = "digest" version = "0.9.0" @@ -135,47 +134,41 @@ dependencies = [ [[package]] name = "eip-712-derive" -version = "0.2.0" -source = "git+https://github.com/graphprotocol/eip-712-derive#889f23c37c6a345c142122f7da8bc33ae9011431" +version = "0.4.0" +source = "git+https://github.com/graphprotocol/eip-712-derive#0ce4f89c98d0b56d9d67c16b732425e7f2fd14b3" dependencies = [ "clear_on_drop", - "keccak-hash 0.8.0", + "keccak-hash", "lazy_static", "libsecp256k1", ] [[package]] -name = "either" -version = "1.6.1" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "faster-hex" -version = "0.5.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68bae82fd85645631575599a2ebab9807b6d42000083417ac5dc3d2da2ba023" +checksum = "e9042d281a5eec0f2387f8c3ea6c4514e2cf2732c90a85aaf383b761ee3b290d" +dependencies = [ + "serde", +] [[package]] name = "firestorm" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31586bda1b136406162e381a3185a506cdfc1631708dd40cba2f6628d8634499" - -[[package]] -name = "fixed-hash" -version = "0.6.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11498d382790b7a8f2fd211780bec78619bba81cdad3a283997c0c41f836759c" -dependencies = [ - "static_assertions", -] +checksum = "2c5f6c2c942da57e2aaaa84b8a521489486f14e75e7fa91dab70aba913975f98" [[package]] name = "fixed-hash" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", "rand", @@ -183,11 +176,17 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "generic-array" -version = "0.14.4" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -195,15 +194,21 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", "wasi", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "hex" version = "0.4.3" @@ -233,13 +238,24 @@ dependencies = [ [[package]] name = "impl-codec" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ "parity-scale-codec", ] +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "indexer-native" version = "0.1.0" @@ -247,33 +263,33 @@ dependencies = [ "arc-swap", "eip-712-derive", "hex", - "keccak-hash 0.5.1", + "keccak-hash", "lazy_static", "neon", "neon-build", "neon-utils", "never", - "primitive-types 0.8.0", + "primitive-types", "secp256k1", ] [[package]] -name = "keccak-hash" -version = "0.5.1" +name = "indexmap" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f58a51ef3df9398cf2434bea8d4eb61fb748d0feb1571f87388579a120a4c8f" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ - "primitive-types 0.7.3", - "tiny-keccak", + "equivalent", + "hashbrown", ] [[package]] name = "keccak-hash" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2bd4c29270e724d3eaadf7bdc8700af4221fc0ed771b855eadcd1b98d52851" +checksum = "4b286e6b663fb926e1eeb68528e69cb70ed46c6d65871a21b2215ae8154c6d3c" dependencies = [ - "primitive-types 0.10.1", + "primitive-types", "tiny-keccak", ] @@ -285,15 +301,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.112" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libsecp256k1" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0452aac8bab02242429380e9b2f94ea20cea2b37e2c1777a1358799bbe97f37" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ "arrayref", "base64", @@ -339,17 +355,16 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "neon" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e85820b585bf3360bf158ac87a75764c48e361c91bbeb69873e6613cc78c023" +checksum = "28e15415261d880aed48122e917a45e87bb82cf0260bb6db48bbab44b7464373" dependencies = [ - "cslice", "neon-build", "neon-runtime", "semver", @@ -358,18 +373,18 @@ dependencies = [ [[package]] name = "neon-build" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9febc63f515156d4311a0c43899d3ace46352ecdd591c21b98ca3974f2a0d0" +checksum = "8bac98a702e71804af3dacfde41edde4a16076a7bbe889ae61e56e18c5b1c811" dependencies = [ "neon-sys", ] [[package]] name = "neon-runtime" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02662cd2e62b131937bdef85d0918b05bc3c204daf4c64af62845403eccb60f3" +checksum = "4676720fa8bb32c64c3d9f49c47a47289239ec46b4bdb66d0913cc512cb0daca" dependencies = [ "cfg-if", "neon-sys", @@ -378,9 +393,9 @@ dependencies = [ [[package]] name = "neon-sys" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fd32e08c4681b0004b3dce16a9f647a8023af7b8dde3bb3ac423034803830" +checksum = "a5ebc923308ac557184455b4aaa749470554cbac70eb4daa8b18cdc16bef7df6" dependencies = [ "cc", "regex", @@ -389,7 +404,7 @@ dependencies = [ [[package]] name = "neon-utils" version = "1.1.0" -source = "git+https://github.com/edgeandnode/neon-utils?rev=2507d4f#2507d4f0994d1cdf2377a7d012c05ddde8813ad7" +source = "git+https://github.com/edgeandnode/neon-utils?rev=b757548#b757548c3b36854e7ce7554a815a5d074278f51c" dependencies = [ "atomic-take", "faster-hex", @@ -397,7 +412,7 @@ dependencies = [ "neon", "neon-build", "never", - "primitive-types 0.8.0", + "primitive-types", "rustc-hex", "secp256k1", ] @@ -408,6 +423,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -416,82 +437,86 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-scale-codec" -version = "1.3.7" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b26b16c7687c3075982af47719e481815df30bc544f7a6690763a25ca16e9d" +checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" dependencies = [ "arrayvec", "bitvec", "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", "serde", ] [[package]] -name = "ppv-lite86" -version = "0.2.15" +name = "parity-scale-codec-derive" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" +checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] [[package]] -name = "primitive-types" -version = "0.7.3" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd39dcacf71411ba488570da7bbc89b717225e46478b30ba99b92db6b149809" -dependencies = [ - "fixed-hash 0.6.1", - "uint 0.8.5", -] +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "primitive-types" -version = "0.8.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3824ae2c5e27160113b9e029a10ec9e3f0237bad8029f69c7724393c9fdefd8" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" dependencies = [ - "fixed-hash 0.7.0", + "fixed-hash", "impl-codec", - "uint 0.9.1", + "uint", ] [[package]] -name = "primitive-types" -version = "0.10.1" +name = "proc-macro-crate" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ - "fixed-hash 0.7.0", - "uint 0.9.1", + "once_cell", + "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.34" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.10" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] name = "radium" -version = "0.3.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", @@ -510,18 +535,30 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "regex" -version = "1.5.4" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" dependencies = [ "aho-corasick", "memchr", @@ -530,9 +567,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "rustc-hex" @@ -542,18 +579,18 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "secp256k1" -version = "0.20.3" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" dependencies = [ "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.4.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "827cb7cce42533829c792fc51b82fbf18b125b45a702ef2c8be77fce65463a7b" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" dependencies = [ "cc", ] @@ -575,29 +612,29 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.132" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.132" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.28", ] [[package]] name = "sha2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer", "cfg-if", @@ -608,9 +645,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.7.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "static_assertions" @@ -620,21 +657,38 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] [[package]] name = "syn" -version = "1.0.83" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a1dfb999630e338648c83e91c59a4e9fb7620f520c3194b6b89e276f2f1959" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -645,28 +699,33 @@ dependencies = [ ] [[package]] -name = "typenum" -version = "1.14.0" +name = "toml_datetime" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] -name = "uint" -version = "0.8.5" +name = "toml_edit" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9db035e67dfaf7edd9aebfe8676afcd63eed53c8a4044fed514c8cccf1835177" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ - "byteorder", - "crunchy", - "rustc-hex", - "static_assertions", + "indexmap", + "toml_datetime", + "winnow", ] +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + [[package]] name = "uint" -version = "0.9.1" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", "crunchy", @@ -675,19 +734,37 @@ dependencies = [ ] [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "unicode-ident" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winnow" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "83817bbecf72c73bad717ee86820ebf286203d2e04c3951f3cd538869c897364" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] diff --git a/packages/indexer-native/native/Cargo.toml b/packages/indexer-native/native/Cargo.toml index d37ac31cd..70936064c 100644 --- a/packages/indexer-native/native/Cargo.toml +++ b/packages/indexer-native/native/Cargo.toml @@ -12,16 +12,16 @@ name = "indexer_native" crate-type = ["cdylib"] [build-dependencies] -neon-build = "0.9.1" +neon-build = "0.10.1" [dependencies] -neon = "0.9" -neon-utils = { git = "https://github.com/edgeandnode/neon-utils", rev = "2507d4f" } -secp256k1 = { version = "0.20", features = ["recovery"] } +neon = "0.10" +neon-utils = { git = "https://github.com/edgeandnode/neon-utils", rev = "b757548" } +secp256k1 = { version = "0.27", features = ["recovery"] } never = "0.1" -keccak-hash = "0.5.1" +keccak-hash = "0.10.0" lazy_static = "1.4" arc-swap = "1.2" eip-712-derive = { git = "https://github.com/graphprotocol/eip-712-derive" } hex = "0.4.2" -primitive-types = "0.8" +primitive-types = "0.12.1" diff --git a/packages/indexer-native/native/src/attestation.rs b/packages/indexer-native/native/src/attestation.rs index c61c48d3e..c6af20f15 100644 --- a/packages/indexer-native/native/src/attestation.rs +++ b/packages/indexer-native/native/src/attestation.rs @@ -87,6 +87,7 @@ pub struct Attestation { pub request_cid: Bytes32, pub response_cid: Bytes32, pub subgraph_deployment_id: Bytes32, + // pub query_version: String, pub v: u8, pub r: Bytes32, pub s: Bytes32, @@ -99,6 +100,7 @@ impl IntoHandle for Attestation { requestCID: &self.request_cid, responseCID: &self.response_cid, subgraphDeploymentID: &self.subgraph_deployment_id, + // queryVersion: &self.query_version, v: self.v as u32, r: &self.r, s: &self.s, diff --git a/packages/indexer-native/native/src/lib.rs b/packages/indexer-native/native/src/lib.rs index 760f30ee8..2381a94cf 100644 --- a/packages/indexer-native/native/src/lib.rs +++ b/packages/indexer-native/native/src/lib.rs @@ -9,7 +9,7 @@ use neon_utils::{ task::run_async, }; use never::Never; -use secp256k1::{recovery::RecoverableSignature, Message, PublicKey, Secp256k1, VerifyOnly}; +use secp256k1::{ecdsa::RecoverableSignature, Message, PublicKey, Secp256k1, VerifyOnly}; use std::sync::Arc; mod attestation; mod signature_verification; diff --git a/packages/indexer-native/native/src/signature_verification.rs b/packages/indexer-native/native/src/signature_verification.rs index d3a9eebe8..ce5bd5112 100644 --- a/packages/indexer-native/native/src/signature_verification.rs +++ b/packages/indexer-native/native/src/signature_verification.rs @@ -26,7 +26,7 @@ impl SignatureVerifier { match self.signer.load().as_ref() { // If we already have the public key we can do the fast path. Signer::PublicKey(signer) => Ok(SECP256K1 - .verify(&message, &signature.to_standard(), signer) + .verify_ecdsa(&message, &signature.to_standard(), signer) .is_ok()), // If we don't have the public key, but have the address instead // we derive the address from the recovered key. If it's a match @@ -35,7 +35,7 @@ impl SignatureVerifier { // verify method instead of the slow recover method. Signer::Address(addr) => { let recovered_signer = SECP256K1 - .recover(&message, &signature) + .recover_ecdsa(&message, &signature) .map_err(|_| "Failed to recover signature")?; let ser = recovered_signer.serialize_uncompressed(); diff --git a/packages/indexer-native/package.json b/packages/indexer-native/package.json index 394b9a995..6e5231151 100644 --- a/packages/indexer-native/package.json +++ b/packages/indexer-native/package.json @@ -46,18 +46,18 @@ "clean": "rm -rf ./node_modules ./binary ./build ./coverage ./native/target ./native/artifacts.json ./native/index.node" }, "dependencies": { - "@graphprotocol/common-ts": "2.0.1", + "@graphprotocol/common-ts": "2.0.7", "@mapbox/node-pre-gyp": "1.0.10", "cargo-cp-artifact": "0.1.7", "node-pre-gyp-github": "1.4.4" }, "devDependencies": { "bs58": "5.0.0", - "eslint": "8.13.0", + "eslint": "8.49.0", "eslint-config-prettier": "8.5.0", "ethers": "5.7.0", - "jest": "27.5.1", - "prettier": "2.6.2" + "jest": "<30.0.0-0", + "prettier": "3.0.3" }, "binary": { "module_name": "index", diff --git a/packages/indexer-service/CHANGELOG.md b/packages/indexer-service/CHANGELOG.md index ee907e969..08a24cb65 100644 --- a/packages/indexer-service/CHANGELOG.md +++ b/packages/indexer-service/CHANGELOG.md @@ -6,6 +6,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.20.23] - 2023-09-29 +### Changed +- Relax network subgraph validation during indexer-service startup + +### Fixed +- Update common-ts and other dependencies +- Update common-ts and other dependencies +- Update common-ts and other dependencies + +### Added +- Added a subgraph freshness check for the Network and Epoch subgraphs +- Added a subgraph freshness check for the Network and Epoch subgraphs + +## [0.20.21] - 2023-08-24 +### Changed +- Upgraded `common-ts` dependency to v2.0.3 + +## [0.20.20] - 2023-08-21 +### Added +- Add TRACE logs to `AllocationReceiptManager._flushOutstanding` method + +### Fixed +- Ensure some database operations run inside their transactions + +## [0.20.19] - 2023-08-12 +### Changed +- Revert indexer-native to 0.20.11 (newer builds are broken) + +## [0.20.18] - 2023-08-11 +### Changed +- Changes to support the multi-network changes from indexer-common + +## [0.20.17] - 2023-06-19 +### Changed +- Use new partial-vouchers encoding, json + ## [0.20.11] - 2023-02-01 ### Fixed - Include yaml package as dependency @@ -282,7 +318,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Update @graphprotocol/common-ts to 0.2.2 -[Unreleased]: https://github.com/graphprotocol/indexer/compare/v0.20.11...HEAD +[Unreleased]: https://github.com/graphprotocol/indexer/compare/v0.20.23-rc.0...HEAD +[0.20.23]: https://github.com/graphprotocol/indexer/compare/v0.20.21...v0.20.23 +[0.20.21]: https://github.com/graphprotocol/indexer/compare/v0.20.20...v0.20.21 +[0.20.20]: https://github.com/graphprotocol/indexer/compare/v0.20.19...v0.20.20 +[0.20.19]: https://github.com/graphprotocol/indexer/compare/v0.20.18...v0.20.19 +[0.20.18]: https://github.com/graphprotocol/indexer/compare/v0.20.17...v0.20.18 +[0.20.17]: https://github.com/graphprotocol/indexer/compare/v0.20.11...v0.20.17 [0.20.11]: https://github.com/graphprotocol/indexer/compare/v0.20.9...v0.20.11 [0.20.9]: https://github.com/graphprotocol/indexer/compare/v0.20.6...v0.20.9 [0.20.6]: https://github.com/graphprotocol/indexer/compare/v0.20.4...v0.20.6 diff --git a/packages/indexer-service/README.md b/packages/indexer-service/README.md index f20b92743..0bef1ca02 100644 --- a/packages/indexer-service/README.md +++ b/packages/indexer-service/README.md @@ -8,13 +8,13 @@ Start the service Ethereum - --ethereum Ethereum node or provider URL [string] [required] - --ethereum-network Ethereum network [string] [default: "rinkeby"] - --ethereum-polling-interval Polling interval for the Ethereum provider (ms) - [number] [default: 4000] - --mnemonic Mnemonic for the operator wallet + --network-provider, --ethereum Ethereum node or provider URL [string] [required] - --indexer-address Ethereum address of the indexer + --ethereum-polling-interval Polling interval for the Ethereum provider + (ms) [number] [default: 4000] + --mnemonic Mnemonic for the operator wallet + [string] [required] + --indexer-address Ethereum address of the indexer [string] [required] Indexer Infrastructure @@ -26,6 +26,8 @@ Indexer Infrastructure --graph-node-status-endpoint Graph Node endpoint for indexing statuses etc. [string] [required] --log-level Log level [string] [default: "debug"] + --query-timing-logs Log time spent on each query received + [boolean] [default: false] Postgres --postgres-host Postgres host [string] [required] @@ -35,20 +37,26 @@ Postgres --postgres-database Postgres database name [string] [required] Network Subgraph - --network-subgraph-endpoint Endpoint to query the network subgraph from + --network-subgraph-deployment Network subgraph deployment [string] + --network-subgraph-endpoint Endpoint to query the network subgraph from [string] [required] - -State Channels - --wallet-worker-threads Number of worker threads for the server wallet - [number] [default: 8] - --wallet-skip-evm-validation Whether to skip EVM-based validation of state - channel transitions [boolean] [default: true] + --network-subgraph-auth-token Bearer token to require for /network queries + [string] + --serve-network-subgraph Whether to serve the network subgraph at + /network [boolean] [default: false] + --allocation-syncing-interval Interval (in ms) for syncing indexer + allocations from the network + [number] [default: 120000] Options: --version Show version number [boolean] --help Show help [boolean] + --gcloud-profiling Whether to enable Google Cloud profiling + [boolean] [default: false] --free-query-auth-token Auth token that clients can use to query for free [array] + --client-signer-address Address that signs query fee receipts from a known + client [string] ``` # Copyright diff --git a/packages/indexer-service/jest.config.js b/packages/indexer-service/jest.config.js index ca2c1bf76..623c9340f 100644 --- a/packages/indexer-service/jest.config.js +++ b/packages/indexer-service/jest.config.js @@ -1,29 +1,30 @@ -const bail = (s) => { +const bail = s => { throw new Error(s) } module.exports = { collectCoverage: true, + forceExit: true, preset: 'ts-jest', testEnvironment: 'node', testPathIgnorePatterns: ['/node_modules/', '/dist/', '.yalc'], globals: { - "ts-jest": { + 'ts-jest': { isolatedModules: true, }, __DATABASE__: { host: process.env.POSTGRES_TEST_HOST || bail('POSTGRES_TEST_HOST is not defined'), port: parseInt(process.env.POSTGRES_TEST_PORT || '5432'), username: - process.env.POSTGRES_TEST_USERNAME || - bail('POSTGRES_TEST_USERNAME is not defined'), + process.env.POSTGRES_TEST_USERNAME || + bail('POSTGRES_TEST_USERNAME is not defined'), password: - process.env.POSTGRES_TEST_PASSWORD || - bail('POSTGRES_TEST_PASSWORD is not defined'), + process.env.POSTGRES_TEST_PASSWORD || + bail('POSTGRES_TEST_PASSWORD is not defined'), database: - process.env.POSTGRES_TEST_DATABASE || - bail('POSTGRES_TEST_DATABASE is not defined'), + process.env.POSTGRES_TEST_DATABASE || + bail('POSTGRES_TEST_DATABASE is not defined'), }, - __LOG_LEVEL__: process.env.LOG_LEVEL || 'info' + __LOG_LEVEL__: process.env.LOG_LEVEL || 'info', }, } diff --git a/packages/indexer-service/package.json b/packages/indexer-service/package.json index 0676ebc3c..17a379e9d 100644 --- a/packages/indexer-service/package.json +++ b/packages/indexer-service/package.json @@ -1,6 +1,6 @@ { "name": "@graphprotocol/indexer-service", - "version": "0.20.14", + "version": "0.20.23", "description": "Indexer service", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -14,11 +14,11 @@ "scripts": { "format": "prettier --write 'src/**/*.ts'", "lint": "eslint . --ext .ts,.tsx --fix", - "compile": "tsc", + "compile": "tsc --build", "prepare": "yarn format && yarn lint && yarn compile", "start": "node ./dist/index.js start", - "test": "jest --colors --verbose --forceExit", - "test:ci": "jest --verbose --forceExit --ci", + "test": "jest --colors --verbose --detectOpenHandles", + "test:ci": "jest --verbose --ci", "test:watch": "jest --watch --passWithNoTests --detectOpenHandles --verbose", "clean": "rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo" }, @@ -28,30 +28,30 @@ "resolutions": { "ethers": "5.7.0", "@ethersproject/bignumber": "5.7.0", - "graphql": "16.3.0" + "graphql": "16.8.0" }, "dependencies": { "@google-cloud/profiler": "4.1.7", - "@graphprotocol/common-ts": "2.0.1", - "@graphprotocol/indexer-common": "^0.20.14", - "@graphprotocol/indexer-native": "^0.20.11", - "@graphql-tools/load": "7.5.8", - "@graphql-tools/url-loader": "7.9.11", - "@graphql-tools/wrap": "8.4.13", + "@graphprotocol/common-ts": "2.0.7", + "@graphprotocol/indexer-common": "^0.20.23", + "@graphprotocol/indexer-native": "0.20.11", + "@graphql-tools/load": "8.0.0", + "@graphql-tools/url-loader": "8.0.0", + "@graphql-tools/wrap": "10.0.1", "@thi.ng/cache": "1.0.94", "@urql/core": "2.4.4", "apollo-link-http": "1.5.17", "axios": "0.26.1", - "body-parser": "1.19.1", + "body-parser": "1.20.2", "bs58": "5.0.0", "cors": "2.8.5", - "cross-fetch": "3.1.5", + "cross-fetch": "4.0.0", "ethers": "5.7.0", "evt": "1.10.1", - "express": "4.17.3", + "express": "4.18.2", "express-graphql": "0.12.0", - "express-rate-limit": "^5.5.1", - "graphql": "16.3.0", + "express-rate-limit": "^7.0.1", + "graphql": "16.8.0", "graphql-tag": "2.12.6", "graphql-tools": "8.2.6", "isomorphic-fetch": "3.0.0", @@ -65,26 +65,25 @@ "yargs": "17.4.1" }, "devDependencies": { - "@types/body-parser": "1.19.0", + "@types/body-parser": "1.19.3", "@types/bs58": "4.0.1", - "@types/cors": "2.8.12", - "@types/express": "4.17.8", - "@types/express-rate-limit": "^5.1.3", + "@types/cors": "2.8.14", + "@types/express": "4.17.17", "@types/isomorphic-fetch": "0.0.35", - "@types/jest": "27.4.1", - "@types/morgan": "1.9.2", + "@types/jest": "29.5.4", + "@types/morgan": "1.9.5", "@types/supertest": "2.0.12", "@types/yargs": "17.0.10", - "@typescript-eslint/eslint-plugin": "5.19.0", - "@typescript-eslint/parser": "5.19.0", - "eslint": "8.13.0", + "@typescript-eslint/eslint-plugin": "6.7.0", + "@typescript-eslint/parser": "6.7.0", + "eslint": "8.49.0", "eslint-config-prettier": "8.5.0", - "jest": "27.5.1", - "nock": "13.2.4", - "prettier": "2.6.2", - "supertest": "6.2.2", - "ts-jest": "27.1.4", - "typescript": "4.6.3" + "jest": "<30.0.0-0", + "nock": "13.3.3", + "prettier": "3.0.3", + "supertest": "6.3.3", + "ts-jest": "29.1.1", + "typescript": "5.2.2" }, "gitHead": "972ab96774007b2aee15b1da169d2ff4be9f9d27" } diff --git a/packages/indexer-service/src/allocations.ts b/packages/indexer-service/src/allocations.ts index 4e6d6e2fc..2e1a48c4d 100644 --- a/packages/indexer-service/src/allocations.ts +++ b/packages/indexer-service/src/allocations.ts @@ -20,12 +20,14 @@ export interface MonitorEligibleAllocationsOptions { logger: Logger networkSubgraph: NetworkSubgraph interval: number + protocolNetwork: string } export const monitorEligibleAllocations = ({ indexer, logger: parentLogger, networkSubgraph, + protocolNetwork, interval, }: MonitorEligibleAllocationsOptions): Eventual => { const logger = parentLogger.child({ component: 'AllocationMonitor' }) @@ -36,14 +38,15 @@ export const monitorEligibleAllocations = ({ logger.debug('Refresh eligible allocations') try { - const currentEpochResult = await networkSubgraph.query( + const currentEpochResult = await networkSubgraph.checkedQuery( gql` query { graphNetwork(id: "1") { currentEpoch } } - `, + } + `, ) if (currentEpochResult.error) { throw currentEpochResult.error @@ -59,7 +62,7 @@ export const monitorEligibleAllocations = ({ const currentEpoch = currentEpochResult.data.graphNetwork.currentEpoch - const result = await networkSubgraph.query( + const result = await networkSubgraph.checkedQuery( gql` query allocations($indexer: String!, $closedAtEpochThreshold: Int!) { indexer(id: $indexer) { @@ -128,7 +131,7 @@ export const monitorEligibleAllocations = ({ return [ ...result.data.indexer.activeAllocations, ...result.data.indexer.recentlyClosedAllocations, - ].map(parseGraphQLAllocation) + ].map(x => parseGraphQLAllocation(x, protocolNetwork)) } catch (err) { logger.warn(`Failed to query indexer allocations, keeping existing`, { allocations: currentAllocations.map(allocation => allocation.id), diff --git a/packages/indexer-service/src/commands/start.ts b/packages/indexer-service/src/commands/start.ts index c9dc07717..37b373df4 100644 --- a/packages/indexer-service/src/commands/start.ts +++ b/packages/indexer-service/src/commands/start.ts @@ -5,6 +5,11 @@ import { BigNumber, Wallet } from 'ethers' import fs from 'fs' import { parse as yaml_parse } from 'yaml' +const DEFAULT_SUBGRAPH_MAX_BLOCK_DISTANCE = 0 +const SUGGESTED_SUBGRAPH_MAX_BLOCK_DISTANCE_ON_L2 = + 50 + DEFAULT_SUBGRAPH_MAX_BLOCK_DISTANCE +const DEFAULT_SUBGRAPH_FRESHNESS_SLEEP_MILLISECONDS = 5_000 + import { connectContracts, connectDatabase, @@ -20,10 +25,13 @@ import { defineQueryFeeModels, indexerError, IndexerErrorCode, - IndexingStatusResolver, + GraphNode, Network, NetworkSubgraph, registerIndexerErrorMetrics, + resolveChainId, + validateProviderNetworkIdentifier, + SubgraphFreshnessChecker, } from '@graphprotocol/indexer-common' import { createServer } from '../server' @@ -36,7 +44,8 @@ export default { describe: 'Start the service', builder: (yargs: Argv): Argv => { return yargs - .option('ethereum', { + .option('network-provider', { + alias: 'ethereum', description: 'Ethereum node or provider URL', type: 'string', required: true, @@ -173,10 +182,24 @@ export default { type: 'string', required: false, }) + .option('subgraph-max-block-distance', { + description: 'How many blocks subgraphs are allowed to stay behind chain head', + type: 'number', + default: DEFAULT_SUBGRAPH_MAX_BLOCK_DISTANCE, + group: 'Protocol', + }) + .option('subgraph-freshness-sleep-milliseconds', { + description: 'How long to wait before retrying subgraph query if it is not fresh', + type: 'number', + default: DEFAULT_SUBGRAPH_FRESHNESS_SLEEP_MILLISECONDS, + group: 'Protocol', + }) + .check(argv => { if (!argv['network-subgraph-endpoint'] && !argv['network-subgraph-deployment']) { return `At least one of --network-subgraph-endpoint and --network-subgraph-deployment must be provided` } + return true }) .config({ @@ -273,39 +296,104 @@ export default { logger.info('Successfully connected to database') logger.info(`Connect to network subgraph`) - const indexingStatusResolver = new IndexingStatusResolver({ + const graphNode = new GraphNode( logger, - statusEndpoint: argv.graphNodeStatusEndpoint, - }) + // We use a fake Graph Node admin endpoint here because we don't + // want the Indexer Service to perform management actions on + // Graph Node. + 'http://fake-graph-node-admin-endpoint', + argv.graphNodeQueryEndpoint, + argv.graphNodeStatusEndpoint, + argv.indexNodeIds, + ) + + const networkProvider = await Network.provider( + logger, + metrics, + '_', + argv.networkProvider, + argv.ethereumPollingInterval, + ) + const networkIdentifier = await networkProvider.getNetwork() + const protocolNetwork = resolveChainId(networkIdentifier.chainId) + + // Warn about inappropriate max block distance for subgraph threshold checks for given networks. + if (protocolNetwork.startsWith('eip155:42161')) { + // Arbitrum-One and Arbitrum-Goerli + if (argv.subgraphMaxBlockDistance <= SUGGESTED_SUBGRAPH_MAX_BLOCK_DISTANCE_ON_L2) { + logger.warn( + `Consider increasing 'subgraph-max-block-distance' for Arbitrum networks`, + { + problem: + 'A low subgraph freshness threshold might cause the Agent to discard too many subgraph queries in fast-paced networks.', + hint: `Increase the 'subgraph-max-block-distance' parameter to a value that accomodates for block and indexing speeds.`, + configuredValue: argv.subgraphMaxBlockDistance, + }, + ) + } + if ( + argv.subgraphFreshnessSleepMilliseconds <= + DEFAULT_SUBGRAPH_FRESHNESS_SLEEP_MILLISECONDS + ) { + logger.warn( + `Consider increasing 'subgraph-freshness-sleep-milliseconds' for Arbitrum networks`, + { + problem: + 'A short subgraph freshness wait time might be insufficient for the subgraph to sync with fast-paced networks.', + hint: `Increase the 'subgraph-freshness-sleep-milliseconds' parameter to a value that accomodates for block and indexing speeds.`, + configuredValue: argv.subgraphFreshnessSleepMilliseconds, + }, + ) + } + } + + const subgraphFreshnessChecker = new SubgraphFreshnessChecker( + 'Network Subgraph', + networkProvider, + argv.subgraphMaxBlockDistance, + argv.subgraphFreshnessSleepMilliseconds, + logger.child({ component: 'FreshnessChecker' }), + Infinity, + ) + const networkSubgraph = await NetworkSubgraph.create({ logger, endpoint: argv.networkSubgraphEndpoint, deployment: argv.networkSubgraphDeployment ? { - indexingStatusResolver, + graphNode, deployment: new SubgraphDeploymentID(argv.networkSubgraphDeployment), - graphNodeQueryEndpoint: argv.graphNodeQueryEndpoint, } : undefined, + subgraphFreshnessChecker, }) logger.info(`Successfully connected to network subgraph`) - const networkProvider = await Network.provider( - logger, - metrics, - argv.ethereum, - argv.ethereumPollingInterval, - ) - const network = await networkProvider.getNetwork() + // If the network subgraph deployment is present, validate if the `chainId` we get from our + // provider is consistent. + if (argv.networkSubgraphDeployment) { + try { + await validateProviderNetworkIdentifier( + protocolNetwork, + argv.networkSubgraphDeployment, + graphNode, + logger, + ) + } catch (e) { + logger.warn( + 'Failed to validate Network Subgraph on index-nodes. Will use external subgraph endpoint instead', + ) + } + } logger.info('Connect to contracts', { - network: network.name, - chainId: network.chainId, + network: networkIdentifier.name, + chainId: networkIdentifier.chainId, }) let contracts = undefined try { - contracts = await connectContracts(networkProvider, network.chainId) + contracts = await connectContracts(networkProvider, networkIdentifier.chainId) } catch (error) { logger.error( `Failed to connect to contracts, please ensure you are using the intended Ethereum Network`, @@ -332,6 +420,7 @@ export default { queryFeeModels, logger, toAddress(argv.clientSignerAddress), + protocolNetwork, ) // Ensure the address is checksummed @@ -347,6 +436,7 @@ export default { indexer: indexerAddress, logger, networkSubgraph, + protocolNetwork, interval: argv.allocationSyncingInterval, }) @@ -355,7 +445,7 @@ export default { logger, allocations, wallet, - chainId: network.chainId, + chainId: networkIdentifier.chainId, disputeManagerAddress: contracts.disputeManager.address, }) @@ -371,12 +461,8 @@ export default { const indexerManagementClient = await createIndexerManagementClient({ models, - address, - contracts, - indexingStatusResolver, + graphNode, indexNodeIDs: ['node_1'], // This is just a dummy since the indexer-service doesn't manage deployments, - deploymentManagementEndpoint: argv.graphNodeStatusEndpoint, - networkSubgraph, logger, defaults: { // This is just a dummy, since we're never writing to the management @@ -385,9 +471,7 @@ export default { allocationAmount: BigNumber.from('0'), }, }, - features: { - injectDai: true, - }, + multiNetworks: undefined, }) // Spin up a basic webserver diff --git a/packages/indexer-service/src/query-fees/allocations.ts b/packages/indexer-service/src/query-fees/allocations.ts index e86fc7079..11d1ecbcb 100644 --- a/packages/indexer-service/src/query-fees/allocations.ts +++ b/packages/indexer-service/src/query-fees/allocations.ts @@ -41,18 +41,25 @@ export class AllocationReceiptManager implements ReceiptManager { private readonly _cache: Map> = new Map() private readonly _flushQueue: string[] = [] private readonly _allocationReceiptVerifier: NativeSignatureVerifier + private readonly protocolNetwork: string + logger: Logger constructor( sequelize: Sequelize, queryFeeModels: QueryFeeModels, logger: Logger, clientSignerAddress: Address, + protocolNetwork: string, ) { - logger = logger.child({ component: 'ReceiptManager' }) + this.logger = logger.child({ + component: 'ReceiptManager', + protocolNetwork, + }) this._sequelize = sequelize this._queryFeeModels = queryFeeModels this._allocationReceiptVerifier = new NativeSignatureVerifier(clientSignerAddress) + this.protocolNetwork = protocolNetwork timer(30_000).pipe(async () => { try { @@ -114,6 +121,7 @@ export class AllocationReceiptManager implements ReceiptManager { allocation: receipt.allocation, fees: receipt.fees.toString(), signature, + protocolNetwork: this.protocolNetwork, }) return receipt @@ -121,6 +129,10 @@ export class AllocationReceiptManager implements ReceiptManager { /// Flushes all receipts that have been registered by this moment in time. private async _flushOutstanding(): Promise { + this.logger.trace('Flushing outsdanding receipts', { + function: 'flushOutstanding', + queueLength: this._flushQueue.length, + }) let count = this._flushQueue.length while (count > 0) { @@ -137,19 +149,27 @@ export class AllocationReceiptManager implements ReceiptManager { const receipt = this._cache.get(id)! this._cache.delete(id) + const logger = this.logger.child({ function: 'flushOutstanding', receipt }) + const transact = async () => { // Put this in a transaction because this has a write which is // dependent on a read and must be atomic or receipt updates could be dropped. await this._sequelize.transaction( { isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ }, async (transaction: Transaction) => { + logger.trace('Begin database transaction to process receipt') const [summary, isNewSummary] = await ensureAllocationSummary( this._queryFeeModels, receipt.allocation, transaction, + this.protocolNetwork, ) + logger.trace('Built allocation summary', { + allocationSummary: summary, + new: isNewSummary, + }) if (isNewSummary) { - await summary.save() + await summary.save({ transaction }) } const [state, isNew] = @@ -160,15 +180,30 @@ export class AllocationReceiptManager implements ReceiptManager { allocation: receipt.allocation, signature: receipt.signature, fees: receipt.fees, + protocolNetwork: this.protocolNetwork, }, transaction, }) + logger.trace('Built allocation receipt', { + allocationReceipt: state, + new: isNew, + relatedAllocationSummary: summary, + }) // Don't save if we already have a version of the receipt // with a higher amount of fees if (!isNew) { const storedFees = BigNumber.from(state.getDataValue('fees')) if (storedFees.gte(receipt.fees)) { + logger.trace( + `Stored fees found in allocation receipt are greater than the current receipt, ignoring.`, + { + storedFees, + receiptFees: receipt.fees, + allocationReceipt: state, + relatedAllocationSummary: summary, + }, + ) return } } @@ -176,11 +211,17 @@ export class AllocationReceiptManager implements ReceiptManager { // Make sure the new receipt fee amount and signature are set state.set('fees', receipt.fees) state.set('signature', receipt.signature) + logger.trace('Saving allocation') // Save the new or updated receipt to the db await state.save({ transaction }) + logger.trace('Saved allocation receipt', { + allocationReceipt: state, + relatedAllocationSummary: summary, + }) }, ) + logger.trace('End database transaction to process receipt') } // Save to the db diff --git a/packages/indexer-service/src/server/__tests__/server.test.ts b/packages/indexer-service/src/server/__tests__/server.test.ts index b6feec5f7..92984a000 100644 --- a/packages/indexer-service/src/server/__tests__/server.test.ts +++ b/packages/indexer-service/src/server/__tests__/server.test.ts @@ -4,7 +4,7 @@ import http from 'http' import supertest from 'supertest' -import { BigNumber, ethers, Wallet } from 'ethers' +import { BigNumber, Wallet } from 'ethers' import { Sequelize } from 'sequelize' import { Socket } from 'net' @@ -62,7 +62,7 @@ const setup = async () => { models = defineIndexerManagementModels(sequelize) address = '0x90f8bf6a479f320ead074411a4b0e7944ea8c9c1' contracts = await connectContracts(getTestProvider('goerli'), 5) - await sequelize.sync({ force: true }) + sequelize = await sequelize.sync({ force: true }) const statusEndpoint = 'http://localhost:8030/graphql' indexingStatusResolver = new IndexingStatusResolver({ logger: logger, diff --git a/packages/indexer-service/src/server/status.ts b/packages/indexer-service/src/server/status.ts index be80a67a0..24e32d9b3 100644 --- a/packages/indexer-service/src/server/status.ts +++ b/packages/indexer-service/src/server/status.ts @@ -23,12 +23,24 @@ export const createStatusServer = async ({ fetch, }) + // Filtering the index-node server schema to the queries we want to expose externally + // indexingStatuses - needed by gateways, and explorer + // others are used for debugging data discrepancies + const supportedRootFields = [ + 'indexingStatuses', + 'publicProofsOfIndexing', + 'entityChangesInBlock', + 'blockData', + 'cachedEthereumCalls', + 'subgraphFeatures', + 'apiVersions', + ] const filteredSchema = wrapSchema({ schema, transforms: [ new FilterRootFields( (_operation: 'Query' | 'Mutation' | 'Subscription', rootFieldName: string) => - rootFieldName === 'indexingStatuses', + supportedRootFields.some(rootField => rootField === rootFieldName), ), ], }) diff --git a/yarn.lock b/yarn.lock index 3f0851367..2e1e757a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,17 +2,22 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" "@apollo/client@~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0": version "3.5.10" - resolved "https://registry.npmjs.org/@apollo/client/-/client-3.5.10.tgz" + resolved "https://registry.npmjs.org/@apollo/client/-/client-3.5.10.tgz#43463108a6e07ae602cca0afc420805a19339a71" integrity sha512-tL3iSpFe9Oldq7gYikZK1dcYxp1c01nlSwtsMz75382HcI6fvQXyFXUCJTTK3wgO2/ckaBvRGw7VqjFREdVoRw== dependencies: "@graphql-typed-document-node/core" "^3.0.0" @@ -28,150 +33,162 @@ tslib "^2.3.0" zen-observable-ts "^1.2.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== - dependencies: - "@babel/highlight" "^7.16.7" - -"@babel/compat-data@^7.17.10": - version "7.18.5" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.5.tgz#acac0c839e317038c73137fbb6ef71a1d6238471" - integrity sha512-BxhE40PVCBxVEJsSBhB6UWyAuqJRxGsAw8BdHMJ3AKGydcwuWW4kOO3HmqBQAdcq/OP+/DlTVxLvsCzRTnZuGg== - -"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.18.5" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.18.5.tgz#c597fa680e58d571c28dda9827669c78cdd7f000" - integrity sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.18.2" - "@babel/helper-compilation-targets" "^7.18.2" - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helpers" "^7.18.2" - "@babel/parser" "^7.18.5" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.5" - "@babel/types" "^7.18.4" +"@ardatan/sync-fetch@^0.0.1": + version "0.0.1" + resolved "https://registry.npmjs.org/@ardatan/sync-fetch/-/sync-fetch-0.0.1.tgz#3385d3feedceb60a896518a1db857ec1e945348f" + integrity sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA== + dependencies: + node-fetch "^2.6.1" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + +"@babel/compat-data@^7.22.9": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz#8df6e96661209623f1975d66c35ffca66f3306d0" + integrity sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.22.20.tgz#e3d0eed84c049e2a2ae0a64d27b6a37edec385b7" + integrity sha512-Y6jd1ahLubuYweD/zJH+vvOY141v4f9igNQAQ+MBgq9JlHS2iTsZKn1aMsb3vGccZsXI16VzTBw52Xx0DWmtnA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.22.15" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-module-transforms" "^7.22.20" + "@babel/helpers" "^7.22.15" + "@babel/parser" "^7.22.16" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.22.20" + "@babel/types" "^7.22.19" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" + json5 "^2.2.3" + semver "^6.3.1" -"@babel/generator@^7.18.2", "@babel/generator@^7.7.2": - version "7.18.2" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d" - integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw== +"@babel/generator@^7.22.15", "@babel/generator@^7.7.2": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz#1564189c7ec94cb8f77b5e8a90c4d200d21b2339" + integrity sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA== dependencies: - "@babel/types" "^7.18.2" - "@jridgewell/gen-mapping" "^0.3.0" + "@babel/types" "^7.22.15" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-compilation-targets@^7.18.2": - version "7.18.2" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b" - integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ== - dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.20.2" - semver "^6.3.0" - -"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.2": - version "7.18.2" - resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd" - integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ== - -"@babel/helper-function-name@^7.17.9": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" - integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== - dependencies: - "@babel/template" "^7.16.7" - "@babel/types" "^7.17.0" - -"@babel/helper-hoist-variables@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" - integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-module-imports@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" - integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-module-transforms@^7.18.0": - version "7.18.0" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd" - integrity sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA== - dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.0" - "@babel/types" "^7.18.0" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.17.12", "@babel/helper-plugin-utils@^7.8.0": - version "7.17.12" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96" - integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA== - -"@babel/helper-simple-access@^7.17.7": - version "7.18.2" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz#4dc473c2169ac3a1c9f4a51cfcd091d1c36fcff9" - integrity sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ== - dependencies: - "@babel/types" "^7.18.2" - -"@babel/helper-split-export-declaration@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" - integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== - -"@babel/helper-validator-option@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" - integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== - -"@babel/helpers@^7.18.2": - version "7.18.2" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.2.tgz#970d74f0deadc3f5a938bfa250738eb4ac889384" - integrity sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg== - dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.2" - "@babel/types" "^7.18.2" - -"@babel/highlight@^7.16.7": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz" - integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - chalk "^2.0.0" +"@babel/helper-compilation-targets@^7.22.15": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" + integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.15" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.20.tgz#da9edc14794babbe7386df438f3768067132f59e" + integrity sha512-dLT7JVWIUUxKOs1UnJUBR3S70YK+pKX6AbJgB2vMIvEkZkrfJDbYDJesnPshtKV4LhDOR3Oc5YULeDizRek+5A== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.22.19", "@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.22.15": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" + integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== + +"@babel/helpers@^7.22.15": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz#f09c3df31e86e3ea0b7ff7556d85cdebd47ea6f1" + integrity sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.18.5": - version "7.18.5" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.18.5.tgz#337062363436a893a2d22faa60be5bb37091c83c" - integrity sha512-YZWVaglMiplo7v8f1oMQ5ZPQr0vn7HPeZXxXWsxXJRjGVrzUFn9OxFQl1sb5wzfootjA/yChhW84BV+383FSOw== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.22.16": + version "7.22.16" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz#180aead7f247305cce6551bea2720934e2fa2c95" + integrity sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -208,6 +225,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" + integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -258,43 +282,44 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.17.12" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz#b54fc3be6de734a56b87508f99d6428b5b605a7b" - integrity sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/template@^7.16.7", "@babel/template@^7.3.3": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.18.5", "@babel/traverse@^7.7.2": - version "7.18.5" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.5.tgz#94a8195ad9642801837988ab77f36e992d9a20cd" - integrity sha512-aKXj1KT66sBj0vVzk6rEeAO6Z9aiiQ68wfDgge3nHhA/my6xMM/7HGQUNumKZaoa2qUPQ5whJG9aAifsxUKfLA== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.18.2" - "@babel/helper-environment-visitor" "^7.18.2" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.18.5" - "@babel/types" "^7.18.4" + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz#aac8d383b062c5072c647a31ef990c1d0af90272" + integrity sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/template@^7.22.15", "@babel/template@^7.22.5", "@babel/template@^7.3.3": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.22.15", "@babel/traverse@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.20.tgz#db572d9cb5c79e02d83e5618b82f6991c07584c9" + integrity sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.22.15" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.16" + "@babel/types" "^7.22.19" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.18.0", "@babel/types@^7.18.2", "@babel/types@^7.18.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.18.4" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354" - integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.3.3": + version "7.22.19" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.22.19.tgz#7425343253556916e440e662bb221a93ddb75684" + integrity sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.19" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -314,21 +339,52 @@ dependencies: "@cspotcode/source-map-consumer" "0.8.0" -"@eslint/eslintrc@^1.2.1", "@eslint/eslintrc@^1.2.2": - version "1.3.0" - resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" - integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": + version "4.8.1" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz#8c4bb756cc2aa7eaf13cfa5e69c83afb3260c20c" + integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ== + +"@eslint/eslintrc@^2.1.2": + version "2.1.2" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" + integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.3.2" - globals "^13.15.0" + espree "^9.6.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/js@8.49.0": + version "8.49.0" + resolved "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz#86f79756004a97fa4df866835093f1df3d03c333" + integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w== + +"@ethereumjs/rlp@^4.0.1": + version "4.0.1" + resolved "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" + integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== + +"@ethereumjs/util@^8.1.0": + version "8.1.0" + resolved "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4" + integrity sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA== + dependencies: + "@ethereumjs/rlp" "^4.0.1" + ethereum-cryptography "^2.0.0" + micro-ftch "^0.3.1" + "@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" @@ -691,7 +747,7 @@ "@fastify/ajv-compiler@^1.0.0": version "1.1.0" - resolved "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-1.1.0.tgz" + resolved "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-1.1.0.tgz#5ce80b1fc8bebffc8c5ba428d5e392d0f9ed10a1" integrity sha512-gvCOUNpXsWrIQ3A4aXCLIdblL0tDq42BG/2Xw7oxbil9h11uow10ztS2GuFazNBfjbrsZ5nl+nPl5jDSjj5TSg== dependencies: ajv "^6.12.6" @@ -703,7 +759,7 @@ "@google-cloud/common@^3.0.0", "@google-cloud/common@^3.4.1": version "3.10.0" - resolved "https://registry.npmjs.org/@google-cloud/common/-/common-3.10.0.tgz" + resolved "https://registry.npmjs.org/@google-cloud/common/-/common-3.10.0.tgz#454d1155bb512109cd83c6183aabbd39f9aabda7" integrity sha512-XMbJYMh/ZSaZnbnrrOFfR/oQrb0SxG4qh6hDisWCoEbFcBHV0qHQo4uXfeMCzolx2Mfkh6VDaOGg+hyJsmxrlw== dependencies: "@google-cloud/projectify" "^2.0.0" @@ -717,9 +773,9 @@ teeny-request "^7.0.0" "@google-cloud/logging-min@^9.6.3": - version "9.8.3" - resolved "https://registry.npmjs.org/@google-cloud/logging-min/-/logging-min-9.8.3.tgz" - integrity sha512-TFzq6zhGBcYB1GaDSJxNsNRRrPrrfEW5BwFM9hL1IgReyJWVr1N0olC2lxPo2g8VuaVG+bcRddWsF5wNCEzHGw== + version "9.9.0" + resolved "https://registry.npmjs.org/@google-cloud/logging-min/-/logging-min-9.9.0.tgz#eb611db9771646bfa7be7d4dceebfe4abf2e87f3" + integrity sha512-sx9KmfOjCw7A4i4rMWD6SVQyBrMhAwdWX99d1jXHl0cCBukRDyossLrksDIn6XDjtYg3GB5o5Yz2dVsFCW8Wcw== dependencies: "@google-cloud/common" "^3.4.1" "@google-cloud/paginator" "^3.0.0" @@ -739,7 +795,7 @@ "@google-cloud/paginator@^3.0.0": version "3.0.7" - resolved "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.7.tgz" + resolved "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.7.tgz#fb6f8e24ec841f99defaebf62c75c2e744dd419b" integrity sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ== dependencies: arrify "^2.0.0" @@ -747,7 +803,7 @@ "@google-cloud/profiler@4.1.7": version "4.1.7" - resolved "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-4.1.7.tgz" + resolved "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-4.1.7.tgz#152364f8284ec35bd4d163499ff0db07055103ea" integrity sha512-ycN2gdl7DlXVbZZYv4F5WcDLFLtpSNLzmchfNzQPyMMSAX2zg5DjhxJCJCu6CqLdJFTKSUylqEO5A3CU1T/vCQ== dependencies: "@google-cloud/common" "^3.0.0" @@ -767,45 +823,45 @@ "@google-cloud/projectify@^2.0.0": version "2.1.1" - resolved "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.1.1.tgz" + resolved "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.1.1.tgz#ae6af4fee02d78d044ae434699a630f8df0084ef" integrity sha512-+rssMZHnlh0twl122gXY4/aCrk0G1acBqkHFfYddtsqpYXGxA29nj9V5V9SfC+GyOG00l650f6lG9KL+EpFEWQ== "@google-cloud/promisify@^2.0.0": version "2.0.4" - resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.4.tgz" + resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.4.tgz#9d8705ecb2baa41b6b2673f3a8e9b7b7e1abc52a" integrity sha512-j8yRSSqswWi1QqUGKVEKOG03Q7qOoZP6/h2zN2YO+F5h2+DHU0bSrHCK9Y7lo2DI9fBd8qGAw795sf+3Jva4yA== -"@graphprotocol/common-ts@2.0.1": - version "2.0.1" - resolved "https://registry.npmjs.org/@graphprotocol/common-ts/-/common-ts-2.0.1.tgz#2f3518fe257b262f6194a35075eeafa6ad9faa2e" - integrity sha512-dmcTWVOS4HEvglMtH0wZTMeqlzniLAo+k04kZ7/cY+QgLXAYB7X1Sz3sHqkGmNbn89kBQ3Z/nPDLLSlln69hYg== +"@graphprotocol/common-ts@2.0.7": + version "2.0.7" + resolved "https://registry.npmjs.org/@graphprotocol/common-ts/-/common-ts-2.0.7.tgz#733ac3d46c9e24f997e7f8d00ffc9128d129285e" + integrity sha512-1O2bvXibViMTkiWNvOjijHR5+cMsuEXIZiUxR3pl4Lan9U2kBfGqJAas8qnrpdc/RdRGooYRMP3hXVB9N8bERA== dependencies: - "@graphprotocol/contracts" "2.1.0" + "@graphprotocol/contracts" "5.2.1" "@graphprotocol/pino-sentry-simple" "0.7.1" "@urql/core" "2.4.4" "@urql/exchange-execute" "1.2.2" - body-parser "1.19.1" - bs58 "4.0.1" + body-parser "1.20.2" + bs58 "5.0.0" cors "2.8.5" - cross-fetch "3.1.5" + cross-fetch "4.0.0" ethers "5.7.0" - express "4.17.3" - graphql "16.3.0" + express "4.18.2" + graphql "16.8.0" graphql-tag "2.12.6" - helmet "5.0.2" + helmet "7.0.0" morgan "1.10.0" ngeohash "0.6.3" - pg "8.7.3" + pg "8.11.3" pg-hstore "2.3.4" pino "7.6.0" pino-multi-stream "6.0.0" - prom-client "14.0.1" - sequelize "6.19.0" + prom-client "14.2.0" + sequelize "6.33.0" -"@graphprotocol/contracts@2.1.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@graphprotocol/contracts/-/contracts-2.1.0.tgz#c2e9131973af231e3f6f84ada31baebccc77ed48" - integrity sha512-SeymJCUxBp488K/KNi77EKvrkPT0t/7rERGp8zFkmf5KByerjihhE9Ty9oXJAU9RzbUpxsU0JkPBSmiw9/2DYQ== +"@graphprotocol/contracts@5.2.1": + version "5.2.1" + resolved "https://registry.npmjs.org/@graphprotocol/contracts/-/contracts-5.2.1.tgz#537e13697ca83c78498242706708b65550a656f7" + integrity sha512-32L6TN50hUcvVg5LDKQfUeJcrAN+sHMGEXVagMbJf9tLSCFAIheg0MPq0s9lQ+89GS1TJPA/fwwv8o268EtU4Q== dependencies: console-table-printer "^2.11.1" ethers "^5.6.0" @@ -820,7 +876,7 @@ "@graphprotocol/pino-sentry-simple@0.7.1": version "0.7.1" - resolved "https://registry.npmjs.org/@graphprotocol/pino-sentry-simple/-/pino-sentry-simple-0.7.1.tgz" + resolved "https://registry.npmjs.org/@graphprotocol/pino-sentry-simple/-/pino-sentry-simple-0.7.1.tgz#ac08b978bfa33178b9e809f53ae0983ff5f724d8" integrity sha512-iccKFdFBjarSp8/liXuK1EtGq8Vwn118tqymbOJBxblecRsi4rOebk63qnL+dK/a0IvxH6h2+RjjWDbRt7UsUA== dependencies: "@sentry/node" "^5.21.1" @@ -828,50 +884,104 @@ split2 "^3.1.1" through2 "^3.0.1" -"@graphql-tools/batch-execute@8.4.4": - version "8.4.4" - resolved "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-8.4.4.tgz" - integrity sha512-5B3srfrNh7qqaH4FWysiZXPDVD7snwM+qsW3Bkq8M0iRAZVUb3P9o23xJbBwS32g678TuCjKy113K0PSqHyeCw== +"@graphql-tools/batch-execute@^9.0.1": + version "9.0.2" + resolved "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.2.tgz#5ac3257501e7941fad40661bb5e1110d6312f58b" + integrity sha512-Y2uwdZI6ZnatopD/SYfZ1eGuQFI7OU2KGZ2/B/7G9ISmgMl5K+ZZWz/PfIEXeiHirIDhyk54s4uka5rj2xwKqQ== dependencies: - "@graphql-tools/utils" "8.6.7" - dataloader "2.1.0" - tslib "~2.3.0" - value-or-promise "1.0.11" + "@graphql-tools/utils" "^10.0.5" + dataloader "^2.2.2" + tslib "^2.4.0" + value-or-promise "^1.0.12" -"@graphql-tools/delegate@8.7.4": - version "8.7.4" - resolved "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-8.7.4.tgz" - integrity sha512-OXdIHRqqUDFvBebSZ/MQAvQOJ1Kvl7gjD78ClG4bPts6qDfFHwzlX0V8QESFCo8H67VDRzB4nnqlDyOIzjVNlQ== +"@graphql-tools/delegate@^10.0.0", "@graphql-tools/delegate@^10.0.3": + version "10.0.3" + resolved "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.0.3.tgz#2d0e133da94ca92c24e0c7360414e5592321cf2d" + integrity sha512-Jor9oazZ07zuWkykD3OOhT/2XD74Zm6Ar0ENZMk75MDD51wB2UWUIMljtHxbJhV5A6UBC2v8x6iY0xdCGiIlyw== dependencies: - "@graphql-tools/batch-execute" "8.4.4" - "@graphql-tools/schema" "8.3.8" - "@graphql-tools/utils" "8.6.7" - dataloader "2.1.0" - graphql-executor "0.0.23" - tslib "~2.3.0" - value-or-promise "1.0.11" + "@graphql-tools/batch-execute" "^9.0.1" + "@graphql-tools/executor" "^1.0.0" + "@graphql-tools/schema" "^10.0.0" + "@graphql-tools/utils" "^10.0.5" + dataloader "^2.2.2" + tslib "^2.5.0" -"@graphql-tools/load@7.5.8": - version "7.5.8" - resolved "https://registry.npmjs.org/@graphql-tools/load/-/load-7.5.8.tgz" - integrity sha512-+kQ7aT9GEuBmiGQlGsFU5f2e1A0hMbwCePzHYOvHR5BF8soJeToWZLiIC2hJf6z06aco+LC9x/os+6p9U9+7iQ== +"@graphql-tools/executor-graphql-ws@^1.0.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-1.1.0.tgz#7727159ebaa9df4dc793d0d02e74dd1ca4a7cc60" + integrity sha512-yM67SzwE8rYRpm4z4AuGtABlOp9mXXVy6sxXnTJRoYIdZrmDbKVfIY+CpZUJCqS0FX3xf2+GoHlsj7Qswaxgcg== dependencies: - "@graphql-tools/schema" "8.3.8" - "@graphql-tools/utils" "8.6.7" + "@graphql-tools/utils" "^10.0.2" + "@types/ws" "^8.0.0" + graphql-ws "^5.14.0" + isomorphic-ws "^5.0.0" + tslib "^2.4.0" + ws "^8.13.0" + +"@graphql-tools/executor-http@^1.0.0": + version "1.0.2" + resolved "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-1.0.2.tgz#d7964a6e5ec883842f9a8e3f104f93c9b8f472be" + integrity sha512-JKTB4E3kdQM2/1NEcyrVPyQ8057ZVthCV5dFJiKktqY9IdmF00M8gupFcW3jlbM/Udn78ickeUBsUzA3EouqpA== + dependencies: + "@graphql-tools/utils" "^10.0.2" + "@repeaterjs/repeater" "^3.0.4" + "@whatwg-node/fetch" "^0.9.0" + extract-files "^11.0.0" + meros "^1.2.1" + tslib "^2.4.0" + value-or-promise "^1.0.12" + +"@graphql-tools/executor-legacy-ws@^1.0.0": + version "1.0.3" + resolved "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.0.3.tgz#de04eaa816fa27f46b401e8d5c28d9c0562b4993" + integrity sha512-rr3IDeO9Dh+8u8KIro++5kzJJYPHkcrIAWzqXtN663nhInC85iW7Ko91yOYwf7ovBci/7s+4Rqe4ZRyca1LGjQ== + dependencies: + "@graphql-tools/utils" "^10.0.0" + "@types/ws" "^8.0.0" + isomorphic-ws "5.0.0" + tslib "^2.4.0" + ws "8.14.1" + +"@graphql-tools/executor@^1.0.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.2.0.tgz#6c45f4add765769d9820c4c4405b76957ba39c79" + integrity sha512-SKlIcMA71Dha5JnEWlw4XxcaJ+YupuXg0QCZgl2TOLFz4SkGCwU/geAsJvUJFwK2RbVLpQv/UMq67lOaBuwDtg== + dependencies: + "@graphql-tools/utils" "^10.0.0" + "@graphql-typed-document-node/core" "3.2.0" + "@repeaterjs/repeater" "^3.0.4" + tslib "^2.4.0" + value-or-promise "^1.0.12" + +"@graphql-tools/load@8.0.0": + version "8.0.0" + resolved "https://registry.npmjs.org/@graphql-tools/load/-/load-8.0.0.tgz#62e00f48c39b4085167a096f66ba6c21fb3fc796" + integrity sha512-Cy874bQJH0FP2Az7ELPM49iDzOljQmK1PPH6IuxsWzLSTxwTqd8dXA09dcVZrI7/LsN26heTY2R8q2aiiv0GxQ== + dependencies: + "@graphql-tools/schema" "^10.0.0" + "@graphql-tools/utils" "^10.0.0" p-limit "3.1.0" - tslib "~2.3.0" + tslib "^2.4.0" "@graphql-tools/merge@8.2.8": version "8.2.8" - resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.2.8.tgz" + resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.2.8.tgz#6ed65c29b963b4d76b59a9d329fdf20ecef19a42" integrity sha512-e4kpzgEIlA0sC0NjJlMwUL73Iz/HoP2OgAUReDDsupvWCqW3PMxjNoviS8xmcklVnv1w8Vmr8U2tao+x40ypLA== dependencies: "@graphql-tools/utils" "8.6.7" tslib "~2.3.0" +"@graphql-tools/merge@^9.0.0": + version "9.0.0" + resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.0.tgz#b0a3636c82716454bff88e9bb40108b0471db281" + integrity sha512-J7/xqjkGTTwOJmaJQJ2C+VDBDOWJL3lKrHJN4yMaRLAJH3PosB7GiPRaSDZdErs0+F77sH2MKs2haMMkywzx7Q== + dependencies: + "@graphql-tools/utils" "^10.0.0" + tslib "^2.4.0" + "@graphql-tools/schema@8.3.8": version "8.3.8" - resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.3.8.tgz" + resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.3.8.tgz#68f35d733487732c522a1b47d27faf8809cce95a" integrity sha512-Bba60ali4fLOKJz/Kk39RcBrDUBtu0Wy7pjpIOmFIKQKwUBNNB0eAmfpvrjnFhRAVdO2kOkPpc8DQY+SCG+lWw== dependencies: "@graphql-tools/merge" "8.2.8" @@ -879,80 +989,109 @@ tslib "~2.3.0" value-or-promise "1.0.11" -"@graphql-tools/url-loader@7.9.11": - version "7.9.11" - resolved "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-7.9.11.tgz" - integrity sha512-janH0mhUxIsttEFwjtFIDAsfQwf1A2f9qKrvBIljF1Gcr/IWVH2DB7HfaQ5jIQrxaKv2wD3VHuU+/vMz+hpyOw== +"@graphql-tools/schema@^10.0.0": + version "10.0.0" + resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.0.tgz#7b5f6b6a59f51c927de8c9069bde4ebbfefc64b3" + integrity sha512-kf3qOXMFcMs2f/S8Y3A8fm/2w+GaHAkfr3Gnhh2LOug/JgpY/ywgFVxO3jOeSpSEdoYcDKLcXVjMigNbY4AdQg== dependencies: - "@graphql-tools/delegate" "8.7.4" - "@graphql-tools/utils" "8.6.7" - "@graphql-tools/wrap" "8.4.13" - "@n1ru4l/graphql-live-query" "^0.9.0" - "@types/websocket" "^1.0.4" + "@graphql-tools/merge" "^9.0.0" + "@graphql-tools/utils" "^10.0.0" + tslib "^2.4.0" + value-or-promise "^1.0.12" + +"@graphql-tools/url-loader@8.0.0": + version "8.0.0" + resolved "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.0.tgz#8d952d5ebb7325e587cb914aaebded3dbd078cf6" + integrity sha512-rPc9oDzMnycvz+X+wrN3PLrhMBQkG4+sd8EzaFN6dypcssiefgWKToXtRKI8HHK68n2xEq1PyrOpkjHFJB+GwA== + dependencies: + "@ardatan/sync-fetch" "^0.0.1" + "@graphql-tools/delegate" "^10.0.0" + "@graphql-tools/executor-graphql-ws" "^1.0.0" + "@graphql-tools/executor-http" "^1.0.0" + "@graphql-tools/executor-legacy-ws" "^1.0.0" + "@graphql-tools/utils" "^10.0.0" + "@graphql-tools/wrap" "^10.0.0" "@types/ws" "^8.0.0" - cross-undici-fetch "^0.2.4" - dset "^3.1.0" - extract-files "^11.0.0" - graphql-sse "^1.0.1" - graphql-ws "^5.4.1" - isomorphic-ws "^4.0.1" - meros "^1.1.4" - subscriptions-transport-ws "^0.11.0" - sync-fetch "^0.3.1" - tslib "^2.3.0" + "@whatwg-node/fetch" "^0.9.0" + isomorphic-ws "^5.0.0" + tslib "^2.4.0" value-or-promise "^1.0.11" - ws "^8.3.0" + ws "^8.12.0" "@graphql-tools/utils@8.6.7": version "8.6.7" - resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.6.7.tgz" + resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.6.7.tgz#0e21101233743eb67a5782a5a40919d85ddb1021" integrity sha512-Qi3EN95Rt3hb8CyDKpPKFWOPrnc00P18cpVTXEgtKxetSP39beJBeEEtLB0R53eP/6IolsyTZOTgkET1EaERaw== dependencies: tslib "~2.3.0" -"@graphql-tools/wrap@8.4.13": - version "8.4.13" - resolved "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-8.4.13.tgz" - integrity sha512-q0Fa0CVgcaqm4FI4GXAVLjz8TQaF6lpFOm/rlgEkMzW9wFY/ZvDs+K3fVh9BgNvpudJArnVzAZgl2+FHNdY9CA== +"@graphql-tools/utils@^10.0.0", "@graphql-tools/utils@^10.0.2", "@graphql-tools/utils@^10.0.5": + version "10.0.6" + resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.0.6.tgz#8a809d6bc0df27ffe8964696f182af2383b5974b" + integrity sha512-hZMjl/BbX10iagovakgf3IiqArx8TPsotq5pwBld37uIX1JiZoSbgbCIFol7u55bh32o6cfDEiiJgfAD5fbeyQ== dependencies: - "@graphql-tools/delegate" "8.7.4" - "@graphql-tools/schema" "8.3.8" - "@graphql-tools/utils" "8.6.7" - tslib "~2.3.0" - value-or-promise "1.0.11" + "@graphql-typed-document-node/core" "^3.1.1" + dset "^3.1.2" + tslib "^2.4.0" -"@graphql-typed-document-node/core@^3.0.0", "@graphql-typed-document-node/core@^3.1.1": - version "3.1.1" - resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz" - integrity sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg== +"@graphql-tools/wrap@10.0.1", "@graphql-tools/wrap@^10.0.0": + version "10.0.1" + resolved "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.0.1.tgz#9e3d27d2723962c26c4377d5d7ab0d3038bf728c" + integrity sha512-Cw6hVrKGM2OKBXeuAGltgy4tzuqQE0Nt7t/uAqnuokSXZhMHXJUb124Bnvxc2gPZn5chfJSDafDe4Cp8ZAVJgg== + dependencies: + "@graphql-tools/delegate" "^10.0.3" + "@graphql-tools/schema" "^10.0.0" + "@graphql-tools/utils" "^10.0.0" + tslib "^2.4.0" + value-or-promise "^1.0.12" + +"@graphql-typed-document-node/core@3.2.0", "@graphql-typed-document-node/core@^3.0.0", "@graphql-typed-document-node/core@^3.1.1": + version "3.2.0" + resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" + integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== "@grpc/grpc-js@~1.6.0": - version "1.6.7" - resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz" - integrity sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw== + version "1.6.12" + resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.12.tgz#20f710d8a8c5c396b2ae9530ba6c06b984614fdf" + integrity sha512-JmvQ03OTSpVd9JTlj/K3IWHSz4Gk/JMLUTtW7Zb0KvO1LcOYGATh5cNuRYzCAeDR3O8wq+q8FZe97eO9MBrkUw== dependencies: - "@grpc/proto-loader" "^0.6.4" + "@grpc/proto-loader" "^0.7.0" "@types/node" ">=12.12.47" -"@grpc/proto-loader@^0.6.1", "@grpc/proto-loader@^0.6.4": - version "0.6.9" - resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.9.tgz" - integrity sha512-UlcCS8VbsU9d3XTXGiEVFonN7hXk+oMXZtoHHG2oSA1/GcDP1q6OUgs20PzHDGizzyi8ufGSUDlk3O2NyY7leg== +"@grpc/proto-loader@^0.6.12": + version "0.6.13" + resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.13.tgz#008f989b72a40c60c96cd4088522f09b05ac66bc" + integrity sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g== dependencies: "@types/long" "^4.0.1" lodash.camelcase "^4.3.0" long "^4.0.0" - protobufjs "^6.10.0" + protobufjs "^6.11.3" yargs "^16.2.0" -"@humanwhocodes/config-array@^0.9.2": - version "0.9.5" - resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" - integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== +"@grpc/proto-loader@^0.7.0": + version "0.7.10" + resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz#6bf26742b1b54d0a473067743da5d3189d06d720" + integrity sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.2.4" + yargs "^17.7.2" + +"@humanwhocodes/config-array@^0.11.11": + version "0.11.11" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" + integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" @@ -961,14 +1100,26 @@ "@hutson/parse-repository-url@^3.0.0": version "3.0.2" - resolved "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz" + resolved "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== "@iarna/toml@2.2.5": version "2.2.5" - resolved "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz" + resolved "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" resolved "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" @@ -990,214 +1141,229 @@ resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" - integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^27.5.1" - jest-util "^27.5.1" + jest-message-util "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" -"@jest/core@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" - integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== dependencies: - "@jest/console" "^27.5.1" - "@jest/reporters" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - emittery "^0.8.1" + ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^27.5.1" - jest-config "^27.5.1" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-resolve-dependencies "^27.5.1" - jest-runner "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - jest-watcher "^27.5.1" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" micromatch "^4.0.4" - rimraf "^3.0.0" + pretty-format "^29.7.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" - integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== dependencies: - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^27.5.1" + jest-mock "^29.7.0" + +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== + dependencies: + expect "^29.7.0" + jest-snapshot "^29.7.0" -"@jest/fake-timers@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" - integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== dependencies: - "@jest/types" "^27.5.1" - "@sinonjs/fake-timers" "^8.0.1" + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" -"@jest/globals@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" - integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== dependencies: - "@jest/environment" "^27.5.1" - "@jest/types" "^27.5.1" - expect "^27.5.1" + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" -"@jest/reporters@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" - integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" - glob "^7.1.2" + glob "^7.1.3" graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" + istanbul-lib-instrument "^6.0.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-haste-map "^27.5.1" - jest-resolve "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" slash "^3.0.0" - source-map "^0.6.0" string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^8.1.0" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" -"@jest/source-map@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" - integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== dependencies: + "@jridgewell/trace-mapping" "^0.3.18" callsites "^3.0.0" graceful-fs "^4.2.9" - source-map "^0.6.0" -"@jest/test-result@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" - integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== dependencies: - "@jest/console" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" - integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== dependencies: - "@jest/test-result" "^27.5.1" + "@jest/test-result" "^29.7.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-runtime "^27.5.1" + jest-haste-map "^29.7.0" + slash "^3.0.0" -"@jest/transform@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" - integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^27.5.1" + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-regex-util "^27.5.1" - jest-util "^27.5.1" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" + write-file-atomic "^4.0.2" -"@jest/types@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" - integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: + "@jest/schemas" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" - "@types/yargs" "^16.0.0" + "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.0": - version "0.3.1" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9" - integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg== +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: - "@jridgewell/set-array" "^1.0.0" + "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.0.3": - version "3.0.7" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" - integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== -"@jridgewell/set-array@^1.0.0": - version "1.1.1" - resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" - integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.13" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" - integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.13" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" - integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.19" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" "@lerna/add@6.1.0": version "6.1.0" @@ -1898,9 +2064,9 @@ semver "^7.3.5" tar "^6.1.11" -"@mapbox/node-pre-gyp@1.0.9", "@mapbox/node-pre-gyp@^1.0.0": +"@mapbox/node-pre-gyp@1.0.9": version "1.0.9" - resolved "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz" + resolved "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc" integrity sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw== dependencies: detect-libc "^2.0.0" @@ -1913,14 +2079,41 @@ semver "^7.3.5" tar "^6.1.11" -"@n1ru4l/graphql-live-query@^0.9.0": - version "0.9.0" - resolved "https://registry.npmjs.org/@n1ru4l/graphql-live-query/-/graphql-live-query-0.9.0.tgz" - integrity sha512-BTpWy1e+FxN82RnLz4x1+JcEewVdfmUhV1C6/XYD5AjS7PQp9QFF7K8bCD6gzPTr2l+prvqOyVueQhFJxB1vfg== +"@mapbox/node-pre-gyp@^1.0.0": + version "1.0.11" + resolved "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" + integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + +"@noble/curves@1.1.0", "@noble/curves@~1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d" + integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== + dependencies: + "@noble/hashes" "1.3.1" + +"@noble/hashes@1.3.1": + version "1.3.1" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" + integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== + +"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1": + version "1.3.2" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -1928,12 +2121,12 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" - resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" @@ -1987,6 +2180,13 @@ "@gar/promisify" "^1.1.3" semver "^7.3.5" +"@npmcli/fs@^3.1.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e" + integrity sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w== + dependencies: + semver "^7.3.5" + "@npmcli/git@^3.0.0": version "3.0.2" resolved "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz#5c5de6b4d70474cf2d09af149ce42e4e1dacb931" @@ -2073,48 +2273,91 @@ read-package-json-fast "^2.0.3" which "^2.0.2" -"@nrwl/cli@15.3.0": - version "15.3.0" - resolved "https://registry.npmjs.org/@nrwl/cli/-/cli-15.3.0.tgz#61b145d2ba613f9df4dbb9188e631ca50a4e42cb" - integrity sha512-WAki2+puBp6qel/VAxdQmr/L/sLyw8K6bynYNmMl4eIlR5hjefrUChPzUiJDAS9/CUYQNOyva2VV5wofzdv95w== +"@nrwl/cli@15.9.7": + version "15.9.7" + resolved "https://registry.npmjs.org/@nrwl/cli/-/cli-15.9.7.tgz#1db113f5cb1cfe63213097be1ece041eef33da1f" + integrity sha512-1jtHBDuJzA57My5nLzYiM372mJW0NY6rFKxlWt5a0RLsAZdPTHsd8lE3Gs9XinGC1jhXbruWmhhnKyYtZvX/zA== dependencies: - nx "15.3.0" + nx "15.9.7" "@nrwl/devkit@>=14.8.6 < 16": - version "15.3.0" - resolved "https://registry.npmjs.org/@nrwl/devkit/-/devkit-15.3.0.tgz#4b0fc4c94f0b92413aa3d028f8cc75f586936d27" - integrity sha512-1O9QLB/eYS6ddw4MZnV4yj4CEqLIbpleZZiG/9w1TaiVO/jfNfXVaxc8EA87XSzMpk2W+/4Qggmabt6gAQaabA== + version "15.9.7" + resolved "https://registry.npmjs.org/@nrwl/devkit/-/devkit-15.9.7.tgz#14d19ec82ff4209c12147a97f1cdea05d8f6c087" + integrity sha512-Sb7Am2TMT8AVq8e+vxOlk3AtOA2M0qCmhBzoM1OJbdHaPKc0g0UgSnWRml1kPGg5qfPk72tWclLoZJ5/ut0vTg== dependencies: - "@phenomnomnominal/tsquery" "4.1.1" ejs "^3.1.7" ignore "^5.0.4" - semver "7.3.4" + semver "7.5.4" + tmp "~0.2.1" tslib "^2.3.0" -"@nrwl/tao@15.3.0": - version "15.3.0" - resolved "https://registry.npmjs.org/@nrwl/tao/-/tao-15.3.0.tgz#20266f1269815cb28e21677b0aa7f913a7e31b17" - integrity sha512-alyzKKSgfgPwQ/FUozvk43VGOZHyNMiSM6Udl49ZaQwT77GXRFkrOu21odW6dciWPd3iUOUjfJISNqrEJmxvpw== - dependencies: - nx "15.3.0" +"@nrwl/nx-darwin-arm64@15.9.7": + version "15.9.7" + resolved "https://registry.npmjs.org/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.9.7.tgz#a2cb7390c782b8acf3bb8806a3002620226a933d" + integrity sha512-aBUgnhlkrgC0vu0fK6eb9Vob7eFnkuknrK+YzTjmLrrZwj7FGNAeyGXSlyo1dVokIzjVKjJg2saZZ0WQbfuCJw== + +"@nrwl/nx-darwin-x64@15.9.7": + version "15.9.7" + resolved "https://registry.npmjs.org/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.9.7.tgz#af0437e726aeb97eb660646bfd9a7da5ba7a0a6f" + integrity sha512-L+elVa34jhGf1cmn38Z0sotQatmLovxoASCIw5r1CBZZeJ5Tg7Y9nOwjRiDixZxNN56hPKXm6xl9EKlVHVeKlg== + +"@nrwl/nx-linux-arm-gnueabihf@15.9.7": + version "15.9.7" + resolved "https://registry.npmjs.org/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.9.7.tgz#e29f4d31afa903bfb4d0fd7421e19be1086eae87" + integrity sha512-pqmfqqEUGFu6PmmHKyXyUw1Al0Ki8PSaR0+ndgCAb1qrekVDGDfznJfaqxN0JSLeolPD6+PFtLyXNr9ZyPFlFg== + +"@nrwl/nx-linux-arm64-gnu@15.9.7": + version "15.9.7" + resolved "https://registry.npmjs.org/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.9.7.tgz#eb2880a24d3268dd93583d21a6a0b9ff96bb23b4" + integrity sha512-NYOa/eRrqmM+In5g3M0rrPVIS9Z+q6fvwXJYf/KrjOHqqan/KL+2TOfroA30UhcBrwghZvib7O++7gZ2hzwOnA== + +"@nrwl/nx-linux-arm64-musl@15.9.7": + version "15.9.7" + resolved "https://registry.npmjs.org/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.9.7.tgz#5d04913c4672a96cefa78491824620d8a8bcfd7f" + integrity sha512-zyStqjEcmbvLbejdTOrLUSEdhnxNtdQXlmOuymznCzYUEGRv+4f7OAepD3yRoR0a/57SSORZmmGQB7XHZoYZJA== + +"@nrwl/nx-linux-x64-gnu@15.9.7": + version "15.9.7" + resolved "https://registry.npmjs.org/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.9.7.tgz#cf7f61fd87f35a793e6824952a6eb12242fe43fd" + integrity sha512-saNK5i2A8pKO3Il+Ejk/KStTApUpWgCxjeUz9G+T8A+QHeDloZYH2c7pU/P3jA9QoNeKwjVO9wYQllPL9loeVg== + +"@nrwl/nx-linux-x64-musl@15.9.7": + version "15.9.7" + resolved "https://registry.npmjs.org/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.9.7.tgz#2bec23c3696780540eb47fa1358dda780c84697f" + integrity sha512-extIUThYN94m4Vj4iZggt6hhMZWQSukBCo8pp91JHnDcryBg7SnYmnikwtY1ZAFyyRiNFBLCKNIDFGkKkSrZ9Q== + +"@nrwl/nx-win32-arm64-msvc@15.9.7": + version "15.9.7" + resolved "https://registry.npmjs.org/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.9.7.tgz#21b56ef3ab4190370effea71bd83fdc3e47ec69c" + integrity sha512-GSQ54hJ5AAnKZb4KP4cmBnJ1oC4ILxnrG1mekxeM65c1RtWg9NpBwZ8E0gU3xNrTv8ZNsBeKi/9UhXBxhsIh8A== + +"@nrwl/nx-win32-x64-msvc@15.9.7": + version "15.9.7" + resolved "https://registry.npmjs.org/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.9.7.tgz#1677ab1dcce921706b5677dc2844e3e0027f8bd5" + integrity sha512-x6URof79RPd8AlapVbPefUD3ynJZpmah3tYaYZ9xZRMXojVtEHV8Qh5vysKXQ1rNYJiiB8Ah6evSKWLbAH60tw== + +"@nrwl/tao@15.9.7": + version "15.9.7" + resolved "https://registry.npmjs.org/@nrwl/tao/-/tao-15.9.7.tgz#c0e78c99caa6742762f7558f20d8524bc9015e97" + integrity sha512-OBnHNvQf3vBH0qh9YnvBQQWyyFZ+PWguF6dJ8+1vyQYlrLVk/XZ8nJ4ukWFb+QfPv/O8VBmqaofaOI9aFC4yTw== + dependencies: + nx "15.9.7" "@octokit/auth-token@^2.4.0", "@octokit/auth-token@^2.4.4": version "2.5.0" - resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz" + resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== dependencies: "@octokit/types" "^6.0.3" "@octokit/auth-token@^3.0.0": - version "3.0.2" - resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.2.tgz#a0fc8de149fd15876e1ac78f6525c1c5ab48435f" - integrity sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q== - dependencies: - "@octokit/types" "^8.0.0" + version "3.0.4" + resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz#70e941ba742bdd2b49bdb7393e821dea8520a3db" + integrity sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ== "@octokit/core@3.2.0": version "3.2.0" - resolved "https://registry.npmjs.org/@octokit/core/-/core-3.2.0.tgz" + resolved "https://registry.npmjs.org/@octokit/core/-/core-3.2.0.tgz#7a872ad4cb8d8d2f417dd7fe1aaff3919c09dc04" integrity sha512-42jzu1GWlCr4KUo52X4hD3if2AwjNJLzsS8mqUs9JkJbsM3vzvSx8AqTnVBQjOM0hQMYBqR7/7SAUTfH7IZqIg== dependencies: "@octokit/auth-token" "^2.4.0" @@ -2126,7 +2369,7 @@ "@octokit/core@^3.5.1": version "3.6.0" - resolved "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz" + resolved "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" integrity sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q== dependencies: "@octokit/auth-token" "^2.4.4" @@ -2137,22 +2380,22 @@ before-after-hook "^2.2.0" universal-user-agent "^6.0.0" -"@octokit/core@^4.1.0": - version "4.1.0" - resolved "https://registry.npmjs.org/@octokit/core/-/core-4.1.0.tgz#b6b03a478f1716de92b3f4ec4fd64d05ba5a9251" - integrity sha512-Czz/59VefU+kKDy+ZfDwtOIYIkFjExOKf+HA92aiTZJ6EfWpFzYQWw0l54ji8bVmyhc+mGaLUbSUmXazG7z5OQ== +"@octokit/core@^4.2.1": + version "4.2.4" + resolved "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz#d8769ec2b43ff37cc3ea89ec4681a20ba58ef907" + integrity sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ== dependencies: "@octokit/auth-token" "^3.0.0" "@octokit/graphql" "^5.0.0" "@octokit/request" "^6.0.0" "@octokit/request-error" "^3.0.0" - "@octokit/types" "^8.0.0" + "@octokit/types" "^9.0.0" before-after-hook "^2.2.0" universal-user-agent "^6.0.0" "@octokit/endpoint@^6.0.1": version "6.0.12" - resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz" + resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== dependencies: "@octokit/types" "^6.0.3" @@ -2160,17 +2403,17 @@ universal-user-agent "^6.0.0" "@octokit/endpoint@^7.0.0": - version "7.0.3" - resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.3.tgz#0b96035673a9e3bedf8bab8f7335de424a2147ed" - integrity sha512-57gRlb28bwTsdNXq+O3JTQ7ERmBTuik9+LelgcLIVfYwf235VHbN9QNo4kXExtp/h8T423cR5iJThKtFYxC7Lw== + version "7.0.6" + resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz#791f65d3937555141fb6c08f91d618a7d645f1e2" + integrity sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg== dependencies: - "@octokit/types" "^8.0.0" + "@octokit/types" "^9.0.0" is-plain-object "^5.0.0" universal-user-agent "^6.0.0" "@octokit/graphql@^4.3.1", "@octokit/graphql@^4.5.8": version "4.8.0" - resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== dependencies: "@octokit/request" "^5.6.0" @@ -2178,67 +2421,67 @@ universal-user-agent "^6.0.0" "@octokit/graphql@^5.0.0": - version "5.0.4" - resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.4.tgz#519dd5c05123868276f3ae4e50ad565ed7dff8c8" - integrity sha512-amO1M5QUQgYQo09aStR/XO7KAl13xpigcy/kI8/N1PnZYSS69fgte+xA4+c2DISKqUZfsh0wwjc2FaCt99L41A== + version "5.0.6" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz#9eac411ac4353ccc5d3fca7d76736e6888c5d248" + integrity sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw== dependencies: "@octokit/request" "^6.0.0" - "@octokit/types" "^8.0.0" + "@octokit/types" "^9.0.0" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^11.2.0": - version "11.2.0" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz" - integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== +"@octokit/openapi-types@^12.11.0": + version "12.11.0" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" + integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== -"@octokit/openapi-types@^14.0.0": - version "14.0.0" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz#949c5019028c93f189abbc2fb42f333290f7134a" - integrity sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw== +"@octokit/openapi-types@^18.0.0": + version "18.0.0" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz#f43d765b3c7533fd6fb88f3f25df079c24fccf69" + integrity sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw== "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" - resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz" + resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== "@octokit/plugin-paginate-rest@^2.16.8": - version "2.17.0" - resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz" - integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== + version "2.21.3" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e" + integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw== dependencies: - "@octokit/types" "^6.34.0" + "@octokit/types" "^6.40.0" -"@octokit/plugin-paginate-rest@^5.0.0": - version "5.0.1" - resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz#93d7e74f1f69d68ba554fa6b888c2a9cf1f99a83" - integrity sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw== +"@octokit/plugin-paginate-rest@^6.1.2": + version "6.1.2" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz#f86456a7a1fe9e58fec6385a85cf1b34072341f8" + integrity sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ== dependencies: - "@octokit/types" "^8.0.0" + "@octokit/tsconfig" "^1.0.2" + "@octokit/types" "^9.2.3" "@octokit/plugin-request-log@^1.0.4": version "1.0.4" - resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz" + resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== "@octokit/plugin-rest-endpoint-methods@^5.12.0": - version "5.13.0" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz" - integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== + version "5.16.2" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" + integrity sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw== dependencies: - "@octokit/types" "^6.34.0" + "@octokit/types" "^6.39.0" deprecation "^2.3.1" -"@octokit/plugin-rest-endpoint-methods@^6.7.0": - version "6.7.0" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz#2f6f17f25b6babbc8b41d2bb0a95a8839672ce7c" - integrity sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw== +"@octokit/plugin-rest-endpoint-methods@^7.1.2": + version "7.2.3" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz#37a84b171a6cb6658816c82c4082ac3512021797" + integrity sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA== dependencies: - "@octokit/types" "^8.0.0" - deprecation "^2.3.1" + "@octokit/types" "^10.0.0" "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": version "2.1.0" - resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz" + resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== dependencies: "@octokit/types" "^6.0.3" @@ -2246,17 +2489,17 @@ once "^1.4.0" "@octokit/request-error@^3.0.0": - version "3.0.2" - resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.2.tgz#f74c0f163d19463b87528efe877216c41d6deb0a" - integrity sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg== + version "3.0.3" + resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz#ef3dd08b8e964e53e55d471acfe00baa892b9c69" + integrity sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ== dependencies: - "@octokit/types" "^8.0.0" + "@octokit/types" "^9.0.0" deprecation "^2.0.0" once "^1.4.0" "@octokit/request@^5.4.0", "@octokit/request@^5.6.0", "@octokit/request@^5.6.3": version "5.6.3" - resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz" + resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== dependencies: "@octokit/endpoint" "^6.0.1" @@ -2267,20 +2510,20 @@ universal-user-agent "^6.0.0" "@octokit/request@^6.0.0": - version "6.2.2" - resolved "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz#a2ba5ac22bddd5dcb3f539b618faa05115c5a255" - integrity sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw== + version "6.2.8" + resolved "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz#aaf480b32ab2b210e9dadd8271d187c93171d8eb" + integrity sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw== dependencies: "@octokit/endpoint" "^7.0.0" "@octokit/request-error" "^3.0.0" - "@octokit/types" "^8.0.0" + "@octokit/types" "^9.0.0" is-plain-object "^5.0.0" node-fetch "^2.6.7" universal-user-agent "^6.0.0" "@octokit/rest@18.12.0": version "18.12.0" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== dependencies: "@octokit/core" "^3.5.1" @@ -2289,35 +2532,47 @@ "@octokit/plugin-rest-endpoint-methods" "^5.12.0" "@octokit/rest@^19.0.3": - version "19.0.5" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz#4dbde8ae69b27dca04b5f1d8119d282575818f6c" - integrity sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow== + version "19.0.13" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.13.tgz#e799393264edc6d3c67eeda9e5bd7832dcf974e4" + integrity sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA== dependencies: - "@octokit/core" "^4.1.0" - "@octokit/plugin-paginate-rest" "^5.0.0" + "@octokit/core" "^4.2.1" + "@octokit/plugin-paginate-rest" "^6.1.2" "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^6.7.0" + "@octokit/plugin-rest-endpoint-methods" "^7.1.2" + +"@octokit/tsconfig@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz#59b024d6f3c0ed82f00d08ead5b3750469125af7" + integrity sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA== + +"@octokit/types@^10.0.0": + version "10.0.0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz#7ee19c464ea4ada306c43f1a45d444000f419a4a" + integrity sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg== + dependencies: + "@octokit/openapi-types" "^18.0.0" "@octokit/types@^5.0.0": version "5.5.0" - resolved "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz" + resolved "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz#e5f06e8db21246ca102aa28444cdb13ae17a139b" integrity sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ== dependencies: "@types/node" ">= 8" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": - version "6.34.0" - resolved "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz" - integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": + version "6.41.0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" + integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== dependencies: - "@octokit/openapi-types" "^11.2.0" + "@octokit/openapi-types" "^12.11.0" -"@octokit/types@^8.0.0": - version "8.0.0" - resolved "https://registry.npmjs.org/@octokit/types/-/types-8.0.0.tgz#93f0b865786c4153f0f6924da067fe0bb7426a9f" - integrity sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg== +"@octokit/types@^9.0.0", "@octokit/types@^9.2.3": + version "9.3.2" + resolved "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz#3f5f89903b69f6a2d196d78ec35f888c0013cac5" + integrity sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA== dependencies: - "@octokit/openapi-types" "^14.0.0" + "@octokit/openapi-types" "^18.0.0" "@parcel/watcher@2.0.4": version "2.0.4" @@ -2327,79 +2582,104 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -"@phenomnomnominal/tsquery@4.1.1": - version "4.1.1" - resolved "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-4.1.1.tgz#42971b83590e9d853d024ddb04a18085a36518df" - integrity sha512-jjMmK1tnZbm1Jq5a7fBliM4gQwjxMU7TFoRNwIyzwlO+eHPRCFv/Nv+H/Gi1jc3WR7QURG8D5d0Tn12YGrUqBQ== - dependencies: - esquery "^1.0.1" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" - resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" - integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= + resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== "@protobufjs/base64@^1.1.2": version "1.1.2" - resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" + resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== "@protobufjs/codegen@^2.0.4": version "2.0.4" - resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz" + resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== "@protobufjs/eventemitter@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz" - integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= + resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== "@protobufjs/fetch@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz" - integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= + resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== dependencies: "@protobufjs/aspromise" "^1.1.1" "@protobufjs/inquire" "^1.1.0" "@protobufjs/float@^1.0.2": version "1.0.2" - resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" - integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= + resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== "@protobufjs/inquire@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz" - integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= + resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== "@protobufjs/path@^1.1.2": version "1.1.2" - resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" - integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= + resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== "@protobufjs/pool@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" - integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= + resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== "@protobufjs/utf8@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" - integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= + resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + +"@repeaterjs/repeater@^3.0.4": + version "3.0.4" + resolved "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca" + integrity sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA== "@rushstack/ts-command-line@^4.7.7": - version "4.10.9" - resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.9.tgz" - integrity sha512-TE3eZgHNVHOY3p8lp38FoNEJUr0+swPb24sCcYuwlC+MHgMGXyJNM+p7l3TKSBRiY01XShoL2k601oGwL00KlA== + version "4.16.0" + resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.16.0.tgz#92d3af993ca5ee271ea130d41f5ce6a9479cae3f" + integrity sha512-WJKhdR9ThK9Iy7t78O3at7I3X4Ssp5RRZay/IQa8NywqkFy/DQbT3iLouodMMdUwLZD9n8n++xLubVd3dkmpkg== dependencies: "@types/argparse" "1.0.38" argparse "~1.0.9" colors "~1.2.1" string-argv "~0.3.1" +"@scure/base@~1.1.0": + version "1.1.3" + resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz#8584115565228290a6c6c4961973e0903bb3df2f" + integrity sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q== + +"@scure/bip32@1.3.1": + version "1.3.1" + resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz#7248aea723667f98160f593d621c47e208ccbb10" + integrity sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A== + dependencies: + "@noble/curves" "~1.1.0" + "@noble/hashes" "~1.3.1" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" + integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + "@sentry/core@5.30.0": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz" + resolved "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" integrity sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg== dependencies: "@sentry/hub" "5.30.0" @@ -2410,7 +2690,7 @@ "@sentry/hub@5.30.0": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz" + resolved "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100" integrity sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ== dependencies: "@sentry/types" "5.30.0" @@ -2419,7 +2699,7 @@ "@sentry/minimal@5.30.0": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz" + resolved "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz#ce3d3a6a273428e0084adcb800bc12e72d34637b" integrity sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw== dependencies: "@sentry/hub" "5.30.0" @@ -2428,7 +2708,7 @@ "@sentry/node@^5.21.1": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz" + resolved "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz#4ca479e799b1021285d7fe12ac0858951c11cd48" integrity sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg== dependencies: "@sentry/core" "5.30.0" @@ -2443,7 +2723,7 @@ "@sentry/tracing@5.30.0": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz" + resolved "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f" integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw== dependencies: "@sentry/hub" "5.30.0" @@ -2454,39 +2734,44 @@ "@sentry/types@5.30.0": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz" + resolved "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402" integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw== "@sentry/utils@5.30.0": version "5.30.0" - resolved "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz" + resolved "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980" integrity sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww== dependencies: "@sentry/types" "5.30.0" tslib "^1.9.3" -"@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^8.0.1": - version "8.1.0" - resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" - integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: - "@sinonjs/commons" "^1.7.0" + "@sinonjs/commons" "^3.0.0" "@thi.ng/api@^7.1.4", "@thi.ng/api@^7.2.0": version "7.2.0" - resolved "https://registry.npmjs.org/@thi.ng/api/-/api-7.2.0.tgz" + resolved "https://registry.npmjs.org/@thi.ng/api/-/api-7.2.0.tgz#ed3d7c70aca157a8f53613f7359be7d2e31d6f18" integrity sha512-4NcwHXxwPF/JgJG/jSFd9rjfQNguF0QrHvd6e+CEf4T0sFChqetW6ZmJ6/a2X+noDVntgulegA+Bx0HHzw+Tyw== "@thi.ng/arrays@^1.0.3": version "1.0.3" - resolved "https://registry.npmjs.org/@thi.ng/arrays/-/arrays-1.0.3.tgz" + resolved "https://registry.npmjs.org/@thi.ng/arrays/-/arrays-1.0.3.tgz#f4d26184f6da3ff30beb2488a131e19f187fd920" integrity sha512-ZUB27bdpTwcvxYJTlt/eWKrj98nWXo0lAUPwRwubk4GlH8rTKKkc7qZr9/4LCKPsNjnZdQqbBtNvNf3HjYxCzw== dependencies: "@thi.ng/api" "^7.2.0" @@ -2498,7 +2783,7 @@ "@thi.ng/cache@1.0.94": version "1.0.94" - resolved "https://registry.npmjs.org/@thi.ng/cache/-/cache-1.0.94.tgz" + resolved "https://registry.npmjs.org/@thi.ng/cache/-/cache-1.0.94.tgz#b2ff14a72474604656cddbfa7fb814eef23f3195" integrity sha512-4APqEYyPnicFsljO5XRQWallT+v9IZLW10GZu7JUXcC9vqbWK0WP04ynLRnHK/ELKfshB9McEx3ogf8457W8Tg== dependencies: "@thi.ng/api" "^7.2.0" @@ -2507,21 +2792,21 @@ "@thi.ng/checks@^2.9.11": version "2.9.11" - resolved "https://registry.npmjs.org/@thi.ng/checks/-/checks-2.9.11.tgz" + resolved "https://registry.npmjs.org/@thi.ng/checks/-/checks-2.9.11.tgz#b7e4c78828f553613d2af025ce7ae3f9306f27bb" integrity sha512-fBvWod32w24JlJsrrOdl+tlx+UNehCORi4rHaJ7l7HH+SEhD/lYTCXOBjwu9D/ztIUjMP5Q+n8cAqI5iPhbvAQ== dependencies: tslib "^2.3.1" "@thi.ng/compare@^1.3.28", "@thi.ng/compare@^1.3.34": version "1.3.34" - resolved "https://registry.npmjs.org/@thi.ng/compare/-/compare-1.3.34.tgz" + resolved "https://registry.npmjs.org/@thi.ng/compare/-/compare-1.3.34.tgz#321e10780955b18a4bcf7876d0fe0323be9d7be8" integrity sha512-E+UWhmo8l5yeHDuriPUsfrnk/Mj5kSDNRX7lPfv2zNdAQ7N8UDzc0IXu46U6EpqtCReo+2n5N8qzfD3TjerFRw== dependencies: "@thi.ng/api" "^7.2.0" "@thi.ng/compose@^1.4.36": version "1.4.36" - resolved "https://registry.npmjs.org/@thi.ng/compose/-/compose-1.4.36.tgz" + resolved "https://registry.npmjs.org/@thi.ng/compose/-/compose-1.4.36.tgz#d9b737d16c80f9355cf598e41f71529acba564aa" integrity sha512-iaij+nAwgeyQTMAiGldAB0dfBBl77kcwJ8HPBFJxDDWsLCS3lIcywTcJPcXxZaL0nw49VJpPrgt0Qw17Q8Y+6g== dependencies: "@thi.ng/api" "^7.2.0" @@ -2529,7 +2814,7 @@ "@thi.ng/dcons@^2.3.34": version "2.3.34" - resolved "https://registry.npmjs.org/@thi.ng/dcons/-/dcons-2.3.34.tgz" + resolved "https://registry.npmjs.org/@thi.ng/dcons/-/dcons-2.3.34.tgz#eae63aaa7f2ef981dbf520fa9dc9f11ad4574cd8" integrity sha512-NTgwtZsB+3X4Hh3cp1KyY0GFwi/dMoX1FSpJhOhEiOa2REX33O5cryo8FUgSasGC7TeKBlLB9UADI84g+4mNlQ== dependencies: "@thi.ng/api" "^7.2.0" @@ -2542,17 +2827,17 @@ "@thi.ng/equiv@^1.0.45": version "1.0.45" - resolved "https://registry.npmjs.org/@thi.ng/equiv/-/equiv-1.0.45.tgz" + resolved "https://registry.npmjs.org/@thi.ng/equiv/-/equiv-1.0.45.tgz#02bf71a630939c8f61a2a7b9e83cca9569ead7f3" integrity sha512-tdXaJfF0pFvT80Q7BOlhc7H7ja/RbVGzlGpE4LqjDWfXPPbLYwmq6EbQuHWeXuvT0qe+BsGnuO5UXAR5B8oGGQ== "@thi.ng/errors@^1.3.4": version "1.3.4" - resolved "https://registry.npmjs.org/@thi.ng/errors/-/errors-1.3.4.tgz" + resolved "https://registry.npmjs.org/@thi.ng/errors/-/errors-1.3.4.tgz#8f7675f7a895a87f0f609d64db69bc04483e0b29" integrity sha512-hTk71OPKnioN349sdj2DAoY+69eSerB3MN4Zwz6mosr1QFzIMkfkNOtBeC+Gm0yi0V0EY5LeBYFgqb3oXbtTbw== "@thi.ng/heaps@1.2.38": version "1.2.38" - resolved "https://registry.npmjs.org/@thi.ng/heaps/-/heaps-1.2.38.tgz" + resolved "https://registry.npmjs.org/@thi.ng/heaps/-/heaps-1.2.38.tgz#c5027c02bedb41644fc71d109cd16a878a48ad0b" integrity sha512-lWDQsGmt6YuHgmog4wpSKa7wqawt0JVyCREWc/J2bA9vcg/cYfljda2teVTA3rTUhKWs/S2htorN8On7+Rxt5A== dependencies: "@thi.ng/api" "^7.1.4" @@ -2560,7 +2845,7 @@ "@thi.ng/heaps@^1.3.1": version "1.3.1" - resolved "https://registry.npmjs.org/@thi.ng/heaps/-/heaps-1.3.1.tgz" + resolved "https://registry.npmjs.org/@thi.ng/heaps/-/heaps-1.3.1.tgz#307d0673afb0a66362485f2ef639c029ec74d9ed" integrity sha512-0vPYpMTbPlfr1uCp+ebqsUzZkPyKFo1/owqQV2naTYU8dmSJ0wK3uP6aiU/UYrnnb0zVtY6xy72ucGOyapu0Rg== dependencies: "@thi.ng/api" "^7.2.0" @@ -2569,12 +2854,12 @@ "@thi.ng/hex@^1.0.4": version "1.0.4" - resolved "https://registry.npmjs.org/@thi.ng/hex/-/hex-1.0.4.tgz" + resolved "https://registry.npmjs.org/@thi.ng/hex/-/hex-1.0.4.tgz#5c5c86d3aef0422709b4aa8cfadd3b3f01a4a808" integrity sha512-9ofIG4nXhEskGeOJthpi/9LXFIPrlZ/MmHpgLWa3wNqTVhODP/o++mu9jDKojHEpKvswkkFCE+mSVmMu8xo4mQ== "@thi.ng/iterators@5.1.74": version "5.1.74" - resolved "https://registry.npmjs.org/@thi.ng/iterators/-/iterators-5.1.74.tgz" + resolved "https://registry.npmjs.org/@thi.ng/iterators/-/iterators-5.1.74.tgz#8e42ca092049ae4aa45e24e1a9649149fc5916b3" integrity sha512-dv7ExN1ygkHuPz0f2pFPoE1Ged+B3e3qgQ4n0iHDV386qDkYRgOFMGnSFaBDnNaKNlhyAXT8Kbb+jy/zbV+P4g== dependencies: "@thi.ng/api" "^7.2.0" @@ -2583,14 +2868,14 @@ "@thi.ng/math@^4.0.6": version "4.0.6" - resolved "https://registry.npmjs.org/@thi.ng/math/-/math-4.0.6.tgz" + resolved "https://registry.npmjs.org/@thi.ng/math/-/math-4.0.6.tgz#14f57425ceed922a3b22f6fc7b0f90a3a9a12f22" integrity sha512-MCXMW9Bzlgt8uqOkz1UB+gTvnB1viK5R/5c+z/1lmH2/rFa5TY8TKAByCsr1P+Rc+F7AbFoJlefFMSS7f5CDlg== dependencies: "@thi.ng/api" "^7.2.0" "@thi.ng/random@^2.4.8": version "2.4.8" - resolved "https://registry.npmjs.org/@thi.ng/random/-/random-2.4.8.tgz" + resolved "https://registry.npmjs.org/@thi.ng/random/-/random-2.4.8.tgz#428950e501c5a76907e3e6cb93985db07322012e" integrity sha512-4JJB8zbaPxjlAp1kCqsBbs6eN4Ivd/5fs1e4GlvmNkyGSucHIDTWvw6NnQWqUx2oPaAEDB9CFCH7SOcGC/cwkw== dependencies: "@thi.ng/api" "^7.2.0" @@ -2599,7 +2884,7 @@ "@thi.ng/transducers@^7.9.2": version "7.9.2" - resolved "https://registry.npmjs.org/@thi.ng/transducers/-/transducers-7.9.2.tgz" + resolved "https://registry.npmjs.org/@thi.ng/transducers/-/transducers-7.9.2.tgz#890166abe47dfbbd1497d6da7e2a1b2402bc4616" integrity sha512-FrC0cCdaQZ9di2sbLbwkQaweLUEvpaSVSwdbEhS77Pn/uoyZe+U+SnBAoyxKFgXHImtBpKFxu8Zd0is3ogrKNQ== dependencies: "@thi.ng/api" "^7.2.0" @@ -2611,14 +2896,9 @@ "@thi.ng/math" "^4.0.6" "@thi.ng/random" "^2.4.8" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - "@tootallnate/once@2": version "2.0.0" - resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== "@tsconfig/node10@^1.0.7": @@ -2627,77 +2907,62 @@ integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== "@tsconfig/node12@^1.0.7": - version "1.0.10" - resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.10.tgz#10fecee4a3be17357ce99b370bd81874044d8dbd" - integrity sha512-N+srakvPaYMGkwjNDx3ASx65Zl3QG8dJgVtIB+YMOkucU+zctlv/hdP5250VKdDHSDoW9PFZoCqbqNcAPjCjXA== + version "1.0.11" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": - version "1.0.2" - resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.2.tgz#b09c08de2eb319ca2acab17a1b8028af110b24b3" - integrity sha512-YwrUA5ysDXHFYfL0Xed9x3sNS4P+aKlCOnnbqUa2E5HdQshHFleCJVrj1PlGTb4GgFUCDyte1v3JWLy2sz8Oqg== + version "1.0.3" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": - version "1.0.3" - resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" - integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + version "1.0.4" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== "@types/argparse@1.0.38": version "1.0.38" - resolved "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz" + resolved "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9" integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.1.19" - resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" - integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw== +"@types/babel__core@^7.1.14": + version "7.20.2" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz#215db4f4a35d710256579784a548907237728756" + integrity sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA== dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" "@types/babel__generator" "*" "@types/babel__template" "*" "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.6.4" - resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" - integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== + version "7.6.5" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz#281f4764bcbbbc51fdded0f25aa587b4ce14da95" + integrity sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": - version "7.4.1" - resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" - integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== + version "7.4.2" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz#843e9f1f47c957553b0c374481dc4772921d6a6b" + integrity sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.17.1" - resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.17.1.tgz#1a0e73e8c28c7e832656db372b779bfd2ef37314" - integrity sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA== - dependencies: - "@babel/types" "^7.3.0" - -"@types/bn.js@^5.1.0": - version "5.1.0" - resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz" - integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== - dependencies: - "@types/node" "*" - -"@types/body-parser@*": - version "1.19.2" - resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" - integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.20.2" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz#4ddf99d95cfdd946ff35d2b65c978d9c9bf2645d" + integrity sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw== dependencies: - "@types/connect" "*" - "@types/node" "*" + "@babel/types" "^7.20.7" -"@types/body-parser@1.19.0": - version "1.19.0" - resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" - integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== +"@types/body-parser@*", "@types/body-parser@1.19.3": + version "1.19.3" + resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz#fb558014374f7d9e56c8f34bab2042a3a07d25cd" + integrity sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ== dependencies: "@types/connect" "*" "@types/node" "*" @@ -2710,77 +2975,68 @@ base-x "^3.0.6" "@types/connect@*", "@types/connect@^3.4.33": - version "3.4.35" - resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + version "3.4.36" + resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz#e511558c15a39cb29bd5357eebb57bd1459cd1ab" + integrity sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w== dependencies: "@types/node" "*" "@types/console-log-level@^1.4.0": - version "1.4.2" - resolved "https://registry.npmjs.org/@types/console-log-level/-/console-log-level-1.4.2.tgz" - integrity sha512-TnhDAntcJthcCMrR3OAKAUjgHyQgoms1yaBJepGv+BtXi8PLf8aX2L/NMCfofRTpVqW0bLklpGTsuqmUSCR2Uw== + version "1.4.3" + resolved "https://registry.npmjs.org/@types/console-log-level/-/console-log-level-1.4.3.tgz#f1f0c65caaf9c47ffbc748c40436957d69c53b31" + integrity sha512-B6Mzad6H4RugduMX84ehFVvGM/JRAd9lZQk4a6dztB4+zcIUehIjKrbWH/nHO2+0wwx05rgyqjXBvOjAv0uL6A== "@types/cookiejar@*": version "2.1.2" resolved "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog== -"@types/cors@2.8.12": - version "2.8.12" - resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" - integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== - -"@types/debug@^4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz" - integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg== +"@types/cors@2.8.14": + version "2.8.14" + resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.14.tgz#94eeb1c95eda6a8ab54870a3bf88854512f43a92" + integrity sha512-RXHUvNWYICtbP6s18PnOCaqToK8y14DnLd75c6HfyKf228dxy7pHNOQkxPtvXKp/hINFMDjbYzsj63nnpPMSRQ== dependencies: - "@types/ms" "*" + "@types/node" "*" -"@types/express-rate-limit@^5.1.3": - version "5.1.3" - resolved "https://registry.npmjs.org/@types/express-rate-limit/-/express-rate-limit-5.1.3.tgz#79f2ca40d90455a5798da6f8e06d8a3d35f4a1d6" - integrity sha512-H+TYy3K53uPU2TqPGFYaiWc2xJV6+bIFkDd/Ma2/h67Pa6ARk9kWE0p/K9OH1Okm0et9Sfm66fmXoAxsH2PHXg== +"@types/debug@^4.1.8": + version "4.1.8" + resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz#cef723a5d0a90990313faec2d1e22aee5eecb317" + integrity sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ== dependencies: - "@types/express" "*" + "@types/ms" "*" -"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18", "@types/express-serve-static-core@^4.17.9": - version "4.17.28" - resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz" - integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== +"@types/express-serve-static-core@^4.17.33", "@types/express-serve-static-core@^4.17.9": + version "4.17.36" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.36.tgz#baa9022119bdc05a4adfe740ffc97b5f9360e545" + integrity sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" + "@types/send" "*" -"@types/express@*", "@types/express@4.17.13": - version "4.17.13" - resolved "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" - integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.18" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/express@4.17.8": - version "4.17.8" - resolved "https://registry.npmjs.org/@types/express/-/express-4.17.8.tgz#3df4293293317e61c60137d273a2e96cd8d5f27a" - integrity sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ== +"@types/express@4.17.17": + version "4.17.17" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" + integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== dependencies: "@types/body-parser" "*" - "@types/express-serve-static-core" "*" + "@types/express-serve-static-core" "^4.17.33" "@types/qs" "*" "@types/serve-static" "*" -"@types/graceful-fs@^4.1.2": - version "4.1.5" - resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== +"@types/graceful-fs@^4.1.3": + version "4.1.6" + resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== dependencies: "@types/node" "*" +"@types/http-errors@*": + version "2.0.2" + resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz#a86e00bbde8950364f8e7846687259ffcd96e8c2" + integrity sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg== + "@types/isomorphic-fetch@0.0.35": version "0.0.35" resolved "https://registry.npmjs.org/@types/isomorphic-fetch/-/isomorphic-fetch-0.0.35.tgz#c1c0d402daac324582b6186b91f8905340ea3361" @@ -2810,33 +3066,89 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@27.4.1": - version "27.4.1" - resolved "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d" - integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw== +"@types/jest@29.5.4": + version "29.5.4" + resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.4.tgz#9d0a16edaa009a71e6a71a999acd582514dab566" + integrity sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/json-schema@^7.0.12": + version "7.0.13" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85" + integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ== + +"@types/lodash.clonedeep@^4.5.7": + version "4.5.7" + resolved "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.7.tgz#0e119f582ed6f9e6b373c04a644651763214f197" + integrity sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw== + dependencies: + "@types/lodash" "*" + +"@types/lodash.countby@^4.6.7": + version "4.6.7" + resolved "https://registry.npmjs.org/@types/lodash.countby/-/lodash.countby-4.6.7.tgz#9dfa94ff43823c314c70056c18c00adfb8fa7cc3" + integrity sha512-RkkfnOXscBXuRc9Iay+tMHtaztdRCtU7doEwi5yLEV/7UHRyy5yS+ppZCzFl06s6lWt6fcHKAN8F6kyes6js5g== dependencies: - jest-matcher-utils "^27.0.0" - pretty-format "^27.0.0" + "@types/lodash" "*" -"@types/json-schema@^7.0.9": - version "7.0.11" - resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/lodash.groupby@^4.6.7": + version "4.6.7" + resolved "https://registry.npmjs.org/@types/lodash.groupby/-/lodash.groupby-4.6.7.tgz#35fdb9647f100450d1004f65f74cbd964cdb567a" + integrity sha512-dFUR1pqdMgjIBbgPJ/8axJX6M1C7zsL+HF4qdYMQeJ7XOp0Qbf37I3zh9gpXr/ks6tgEYPDRqyZRAnFYvewYHQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash.intersection@^4.4.7": + version "4.4.7" + resolved "https://registry.npmjs.org/@types/lodash.intersection/-/lodash.intersection-4.4.7.tgz#fb7850d59e3259238dfe0ecb20952c589dd31dc5" + integrity sha512-7ukD2s54bmRNNpiH9ApEErO4H6mB8+WmXFr/6RpP3e/n7h3UFhEJC7QwLcoWAqOrYCIRFMAAwDf3ambSsW8c5Q== + dependencies: + "@types/lodash" "*" + +"@types/lodash.isequal@4.5.6": + version "4.5.6" + resolved "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.6.tgz#ff42a1b8e20caa59a97e446a77dc57db923bc02b" + integrity sha512-Ww4UGSe3DmtvLLJm2F16hDwEQSv7U0Rr8SujLUA2wHI2D2dm8kPu6Et+/y303LfjTIwSBKXB/YTUcAKpem/XEg== + dependencies: + "@types/lodash" "*" + +"@types/lodash.mapvalues@^4.6.7": + version "4.6.7" + resolved "https://registry.npmjs.org/@types/lodash.mapvalues/-/lodash.mapvalues-4.6.7.tgz#a1459b4353c769696b6735f19246753fd25ffc3a" + integrity sha512-yGTo9zD60Iw1Q+YBvx4Ad7RDj5rA6EpvYTpVPVsfe6pwHcwDcGChSqL1n2sPBMbCeHJ3R5kcaqpkdlrFe/y4Vg== + dependencies: + "@types/lodash" "*" + +"@types/lodash.xor@^4.5.7": + version "4.5.7" + resolved "https://registry.npmjs.org/@types/lodash.xor/-/lodash.xor-4.5.7.tgz#a8f218586005476b5bfe14a824aca33ae3b26161" + integrity sha512-Drr3ZdJWjpo7dNR17QdBwgfQRh7kEhhVVdeTVX0v35bT47zpfGSDYo40Ei0oOqk5DQzzxAYuNuW0s/N3mqBwxw== + dependencies: + "@types/lodash" "*" -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/lodash.zip@^4.2.7": + version "4.2.7" + resolved "https://registry.npmjs.org/@types/lodash.zip/-/lodash.zip-4.2.7.tgz#7f59b70e20d957a0fdbdbb907334ac2c470655f2" + integrity sha512-wRtK2bZ0HYXkJkeldrD35qOquGn5GOmp8+o886N18Aqw2DGFLP7JCTEb00j3xQZ+PCMTyfMS2OMbLUwah+bcyg== + dependencies: + "@types/lodash" "*" -"@types/lodash@^4.14.159": - version "4.14.182" - resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz" - integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== +"@types/lodash@*", "@types/lodash@^4.14.159": + version "4.14.198" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.198.tgz#4d27465257011aedc741a809f1269941fa2c5d4c" + integrity sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg== "@types/long@^4.0.0", "@types/long@^4.0.1": - version "4.0.1" - resolved "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz" - integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== + version "4.0.2" + resolved "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" + integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== + +"@types/mime@*": + version "3.0.1" + resolved "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== "@types/mime@^1": version "1.3.2" @@ -2845,24 +3157,24 @@ "@types/minimatch@^3.0.3": version "3.0.5" - resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz" + resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/minimist@^1.2.0": version "1.2.2" - resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz" + resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== -"@types/morgan@1.9.2": - version "1.9.2" - resolved "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.2.tgz#450f958a4d3fb0694a3ba012b09c8106f9a2885e" - integrity sha512-edtGMEdit146JwwIeyQeHHg9yID4WSolQPxpEorHmN3KuytuCHyn2ELNr5Uxy8SerniFbbkmgKMrGM933am5BQ== +"@types/morgan@1.9.5": + version "1.9.5" + resolved "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.5.tgz#dda7388af1e67863f9fa4496d6d63f6f53334306" + integrity sha512-5TgfIWm0lcTGnbCZExwc19dCOMOMmAiiBZQj8Ko3NRxsVDgRxf+AEGRQTqNVA5Yh2xfdWp4clbAEMbYP+jkOqg== dependencies: "@types/node" "*" "@types/ms@*": version "0.7.31" - resolved "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz" + resolved "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== "@types/ngeohash@0.6.4": @@ -2871,75 +3183,75 @@ integrity sha512-rr20mmx41OkWx4q5du2dv2sESR/6xH2tzScUQXwO8SiaQWa6PYTuan1nqBtA76FR9qkVfZY7nwQwZNC9StX/Ww== "@types/node@*", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0": - version "17.0.25" - resolved "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz" - integrity sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w== + version "20.6.2" + resolved "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz#a065925409f59657022e9063275cd0b9bd7e1b12" + integrity sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw== -"@types/node@17.0.23": - version "17.0.23" - resolved "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da" - integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw== +"@types/node@20.6.1": + version "20.6.1" + resolved "https://registry.npmjs.org/@types/node/-/node-20.6.1.tgz#8b589bba9b2af0128796461a0979764562687e6f" + integrity sha512-4LcJvuXQlv4lTHnxwyHQZ3uR9Zw2j7m1C9DfuwoTFQQP4Pmu04O6IfLYgMmHoOCt0nosItLLZAH+sOrRE0Bo8g== "@types/node@^12.12.54": - version "12.20.48" - resolved "https://registry.npmjs.org/@types/node/-/node-12.20.48.tgz" - integrity sha512-4kxzqkrpwYtn6okJUcb2lfUu9ilnb3yhUOH6qX3nug8D2DupZ2drIkff2yJzYcNJVl3begnlcaBJ7tqiTTzjnQ== + version "12.20.55" + resolved "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== "@types/normalize-package-data@^2.4.0": version "2.4.1" - resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz" + resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== "@types/parse-json@^4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" + resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/pbkdf2@^3.0.0": - version "3.1.0" - resolved "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz" - integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== - dependencies: - "@types/node" "*" +"@types/parsimmon@^1.10.6": + version "1.10.6" + resolved "https://registry.npmjs.org/@types/parsimmon/-/parsimmon-1.10.6.tgz#8fcf95990514d2a7624aa5f630c13bf2427f9cdd" + integrity sha512-FwAQwMRbkhx0J6YELkwIpciVzCcgEqXEbIrIn3a2P5d3kGEHQ3wVhlN3YdVepYP+bZzCYO6OjmD4o9TGOZ40rA== -"@types/prettier@^2.1.1", "@types/prettier@^2.1.5": - version "2.6.3" - resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a" - integrity sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg== +"@types/prettier@^2.1.1": + version "2.7.3" + resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" + integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== "@types/qs@*": - version "6.9.7" - resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + version "6.9.8" + resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz#f2a7de3c107b89b441e071d5472e6b726b4adf45" + integrity sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg== "@types/range-parser@*": version "1.2.4" - resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz" + resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/retry@^0.12.0": - version "0.12.1" - resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz" - integrity sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g== + version "0.12.2" + resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" + integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== -"@types/secp256k1@^4.0.1": - version "4.0.3" - resolved "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz" - integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w== +"@types/semver@^7.0.0", "@types/semver@^7.5.0": + version "7.5.2" + resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.2.tgz#31f6eec1ed7ec23f4f05608d3a2d381df041f564" + integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw== + +"@types/send@*": + version "0.17.1" + resolved "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz#ed4932b8a2a805f1fe362a70f4e62d0ac994e301" + integrity sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q== dependencies: + "@types/mime" "^1" "@types/node" "*" -"@types/semver@^7.0.0": - version "7.3.9" - resolved "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz" - integrity sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ== - "@types/serve-static@*": - version "1.13.10" - resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" - integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + version "1.15.2" + resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz#3e5419ecd1e40e7405d34093f10befb43f63381a" + integrity sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw== dependencies: - "@types/mime" "^1" + "@types/http-errors" "*" + "@types/mime" "*" "@types/node" "*" "@types/stack-utils@^2.0.0": @@ -2948,9 +3260,9 @@ integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== "@types/superagent@*": - version "4.1.15" - resolved "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.15.tgz#63297de457eba5e2bc502a7609426c4cceab434a" - integrity sha512-mu/N4uvfDN2zVQQ5AYJI/g4qxn2bHB6521t1UuH09ShNWjebTqN0ZFuYK9uYjcgmI0dTQEs+Owi1EO6U0OkOZQ== + version "4.1.18" + resolved "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.18.tgz#e8f037d015cb3b55e64dd00c4d07a84be6d16d34" + integrity sha512-LOWgpacIV8GHhrsQU+QMZuomfqXiqzz3ILLkCtKx3Us6AmomFViuzKT9D693QTKgyut2oCytMG8/efOop+DB+w== dependencies: "@types/cookiejar" "*" "@types/node" "*" @@ -2962,34 +3274,27 @@ dependencies: "@types/superagent" "*" -"@types/validator@^13.7.1": - version "13.7.2" - resolved "https://registry.npmjs.org/@types/validator/-/validator-13.7.2.tgz" - integrity sha512-KFcchQ3h0OPQgFirBRPZr5F/sVjxZsOrQHedj3zi8AH3Zv/hOLx2OLR4hxR5HcfoU+33n69ZuOfzthKVdMoTiw== +"@types/validator@^13.7.17": + version "13.11.1" + resolved "https://registry.npmjs.org/@types/validator/-/validator-13.11.1.tgz#6560af76ed54490e68c42f717ab4e742ba7be74b" + integrity sha512-d/MUkJYdOeKycmm75Arql4M5+UuXmf4cHdHKsyw1GcvnNgL6s77UkgSgJ8TE/rI5PYsnwYq5jkcWBLuN/MpQ1A== "@types/verror@^1.10.4": - version "1.10.5" - resolved "https://registry.npmjs.org/@types/verror/-/verror-1.10.5.tgz" - integrity sha512-9UjMCHK5GPgQRoNbqdLIAvAy0EInuiqbW0PBMtVP6B5B2HQJlvoJHM+KodPZMEjOa5VkSc+5LH7xy+cUzQdmHw== - -"@types/websocket@^1.0.4": - version "1.0.5" - resolved "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.5.tgz" - integrity sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ== - dependencies: - "@types/node" "*" + version "1.10.6" + resolved "https://registry.npmjs.org/@types/verror/-/verror-1.10.6.tgz#3e600c62d210c5826460858f84bcbb65805460bb" + integrity sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ== "@types/ws@^7.4.4": version "7.4.7" - resolved "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz" + resolved "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== dependencies: "@types/node" "*" "@types/ws@^8.0.0": - version "8.5.3" - resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz" - integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== + version "8.5.5" + resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" + integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== dependencies: "@types/node" "*" @@ -3005,176 +3310,101 @@ dependencies: "@types/yargs-parser" "*" -"@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== +"@types/yargs@^17.0.8": + version "17.0.24" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.19.0.tgz#9608a4b6d0427104bccf132f058cba629a6553c0" - integrity sha512-w59GpFqDYGnWFim9p6TGJz7a3qWeENJuAKCqjGSx+Hq/bwq3RZwXYqy98KIfN85yDqz9mq6QXiY5h0FjGQLyEg== +"@typescript-eslint/eslint-plugin@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz#ed2a38867190f8a688af85ad7c8a74670b8b3675" + integrity sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag== + dependencies: + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.7.0" + "@typescript-eslint/type-utils" "6.7.0" + "@typescript-eslint/utils" "6.7.0" + "@typescript-eslint/visitor-keys" "6.7.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/parser@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.0.tgz#332fe9c7ecf6783d3250b4c8a960bd4af0995807" + integrity sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng== dependencies: - "@typescript-eslint/scope-manager" "5.19.0" - "@typescript-eslint/type-utils" "5.19.0" - "@typescript-eslint/utils" "5.19.0" - debug "^4.3.2" - functional-red-black-tree "^1.0.1" - ignore "^5.1.8" - regexpp "^3.2.0" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/eslint-plugin@5.22.0": - version "5.22.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.22.0.tgz#7b52a0de2e664044f28b36419210aea4ab619e2a" - integrity sha512-YCiy5PUzpAeOPGQ7VSGDEY2NeYUV1B0swde2e0HzokRsHBYjSdF6DZ51OuRZxVPHx0032lXGLvOMls91D8FXlg== - dependencies: - "@typescript-eslint/scope-manager" "5.22.0" - "@typescript-eslint/type-utils" "5.22.0" - "@typescript-eslint/utils" "5.22.0" - debug "^4.3.2" - functional-red-black-tree "^1.0.1" - ignore "^5.1.8" - regexpp "^3.2.0" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/parser@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.19.0.tgz#05e587c1492868929b931afa0cb5579b0f728e75" - integrity sha512-yhktJjMCJX8BSBczh1F/uY8wGRYrBeyn84kH6oyqdIJwTGKmzX5Qiq49LRQ0Jh0LXnWijEziSo6BRqny8nqLVQ== - dependencies: - "@typescript-eslint/scope-manager" "5.19.0" - "@typescript-eslint/types" "5.19.0" - "@typescript-eslint/typescript-estree" "5.19.0" - debug "^4.3.2" - -"@typescript-eslint/parser@5.22.0": - version "5.22.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.22.0.tgz#7bedf8784ef0d5d60567c5ba4ce162460e70c178" - integrity sha512-piwC4krUpRDqPaPbFaycN70KCP87+PC5WZmrWs+DlVOxxmF+zI6b6hETv7Quy4s9wbkV16ikMeZgXsvzwI3icQ== - dependencies: - "@typescript-eslint/scope-manager" "5.22.0" - "@typescript-eslint/types" "5.22.0" - "@typescript-eslint/typescript-estree" "5.22.0" - debug "^4.3.2" - -"@typescript-eslint/scope-manager@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.19.0.tgz#97e59b0bcbcb54dbcdfba96fc103b9020bbe9cb4" - integrity sha512-Fz+VrjLmwq5fbQn5W7cIJZ066HxLMKvDEmf4eu1tZ8O956aoX45jAuBB76miAECMTODyUxH61AQM7q4/GOMQ5g== - dependencies: - "@typescript-eslint/types" "5.19.0" - "@typescript-eslint/visitor-keys" "5.19.0" - -"@typescript-eslint/scope-manager@5.22.0": - version "5.22.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.22.0.tgz#590865f244ebe6e46dc3e9cab7976fc2afa8af24" - integrity sha512-yA9G5NJgV5esANJCO0oF15MkBO20mIskbZ8ijfmlKIvQKg0ynVKfHZ15/nhAJN5m8Jn3X5qkwriQCiUntC9AbA== - dependencies: - "@typescript-eslint/types" "5.22.0" - "@typescript-eslint/visitor-keys" "5.22.0" + "@typescript-eslint/scope-manager" "6.7.0" + "@typescript-eslint/types" "6.7.0" + "@typescript-eslint/typescript-estree" "6.7.0" + "@typescript-eslint/visitor-keys" "6.7.0" + debug "^4.3.4" -"@typescript-eslint/type-utils@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.19.0.tgz#80f2125b0dfe82494bbae1ea99f1c0186d420282" - integrity sha512-O6XQ4RI4rQcBGshTQAYBUIGsKqrKeuIOz9v8bckXZnSeXjn/1+BDZndHLe10UplQeJLXDNbaZYrAytKNQO2T4Q== +"@typescript-eslint/scope-manager@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.0.tgz#6b3c22187976e2bf5ed0dc0d9095f1f2cbd1d106" + integrity sha512-lAT1Uau20lQyjoLUQ5FUMSX/dS07qux9rYd5FGzKz/Kf8W8ccuvMyldb8hadHdK/qOI7aikvQWqulnEq2nCEYA== dependencies: - "@typescript-eslint/utils" "5.19.0" - debug "^4.3.2" - tsutils "^3.21.0" + "@typescript-eslint/types" "6.7.0" + "@typescript-eslint/visitor-keys" "6.7.0" -"@typescript-eslint/type-utils@5.22.0": - version "5.22.0" - resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.22.0.tgz#0c0e93b34210e334fbe1bcb7250c470f4a537c19" - integrity sha512-iqfLZIsZhK2OEJ4cQ01xOq3NaCuG5FQRKyHicA3xhZxMgaxQazLUHbH/B2k9y5i7l3+o+B5ND9Mf1AWETeMISA== +"@typescript-eslint/type-utils@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.0.tgz#21a013d4c7f96255f5e64ac59fb41301d1e052ba" + integrity sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg== dependencies: - "@typescript-eslint/utils" "5.22.0" - debug "^4.3.2" - tsutils "^3.21.0" - -"@typescript-eslint/types@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.19.0.tgz#12d3d600d754259da771806ee8b2c842d3be8d12" - integrity sha512-zR1ithF4Iyq1wLwkDcT+qFnhs8L5VUtjgac212ftiOP/ZZUOCuuF2DeGiZZGQXGoHA50OreZqLH5NjDcDqn34w== + "@typescript-eslint/typescript-estree" "6.7.0" + "@typescript-eslint/utils" "6.7.0" + debug "^4.3.4" + ts-api-utils "^1.0.1" -"@typescript-eslint/types@5.22.0": - version "5.22.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.22.0.tgz#50a4266e457a5d4c4b87ac31903b28b06b2c3ed0" - integrity sha512-T7owcXW4l0v7NTijmjGWwWf/1JqdlWiBzPqzAWhobxft0SiEvMJB56QXmeCQjrPuM8zEfGUKyPQr/L8+cFUBLw== +"@typescript-eslint/types@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz#8de8ba9cafadc38e89003fe303e219c9250089ae" + integrity sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q== -"@typescript-eslint/typescript-estree@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.19.0.tgz#fc987b8f62883f9ea6a5b488bdbcd20d33c0025f" - integrity sha512-dRPuD4ocXdaE1BM/dNR21elSEUPKaWgowCA0bqJ6YbYkvtrPVEvZ+zqcX5a8ECYn3q5iBSSUcBBD42ubaOp0Hw== +"@typescript-eslint/typescript-estree@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.0.tgz#20ce2801733bd46f02cc0f141f5b63fbbf2afb63" + integrity sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ== dependencies: - "@typescript-eslint/types" "5.19.0" - "@typescript-eslint/visitor-keys" "5.19.0" - debug "^4.3.2" - globby "^11.0.4" + "@typescript-eslint/types" "6.7.0" + "@typescript-eslint/visitor-keys" "6.7.0" + debug "^4.3.4" + globby "^11.1.0" is-glob "^4.0.3" - semver "^7.3.5" - tsutils "^3.21.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" -"@typescript-eslint/typescript-estree@5.22.0": - version "5.22.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.22.0.tgz#e2116fd644c3e2fda7f4395158cddd38c0c6df97" - integrity sha512-EyBEQxvNjg80yinGE2xdhpDYm41so/1kOItl0qrjIiJ1kX/L/L8WWGmJg8ni6eG3DwqmOzDqOhe6763bF92nOw== +"@typescript-eslint/utils@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.0.tgz#61b6f1f1b82ad529abfcee074d21764e880886fb" + integrity sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.7.0" + "@typescript-eslint/types" "6.7.0" + "@typescript-eslint/typescript-estree" "6.7.0" + semver "^7.5.4" + +"@typescript-eslint/visitor-keys@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz#34140ac76dfb6316d17012e4469acf3366ad3f44" + integrity sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ== dependencies: - "@typescript-eslint/types" "5.22.0" - "@typescript-eslint/visitor-keys" "5.22.0" - debug "^4.3.2" - globby "^11.0.4" - is-glob "^4.0.3" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/utils@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.19.0.tgz#fe87f1e3003d9973ec361ed10d36b4342f1ded1e" - integrity sha512-ZuEckdupXpXamKvFz/Ql8YnePh2ZWcwz7APICzJL985Rp5C2AYcHO62oJzIqNhAMtMK6XvrlBTZeNG8n7gS3lQ== - dependencies: - "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.19.0" - "@typescript-eslint/types" "5.19.0" - "@typescript-eslint/typescript-estree" "5.19.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - -"@typescript-eslint/utils@5.22.0": - version "5.22.0" - resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.22.0.tgz#1f2c4897e2cf7e44443c848a13c60407861babd8" - integrity sha512-HodsGb037iobrWSUMS7QH6Hl1kppikjA1ELiJlNSTYf/UdMEwzgj0WIp+lBNb6WZ3zTwb0tEz51j0Wee3iJ3wQ== - dependencies: - "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.22.0" - "@typescript-eslint/types" "5.22.0" - "@typescript-eslint/typescript-estree" "5.22.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - -"@typescript-eslint/visitor-keys@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.19.0.tgz#c84ebc7f6c744707a361ca5ec7f7f64cd85b8af6" - integrity sha512-Ym7zZoMDZcAKWsULi2s7UMLREdVQdScPQ/fKWMYefarCztWlHPFVJo8racf8R0Gc8FAEJ2eD4of8As1oFtnQlQ== - dependencies: - "@typescript-eslint/types" "5.19.0" - eslint-visitor-keys "^3.0.0" - -"@typescript-eslint/visitor-keys@5.22.0": - version "5.22.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.22.0.tgz#f49c0ce406944ffa331a1cfabeed451ea4d0909c" - integrity sha512-DbgTqn2Dv5RFWluG88tn0pP6Ex0ROF+dpDO1TNNZdRtLjUr6bdznjA6f/qNqJLjd2PgguAES2Zgxh/JzwzETDg== - dependencies: - "@typescript-eslint/types" "5.22.0" - eslint-visitor-keys "^3.0.0" + "@typescript-eslint/types" "6.7.0" + eslint-visitor-keys "^3.4.1" "@uniswap/sdk@3.0.3": version "3.0.3" - resolved "https://registry.npmjs.org/@uniswap/sdk/-/sdk-3.0.3.tgz" + resolved "https://registry.npmjs.org/@uniswap/sdk/-/sdk-3.0.3.tgz#8201c7c72215d0030cb99acc7e661eff895c18a9" integrity sha512-t4s8bvzaCFSiqD2qfXIm3rWhbdnXp+QjD3/mRaeVDHK7zWevs6RGEb1ohMiNgOCTZANvBayb4j8p+XFdnMBadQ== dependencies: "@uniswap/v2-core" "^1.0.0" @@ -3187,12 +3417,12 @@ "@uniswap/v2-core@^1.0.0": version "1.0.1" - resolved "https://registry.npmjs.org/@uniswap/v2-core/-/v2-core-1.0.1.tgz" + resolved "https://registry.npmjs.org/@uniswap/v2-core/-/v2-core-1.0.1.tgz#af8f508bf183204779938969e2e54043e147d425" integrity sha512-MtybtkUPSyysqLY2U210NBDeCHX+ltHt3oADGdjqoThZaFRDKwM6k1Nb3F0A3hk5hwuQvytFWhrWHOEq6nVJ8Q== "@urql/core@2.4.4", "@urql/core@>=2.3.6": version "2.4.4" - resolved "https://registry.npmjs.org/@urql/core/-/core-2.4.4.tgz" + resolved "https://registry.npmjs.org/@urql/core/-/core-2.4.4.tgz#29f1d03cc439134259761e70a78ae20302c3d7fe" integrity sha512-TD+OS7jG1Ts6QkpU0TZ85i/vu40r71GF0QQFDhnWFtgkHcNwnpkIwWBMa72AR3j2imBTPpk61e/xb39uM/t37A== dependencies: "@graphql-typed-document-node/core" "^3.1.1" @@ -3200,37 +3430,68 @@ "@urql/exchange-execute@1.2.2": version "1.2.2" - resolved "https://registry.npmjs.org/@urql/exchange-execute/-/exchange-execute-1.2.2.tgz" + resolved "https://registry.npmjs.org/@urql/exchange-execute/-/exchange-execute-1.2.2.tgz#41a68500c44d12c4c1e6a80dfc77cefccde15687" integrity sha512-KebdnKWMKI1NkRtIxp8YIouynOaFnhcdaMNCcJEtp+kmY4vGZUgdxT/SIzTPXXYJvk5G2aiQ/JMr97I+wM/EHA== dependencies: "@urql/core" ">=2.3.6" wonka "^4.0.14" +"@whatwg-node/events@^0.1.0": + version "0.1.1" + resolved "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz#0ca718508249419587e130da26d40e29d99b5356" + integrity sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w== + +"@whatwg-node/fetch@^0.9.0": + version "0.9.13" + resolved "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.13.tgz#1d084cd546b9cd425ae89cbb1252a3e47a9a2e1c" + integrity sha512-PPtMwhjtS96XROnSpowCQM85gCUG2m7AXZFw0PZlGbhzx2GK7f2iOXilfgIJ0uSlCuuGbOIzfouISkA7C4FJOw== + dependencies: + "@whatwg-node/node-fetch" "^0.4.17" + urlpattern-polyfill "^9.0.0" + +"@whatwg-node/node-fetch@^0.4.17": + version "0.4.19" + resolved "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.4.19.tgz#29c72ff65a8e450949238612ff17a3d3717736d3" + integrity sha512-AW7/m2AuweAoSXmESrYQr/KBafueScNbn2iNO0u6xFr2JZdPmYsSm5yvAXYk6yDLv+eDmSSKrf7JnFZ0CsJIdA== + dependencies: + "@whatwg-node/events" "^0.1.0" + busboy "^1.6.0" + fast-querystring "^1.1.1" + fast-url-parser "^1.1.3" + tslib "^2.3.1" + "@wry/context@^0.6.0": version "0.6.1" - resolved "https://registry.npmjs.org/@wry/context/-/context-0.6.1.tgz" + resolved "https://registry.npmjs.org/@wry/context/-/context-0.6.1.tgz#c3c29c0ad622adb00f6a53303c4f965ee06ebeb2" integrity sha512-LOmVnY1iTU2D8tv4Xf6MVMZZ+juIJ87Kt/plMijjN20NMAXGmH4u8bS1t0uT74cZ5gwpocYueV58YwyI8y+GKw== dependencies: tslib "^2.3.0" +"@wry/context@^0.7.0": + version "0.7.3" + resolved "https://registry.npmjs.org/@wry/context/-/context-0.7.3.tgz#240f6dfd4db5ef54f81f6597f6714e58d4f476a1" + integrity sha512-Nl8WTesHp89RF803Se9X3IiHjdmLBrIvPMaJkl+rKVJAYyPsz1TEUbu89943HpvujtSJgDUx9W4vZw3K1Mr3sA== + dependencies: + tslib "^2.3.0" + "@wry/equality@^0.1.2": version "0.1.11" - resolved "https://registry.npmjs.org/@wry/equality/-/equality-0.1.11.tgz" + resolved "https://registry.npmjs.org/@wry/equality/-/equality-0.1.11.tgz#35cb156e4a96695aa81a9ecc4d03787bc17f1790" integrity sha512-mwEVBDUVODlsQQ5dfuLUS5/Tf7jqUKyhKYHmVi4fPB6bDMOfWvUPJmKgS1Z7Za/sOI3vzWt4+O7yCiL/70MogA== dependencies: tslib "^1.9.3" "@wry/equality@^0.5.0": - version "0.5.2" - resolved "https://registry.npmjs.org/@wry/equality/-/equality-0.5.2.tgz" - integrity sha512-oVMxbUXL48EV/C0/M7gLVsoK6qRHPS85x8zECofEZOVvxGmIPLA9o5Z27cc2PoAyZz1S2VoM2A7FLAnpfGlneA== + version "0.5.6" + resolved "https://registry.npmjs.org/@wry/equality/-/equality-0.5.6.tgz#cd4a533c72c3752993ab8cbf682d3d20e3cb601e" + integrity sha512-D46sfMTngaYlrH+OspKf8mIJETntFnf6Hsjb0V41jAXJ7Bx2kB8Rv8RCUujuVWYttFtHkUNp7g+FwxNQAr6mXA== dependencies: tslib "^2.3.0" "@wry/trie@^0.3.0": - version "0.3.1" - resolved "https://registry.npmjs.org/@wry/trie/-/trie-0.3.1.tgz" - integrity sha512-WwB53ikYudh9pIorgxrkHKrQZcCqNM/Q/bDzZBffEaGUKGuHrRb3zZUT9Sh2qw9yogC7SsdRmQ1ER0pqvd3bfw== + version "0.3.2" + resolved "https://registry.npmjs.org/@wry/trie/-/trie-0.3.2.tgz#a06f235dc184bd26396ba456711f69f8c35097e6" + integrity sha512-yRTyhWSls2OY/pYLfwff867r8ekooZ4UI+/gxot5Wj8EFwSf2rG+n+Mo/6LoLQm1TKA4GRj2+LCpbfS937dClQ== dependencies: tslib "^2.3.0" @@ -3239,10 +3500,10 @@ resolved "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== -"@yarnpkg/parsers@^3.0.0-rc.18": - version "3.0.0-rc.32" - resolved "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.32.tgz#0aef0bd1b9e9954173c01a7cbd35f98765e39e7d" - integrity sha512-Sz2g88b3iAu2jpMnhtps2bRX2GAAOvanOxGcVi+o7ybGjLetxK23o2cHskXKypvXxtZTsJegel5pUWSPpYphww== +"@yarnpkg/parsers@3.0.0-rc.46": + version "3.0.0-rc.46" + resolved "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz#03f8363111efc0ea670e53b0282cd3ef62de4e01" + integrity sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q== dependencies: js-yaml "^3.10.0" tslib "^2.4.0" @@ -3256,112 +3517,87 @@ JSONStream@^1.0.4, JSONStream@^1.3.5: version "1.3.5" - resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" + resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== dependencies: jsonparse "^1.2.0" through ">=2.2.7 <3" -abab@^2.0.3, abab@^2.0.5: - version "2.0.6" - resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== - abbrev@1, abbrev@^1.0.0: version "1.1.1" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" + resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== abort-controller@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" + resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== dependencies: event-target-shim "^5.0.0" abstract-logging@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz" + resolved "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz#6b0c371df212db7129b57d2e7fcf282b8bf1c839" integrity sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA== accepts@^1.3.7, accepts@~1.3.8: version "1.3.8" - resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: mime-types "~2.1.34" negotiator "0.6.3" -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.2.4, acorn@^8.4.1, acorn@^8.7.1: - version "8.7.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== +acorn@^8.4.1, acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== add-stream@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz" - integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= + resolved "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" + integrity sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ== aes-js@3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz" - integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= + resolved "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== agent-base@6, agent-base@^6.0.2: version "6.0.2" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" agentkeepalive@^4.2.1: - version "4.2.1" - resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" - integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== + version "4.5.0" + resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== dependencies: - debug "^4.1.0" - depd "^1.1.2" humanize-ms "^1.2.1" aggregate-error@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" + resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.11.0, ajv@^6.12.4, ajv@^6.12.6: +ajv@^6.11.0, ajv@^6.12.4, ajv@^6.12.6: version "6.12.6" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -3370,9 +3606,9 @@ ajv@^6.10.0, ajv@^6.11.0, ajv@^6.12.4, ajv@^6.12.6: uri-js "^4.2.2" ajv@^8.0.1, ajv@^8.1.0: - version "8.11.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz" - integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -3381,7 +3617,7 @@ ajv@^8.0.1, ajv@^8.1.0: ansi-colors@^3.2.1: version "3.2.4" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== ansi-colors@^4.1.1: @@ -3391,31 +3627,36 @@ ansi-colors@^4.1.1: ansi-escapes@^4.2.1: version "4.3.2" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" ansi-regex@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" @@ -3425,15 +3666,12 @@ ansi-styles@^5.0.0: resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -anymatch@^3.0.3: - version "3.1.2" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -anymatch@~3.1.2: +anymatch@^3.0.3: version "3.1.3" resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -3442,15 +3680,15 @@ anymatch@~3.1.2: picomatch "^2.0.4" apisauce@^2.0.1: - version "2.1.5" - resolved "https://registry.npmjs.org/apisauce/-/apisauce-2.1.5.tgz" - integrity sha512-bkMlz0ZUnyS8vDigej9UBYo5dne9/bQrkgIiIkGaiDHF6e5OxhYRLJDYu65V/Ox86tmWVwepIntAoTmk4Db0Hg== + version "2.1.6" + resolved "https://registry.npmjs.org/apisauce/-/apisauce-2.1.6.tgz#94887f335bf3d735305fc895c8a191c9c2608a7f" + integrity sha512-MdxR391op/FucS2YQRfB/NMRyCnHEPDd4h17LRIuVYi0BpGmMhpxc0shbOpfs5ahABuBEffNCGal5EcsydbBWg== dependencies: axios "^0.21.4" apollo-link-http-common@^0.2.16: version "0.2.16" - resolved "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.16.tgz" + resolved "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.16.tgz#756749dafc732792c8ca0923f9a40564b7c59ecc" integrity sha512-2tIhOIrnaF4UbQHf7kjeQA/EmSorB7+HyJIIrUjJOKBgnXwuexi8aMecRlqTIDWcyVXCeqLhUnztMa6bOH/jTg== dependencies: apollo-link "^1.2.14" @@ -3459,7 +3697,7 @@ apollo-link-http-common@^0.2.16: apollo-link-http@1.5.17: version "1.5.17" - resolved "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.17.tgz" + resolved "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.17.tgz#499e9f1711bf694497f02c51af12d82de5d8d8ba" integrity sha512-uWcqAotbwDEU/9+Dm9e1/clO7hTB2kQ/94JYcGouBVLjoKmTeJTUPQKcJGpPwUjZcSqgYicbFqQSoJIW0yrFvg== dependencies: apollo-link "^1.2.14" @@ -3468,7 +3706,7 @@ apollo-link-http@1.5.17: apollo-link@^1.2.14: version "1.2.14" - resolved "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.14.tgz" + resolved "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.14.tgz#3feda4b47f9ebba7f4160bef8b977ba725b684d9" integrity sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg== dependencies: apollo-utilities "^1.3.0" @@ -3478,7 +3716,7 @@ apollo-link@^1.2.14: apollo-utilities@^1.3.0: version "1.3.4" - resolved "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz" + resolved "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz#6129e438e8be201b6c55b0f13ce49d2c7175c9cf" integrity sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig== dependencies: "@wry/equality" "^0.1.2" @@ -3488,22 +3726,22 @@ apollo-utilities@^1.3.0: app-module-path@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz" - integrity sha1-ZBqlXft9am8KgUHEucCqULbCTdU= + resolved "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" + integrity sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ== "aproba@^1.0.3 || ^2.0.0", aproba@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz" + resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== archy@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz" - integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + resolved "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== are-we-there-yet@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz" + resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== dependencies: delegates "^1.0.0" @@ -3524,7 +3762,7 @@ arg@^4.1.0: argparse@^1.0.7, argparse@~1.0.9: version "1.0.10" - resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" @@ -3546,47 +3784,47 @@ array-back@^4.0.1, array-back@^4.0.2: array-differ@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz" + resolved "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== array-flatten@1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== array-ify@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz" - integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= + resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" + integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== array-union@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== arrify@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" - integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== arrify@^2.0.0, arrify@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz" + resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== asap@^2.0.0: version "2.0.6" - resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" - integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== assert-plus@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async@^3.2.3: @@ -3596,22 +3834,22 @@ async@^3.2.3: asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== at-least-node@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" + resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== atomic-sleep@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz" + resolved "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== avvio@^7.1.2: version "7.2.5" - resolved "https://registry.npmjs.org/avvio/-/avvio-7.2.5.tgz" + resolved "https://registry.npmjs.org/avvio/-/avvio-7.2.5.tgz#65ba255f10b0bea7ac6eded71a5344cd88f5de19" integrity sha512-AOhBxyLVdpOad3TujtC9kL/9r3HnTkxwQ5ggOsYrvvZP1cCFvzHWJd5XxZDFuTn+IN8vkKSG5SEJrd27vCSbeA== dependencies: archy "^1.0.0" @@ -3621,37 +3859,36 @@ avvio@^7.1.2: axios@0.26.1: version "0.26.1" - resolved "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz" + resolved "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== dependencies: follow-redirects "^1.14.8" axios@^0.21.4: version "0.21.4" - resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" + resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== dependencies: follow-redirects "^1.14.0" axios@^1.0.0: - version "1.2.1" - resolved "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz#44cf04a3c9f0c2252ebd85975361c026cb9f864a" - integrity sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A== + version "1.5.0" + resolved "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz#f02e4af823e2e46a9768cfc74691fdd0517ea267" + integrity sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" proxy-from-env "^1.1.0" -babel-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" - integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== dependencies: - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/transform" "^29.7.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.5.1" + babel-preset-jest "^29.6.3" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -3667,14 +3904,14 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" - integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" + "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" babel-preset-current-node-syntax@^1.0.0: @@ -3695,67 +3932,62 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" - integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== dependencies: - babel-plugin-jest-hoist "^27.5.1" + babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" -backo2@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz" - integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= - balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base-x@^3.0.2, base-x@^3.0.6: +base-x@^3.0.6: version "3.0.9" - resolved "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz" + resolved "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== dependencies: safe-buffer "^5.0.1" base-x@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz" + resolved "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== base64-js@^1.3.0, base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== basic-auth@~2.0.0, basic-auth@~2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz" + resolved "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== dependencies: safe-buffer "5.1.2" bech32@1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz" + resolved "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== before-after-hook@^2.1.0, before-after-hook@^2.2.0: - version "2.2.2" - resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz" - integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== + version "2.2.3" + resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" + integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== big.js@^5.2.2: version "5.2.2" - resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" + resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== bignumber.js@^9.0.0, bignumber.js@^9.0.1: - version "9.0.2" - resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz" - integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== + version "9.1.2" + resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== bin-links@^3.0.0: version "3.0.3" @@ -3769,22 +4001,17 @@ bin-links@^3.0.0: rimraf "^3.0.0" write-file-atomic "^4.0.0" -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - bindings@^1.2.1: version "1.5.0" - resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz" + resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== dependencies: file-uri-to-path "1.0.0" -bintrees@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz" - integrity sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ= +bintrees@1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" + integrity sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw== bl@^4.0.3, bl@^4.1.0: version "4.1.0" @@ -3795,66 +4022,60 @@ bl@^4.0.3, bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -blakejs@^1.1.0: - version "1.2.1" - resolved "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz" - integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== - bn.js@4.11.6: version "4.11.6" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz" - integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= + resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA== bn.js@^4.11.9: version "4.12.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.1.2, bn.js@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== - bn.js@^5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -body-parser@1.19.1: - version "1.19.1" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz" - integrity sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA== +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== dependencies: - bytes "3.1.1" + bytes "3.1.2" content-type "~1.0.4" debug "2.6.9" - depd "~1.1.2" - http-errors "1.8.1" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.9.6" - raw-body "2.4.2" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" type-is "~1.6.18" + unpipe "1.0.0" -body-parser@1.19.2: - version "1.19.2" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz" - integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== +body-parser@1.20.2: + version "1.20.2" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== dependencies: bytes "3.1.2" - content-type "~1.0.4" + content-type "~1.0.5" debug "2.6.9" - depd "~1.1.2" - http-errors "1.8.1" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.9.7" - raw-body "2.4.3" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" type-is "~1.6.18" + unpipe "1.0.0" brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" @@ -3867,45 +4088,27 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2, braces@~3.0.2: +braces@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" brorand@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - -browserify-aes@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" + resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== -browserslist@^4.20.2: - version "4.20.4" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.20.4.tgz#98096c9042af689ee1e0271333dbc564b8ce4477" - integrity sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw== +browserslist@^4.21.9: + version "4.21.10" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" + integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== dependencies: - caniuse-lite "^1.0.30001349" - electron-to-chromium "^1.4.147" - escalade "^3.1.1" - node-releases "^2.0.5" - picocolors "^1.0.0" + caniuse-lite "^1.0.30001517" + electron-to-chromium "^1.4.477" + node-releases "^2.0.13" + update-browserslist-db "^1.0.11" bs-logger@0.x: version "0.2.6" @@ -3914,29 +4117,13 @@ bs-logger@0.x: dependencies: fast-json-stable-stringify "2.x" -bs58@4.0.1, bs58@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz" - integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= - dependencies: - base-x "^3.0.2" - bs58@5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz" + resolved "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ== dependencies: base-x "^4.0.0" -bs58check@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz" - integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== - dependencies: - bs58 "^4.0.0" - create-hash "^1.1.0" - safe-buffer "^5.1.2" - bser@2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -3946,30 +4133,25 @@ bser@2.1.1: buffer-equal-constant-time@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" - integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== buffer-from@^1.0.0: version "1.1.2" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer-reverse@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz" - integrity sha1-SSg8jvpvkBvAH6MwTQYCeXGuL2A= + resolved "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60" + integrity sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg== buffer-writer@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz" + resolved "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= - -buffer@^5.5.0, buffer@^5.7.0: +buffer@^5.5.0: version "5.7.1" resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -3979,8 +4161,8 @@ buffer@^5.5.0, buffer@^5.7.0: builtins@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz" - integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= + resolved "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" + integrity sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ== builtins@^5.0.0: version "5.0.1" @@ -3989,19 +4171,21 @@ builtins@^5.0.0: dependencies: semver "^7.0.0" +busboy@^1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + byte-size@^7.0.0: version "7.0.1" - resolved "https://registry.npmjs.org/byte-size/-/byte-size-7.0.1.tgz" + resolved "https://registry.npmjs.org/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== -bytes@3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz" - integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg== - bytes@3.1.2: version "3.1.2" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: @@ -4028,9 +4212,27 @@ cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: tar "^6.1.11" unique-filename "^2.0.0" +cacache@^17.0.0: + version "17.1.4" + resolved "https://registry.npmjs.org/cacache/-/cacache-17.1.4.tgz#b3ff381580b47e85c6e64f801101508e26604b35" + integrity sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^7.7.1" + minipass "^7.0.3" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" + call-bind@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" @@ -4038,12 +4240,12 @@ call-bind@^1.0.0: callsites@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase-keys@^6.2.2: version "6.2.2" - resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== dependencies: camelcase "^5.3.1" @@ -4052,7 +4254,7 @@ camelcase-keys@^6.2.2: camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.2.0: @@ -4060,14 +4262,14 @@ camelcase@^6.2.0: resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001349: - version "1.0.30001352" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001352.tgz#cc6f5da3f983979ad1e2cdbae0505dccaa7c6a12" - integrity sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA== +caniuse-lite@^1.0.30001517: + version "1.0.30001538" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001538.tgz#9dbc6b9af1ff06b5eb12350c2012b3af56744f3f" + integrity sha512-HWJnhnID+0YMtGlzcp3T9drmBJUVDchPJ08tpUGFLs9CYlwWPH2uLgpHn8fND5pCgXVtnGS3H4QR9XLMHVNkHw== cargo-cp-artifact@0.1.6: version "0.1.6" - resolved "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.6.tgz" + resolved "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.6.tgz#df1bc9dad036ae0f4230639a869182e1d5850f89" integrity sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg== cargo-cp-artifact@0.1.7: @@ -4075,25 +4277,17 @@ cargo-cp-artifact@0.1.7: resolved "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.7.tgz#1181b9d6e71f00f17c068c05e3cd1b0864783341" integrity sha512-pxEV9p1on8vu3BOKstVisF9TwMyGKCBRvzaVpQHuU2sLULCKrn3MJWx/4XlNzmG6xNCTPf78DJ7WCGgr2mOzjg== -chalk@4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chalk@4.1.2, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^2.0.0, chalk@^2.4.2: +chalk@^2.4.2: version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -4102,7 +4296,7 @@ chalk@^2.0.0, chalk@^2.4.2: chalk@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz" + resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== dependencies: ansi-styles "^4.1.0" @@ -4115,77 +4309,54 @@ char-regex@^1.0.2: chardet@^0.7.0: version "0.7.0" - resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" + resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@^3.5.1: - version "3.5.3" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - chownr@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" + resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== ci-info@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.3.1" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.1.tgz#58331f6f472a25fe3a50a351ae3052936c2c7f32" - integrity sha512-SXgeMX9VwDe7iFFaEWkA5AstuER9YKqy4EhHqr4DVqkwmD9rpVimkMKWHdjn30Ja45txyjhSn63lVX69eVCckg== - -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" + version "3.8.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== cjs-module-lexer@^1.0.0: - version "1.2.2" - resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" - integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + version "1.2.3" + resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== clean-stack@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" + resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== cli-cursor@3.1.0, cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" -cli-spinners@2.6.1, cli-spinners@^2.2.0: +cli-spinners@2.6.1: version "2.6.1" resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== -cli-spinners@^2.5.0: - version "2.7.0" - resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== +cli-spinners@^2.2.0, cli-spinners@^2.5.0: + version "2.9.1" + resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz#9c0b9dad69a6d47cbb4333c14319b060ed395a35" + integrity sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ== cli-table3@~0.5.0: version "0.5.1" - resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz" + resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== dependencies: object-assign "^4.1.0" @@ -4195,12 +4366,12 @@ cli-table3@~0.5.0: cli-width@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^7.0.2: version "7.0.4" - resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" + resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" @@ -4218,7 +4389,7 @@ cliui@^8.0.1: clone-deep@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== dependencies: is-plain-object "^2.0.4" @@ -4227,8 +4398,8 @@ clone-deep@^4.0.1: clone@^1.0.2: version "1.0.4" - resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== cmd-shim@^5.0.0: version "5.0.0" @@ -4243,32 +4414,32 @@ co@^4.6.0: integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + version "1.0.2" + resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-support@^1.1.2, color-support@^1.1.3: @@ -4278,12 +4449,12 @@ color-support@^1.1.2, color-support@^1.1.3: colors@^1.1.2, colors@^1.3.3: version "1.4.0" - resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz" + resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== colors@~1.2.1: version "1.2.5" - resolved "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz" + resolved "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz#89c7ad9a374bc030df8013241f68136ed8835afc" integrity sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg== columnify@^1.6.0: @@ -4296,7 +4467,7 @@ columnify@^1.6.0: combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" @@ -4328,7 +4499,7 @@ commander@7.2.0: commander@^2.20.3: version "2.20.3" - resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== common-ancestor-path@^1.0.1: @@ -4338,7 +4509,7 @@ common-ancestor-path@^1.0.1: compare-func@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz" + resolved "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== dependencies: array-ify "^1.0.0" @@ -4351,12 +4522,12 @@ component-emitter@^1.3.0: concat-map@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== concat-stream@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz" + resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== dependencies: buffer-from "^1.0.0" @@ -4366,7 +4537,7 @@ concat-stream@^2.0.0: config-chain@^1.1.12: version "1.1.13" - resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz" + resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== dependencies: ini "^1.3.4" @@ -4374,36 +4545,36 @@ config-chain@^1.1.12: console-control-strings@^1.0.0, console-control-strings@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== console-log-level@^1.4.0: version "1.4.1" - resolved "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz" + resolved "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz#9c5a6bb9ef1ef65b05aba83028b0ff894cdf630a" integrity sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ== console-table-printer@^2.11.1: - version "2.11.1" - resolved "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.11.1.tgz#c2dfe56e6343ea5bcfa3701a4be29fe912dbd9c7" - integrity sha512-8LfFpbF/BczoxPwo2oltto5bph8bJkGOATXsg3E9ddMJOGnWJciKHldx2zDj5XIBflaKzPfVCjOTl6tMh7lErg== + version "2.11.2" + resolved "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.11.2.tgz#549757033a7e3cde7e26e030038c9392ce600ee5" + integrity sha512-uuUHie0sfPP542TKGzPFal0W1wo1beuKAqIZdaavcONx8OoqdnJRKjkinbRTOta4FaCa1RcIL+7mMJWX3pQGVg== dependencies: simple-wcswidth "^1.0.1" content-disposition@0.5.4: version "0.5.4" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: safe-buffer "5.2.1" -content-type@^1.0.4, content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== +content-type@^1.0.4, content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== conventional-changelog-angular@^5.0.12: version "5.0.13" - resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz" + resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" integrity sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA== dependencies: compare-func "^2.0.0" @@ -4431,12 +4602,12 @@ conventional-changelog-core@^4.2.4: conventional-changelog-preset-loader@^2.3.4: version "2.3.4" - resolved "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz" + resolved "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== conventional-changelog-writer@^5.0.0: version "5.0.1" - resolved "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz" + resolved "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz#e0757072f045fe03d91da6343c843029e702f359" integrity sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ== dependencies: conventional-commits-filter "^2.0.7" @@ -4451,7 +4622,7 @@ conventional-changelog-writer@^5.0.0: conventional-commits-filter@^2.0.7: version "2.0.7" - resolved "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz" + resolved "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" integrity sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA== dependencies: lodash.ismatch "^4.4.0" @@ -4459,7 +4630,7 @@ conventional-commits-filter@^2.0.7: conventional-commits-parser@^3.2.0: version "3.2.4" - resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz" + resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz#a7d3b77758a202a9b2293d2112a8d8052c740972" integrity sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q== dependencies: JSONStream "^1.0.4" @@ -4471,7 +4642,7 @@ conventional-commits-parser@^3.2.0: conventional-recommended-bump@^6.1.0: version "6.1.0" - resolved "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz" + resolved "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== dependencies: concat-stream "^2.0.0" @@ -4483,41 +4654,49 @@ conventional-recommended-bump@^6.1.0: meow "^8.0.0" q "^1.5.1" -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" +convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== cookie-signature@1.0.6: version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0, cookie@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookie@0.4.2, cookie@^0.4.0, cookie@^0.4.1: +cookie@^0.4.1: version "0.4.2" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -cookiejar@^2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc" - integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ== +cookiejar@^2.1.4: + version "2.1.4" + resolved "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" + integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== core-util-is@1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== core-util-is@~1.0.0: version "1.0.3" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== cors@2.8.5: version "2.8.5" - resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz" + resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== dependencies: object-assign "^4" @@ -4525,7 +4704,7 @@ cors@2.8.5: cosmiconfig@6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== dependencies: "@types/parse-json" "^4.0.0" @@ -4535,9 +4714,9 @@ cosmiconfig@6.0.0: yaml "^1.7.2" cosmiconfig@^7.0.0: - version "7.0.1" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + version "7.1.0" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== dependencies: "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" @@ -4545,174 +4724,126 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.4, create-hmac@^1.1.7: - version "1.1.7" - resolved "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" create-require@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-fetch@3.1.5: - version "3.1.5" - resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== +cross-fetch@4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" + integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== dependencies: - node-fetch "2.6.7" + node-fetch "^2.6.12" cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" which "^2.0.1" -cross-undici-fetch@^0.2.4: - version "0.2.5" - resolved "https://registry.npmjs.org/cross-undici-fetch/-/cross-undici-fetch-0.2.5.tgz" - integrity sha512-6IR+JN6o2UMNj2f3fu0ZVkZeP0h22DRKzq78SiMenkqyBYyGIT1AkZjHkItvh0A80LdsAlWENHUpvapapePucw== - dependencies: - abort-controller "^3.0.0" - form-data-encoder "^1.7.1" - formdata-node "^4.3.1" - node-fetch "^2.6.7" - undici "^5.0.0" - web-streams-polyfill "^3.2.0" - crypto-js@^3.1.9-1: version "3.3.0" - resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz" + resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b" integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== - dependencies: - cssom "~0.3.6" - dargs@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz" + resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== - dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - -dataloader@2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.1.0.tgz" - integrity sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ== +dataloader@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0" + integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== dateformat@^3.0.0: version "3.0.3" - resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz" + resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== debug@2.6.9: version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" debuglog@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz" - integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= + resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== decamelize-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz" - integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= + version "1.1.1" + resolved "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" + integrity sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== dependencies: decamelize "^1.1.0" map-obj "^1.0.0" decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== decimal.js-light@^2.5.0: version "2.5.1" - resolved "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz" + resolved "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== -decimal.js@^10.2.1: - version "10.3.1" - resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" - integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== - dedent@^0.7.0: version "0.7.0" - resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" - integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== + +dedent@^1.0.0: + version "1.5.1" + resolved "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" + integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== deep-extend@~0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-is@^0.1.3, deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.4" resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + version "4.3.1" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + version "1.0.4" + resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== dependencies: clone "^1.0.2" @@ -4723,79 +4854,71 @@ define-lazy-prop@^2.0.0: delay@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz" + resolved "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== delegates@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== depd@2.0.0, depd@~2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -depd@^1.1.2, depd@~1.1.2: +depd@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" + resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== detect-indent@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz" - integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= + resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + integrity sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g== detect-indent@^6.0.0: version "6.1.0" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz" + resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== detect-libc@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz" - integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== + version "2.0.2" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" + integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -dezalgo@1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" - integrity sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ== - dependencies: - asap "^2.0.0" - wrappy "1" - -dezalgo@^1.0.0: +dezalgo@^1.0.0, dezalgo@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz" + resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== dependencies: asap "^2.0.0" wrappy "1" -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== diff@^4.0.1: version "4.0.2" @@ -4804,7 +4927,7 @@ diff@^4.0.1: dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" @@ -4818,26 +4941,19 @@ doctrine@^3.0.0: dom-walk@^0.1.0: version "0.1.2" - resolved "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz" + resolved "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== - dependencies: - webidl-conversions "^5.0.0" - dot-prop@^5.1.0: version "5.3.0" - resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== dependencies: is-obj "^2.0.0" dot-prop@^6.0.0, dot-prop@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== dependencies: is-obj "^2.0.0" @@ -4847,24 +4963,24 @@ dotenv@~10.0.0: resolved "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -dottie@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz" - integrity sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg== +dottie@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz#34564ebfc6ec5e5772272d466424ad5b696484d4" + integrity sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA== -dset@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/dset/-/dset-3.1.1.tgz" - integrity sha512-hYf+jZNNqJBD2GiMYb+5mqOIX4R4RRHXU3qWMWYN+rqcR2/YpRL2bUHr8C8fU+5DNvqYjJ8YvMGSLuVPWU1cNg== +dset@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a" + integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q== duplexer@^0.1.1: version "0.1.2" - resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" + resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== duplexify@^4.0.0, duplexify@^4.1.1, duplexify@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz" + resolved "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0" integrity sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw== dependencies: end-of-stream "^1.4.1" @@ -4872,38 +4988,43 @@ duplexify@^4.0.0, duplexify@^4.1.1, duplexify@^4.1.2: readable-stream "^3.1.1" stream-shift "^1.0.0" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: version "1.0.11" - resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" + resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== dependencies: safe-buffer "^5.0.1" ee-first@1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== ejs@^2.6.1: version "2.7.4" - resolved "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz" + resolved "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== ejs@^3.1.7: - version "3.1.8" - resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b" - integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ== + version "3.1.9" + resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" + integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== dependencies: jake "^10.8.5" -electron-to-chromium@^1.4.147: - version "1.4.154" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.154.tgz#d69c60499fc467a6c59591d29183e520afbc78a1" - integrity sha512-GbV9djOkrnj6xmW+YYVVEI3VCQnJ0pnSTu7TW2JyjKd5cakoiSaG5R4RbEtfaD92GsY10DzbU3GYRe+IOA9kqA== +electron-to-chromium@^1.4.477: + version "1.4.523" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.523.tgz#f82f99243c827df05c26776d49712cb284972df6" + integrity sha512-9AreocSUWnzNtvLcbpng6N+GkXnCcBR80IQkxRC9Dfdyg4gaWNUPBujAHUpKkiUkoSoR9UlhA4zD/IgBklmhzg== -elliptic@6.5.4, elliptic@^6.5.4: +elliptic@6.5.4: version "6.5.4" - resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz" + resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== dependencies: bn.js "^4.11.9" @@ -4916,23 +5037,28 @@ elliptic@6.5.4, elliptic@^6.5.4: emittery@^0.10.0: version "0.10.2" - resolved "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== -emittery@^0.8.1: - version "0.8.1" - resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" - integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + encodeurl@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== encoding@^0.1.13: version "0.1.13" @@ -4943,14 +5069,14 @@ encoding@^0.1.13: end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" - resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" enquirer@2.3.4: version "2.3.4" - resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.4.tgz" + resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.4.tgz#c608f2e1134c7f68c1c9ee056de13f9b31076de9" integrity sha512-pkYrrDZumL2VS6VBGDhqbajCM2xpkUNLuKfGPjfKaSIBKYopQbqEFyrOkRMIb2HDR/rO1kGhEt/5twBwtzKBXw== dependencies: ansi-colors "^3.2.1" @@ -4964,57 +5090,57 @@ enquirer@~2.3.6: ent@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz" - integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= + resolved "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" + integrity sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA== env-paths@2.2.1, env-paths@^2.2.0: version "2.2.1" - resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" + resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== envinfo@^7.7.4: - version "7.8.1" - resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz" - integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + version "7.10.0" + resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.10.0.tgz#55146e3909cc5fe63c22da63fb15b05aeac35b13" + integrity sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw== err-code@^2.0.2: version "2.0.3" - resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz" + resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" es6-promise@^4.0.3: version "4.2.8" - resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz" + resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== es6-promisify@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + resolved "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ== dependencies: es6-promise "^4.0.3" escalade@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-html@~1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^2.0.0: version "2.0.0" @@ -5026,156 +5152,90 @@ escape-string-regexp@^4.0.0: resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -eslint-config-prettier@8.5.0, eslint-config-prettier@^8.5.0: +eslint-config-prettier@8.5.0: version "8.5.0" resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" +eslint-config-prettier@^8.5.0: + version "8.10.0" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" + integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== - -eslint@8.13.0: - version "8.13.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz#6fcea43b6811e655410f5626cfcf328016badcd7" - integrity sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ== - dependencies: - "@eslint/eslintrc" "^1.2.1" - "@humanwhocodes/config-array" "^0.9.2" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.3.1" - esquery "^1.4.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^6.0.1" - globals "^13.6.0" - ignore "^5.2.0" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.0.4" - natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" - strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - -eslint@8.14.0: - version "8.14.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz#62741f159d9eb4a79695b28ec4989fcdec623239" - integrity sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw== - dependencies: - "@eslint/eslintrc" "^1.2.2" - "@humanwhocodes/config-array" "^0.9.2" - ajv "^6.10.0" +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@8.49.0: + version "8.49.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz#09d80a89bdb4edee2efcf6964623af1054bf6d42" + integrity sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.2" + "@eslint/js" "8.49.0" + "@humanwhocodes/config-array" "^0.11.11" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.3.1" - esquery "^1.4.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^6.0.1" - globals "^13.6.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" + is-path-inside "^3.0.3" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" - minimatch "^3.0.4" + minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" + optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^9.3.1, espree@^9.3.2: - version "9.3.2" - resolved "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596" - integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^8.7.1" + acorn "^8.9.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.1" -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.0: version "4.0.1" resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.1, esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" @@ -5186,11 +5246,6 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" @@ -5203,47 +5258,25 @@ esutils@^2.0.2: etag@~1.8.1: version "1.8.1" - resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== ethereum-bloom-filters@^1.0.6: version "1.0.10" - resolved "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz" + resolved "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz#3ca07f4aed698e75bd134584850260246a5fed8a" integrity sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA== dependencies: js-sha3 "^0.8.0" -ethereum-cryptography@^0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz" - integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== - dependencies: - "@types/pbkdf2" "^3.0.0" - "@types/secp256k1" "^4.0.1" - blakejs "^1.1.0" - browserify-aes "^1.2.0" - bs58check "^2.1.2" - create-hash "^1.2.0" - create-hmac "^1.1.7" - hash.js "^1.1.7" - keccak "^3.0.0" - pbkdf2 "^3.0.17" - randombytes "^2.1.0" - safe-buffer "^5.1.2" - scrypt-js "^3.0.0" - secp256k1 "^4.0.1" - setimmediate "^1.0.5" - -ethereumjs-util@^7.1.0: - version "7.1.4" - resolved "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.4.tgz" - integrity sha512-p6KmuPCX4mZIqsQzXfmSx9Y0l2hqf+VkAiwSisW3UKUFdk8ZkAt+AYaor83z2nSi6CU2zSsXMlD80hAbNEGM0A== +ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz#18fa7108622e56481157a5cb7c01c0c6a672eb67" + integrity sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug== dependencies: - "@types/bn.js" "^5.1.0" - bn.js "^5.1.2" - create-hash "^1.1.2" - ethereum-cryptography "^0.1.3" - rlp "^2.2.4" + "@noble/curves" "1.1.0" + "@noble/hashes" "1.3.1" + "@scure/bip32" "1.3.1" + "@scure/bip39" "1.2.1" ethers@5.7.0, ethers@^5.6.0: version "5.7.0" @@ -5283,45 +5316,32 @@ ethers@5.7.0, ethers@^5.6.0: ethjs-unit@0.1.6: version "0.1.6" - resolved "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz" - integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk= + resolved "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw== dependencies: bn.js "4.11.6" number-to-bn "1.7.0" event-target-shim@^5.0.0: version "5.0.1" - resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" + resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -eventemitter3@^3.1.0: - version "3.1.2" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz" - integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== - eventemitter3@^4.0.4: version "4.0.7" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== eventid@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/eventid/-/eventid-2.0.1.tgz" + resolved "https://registry.npmjs.org/eventid/-/eventid-2.0.1.tgz#574e860149457a79a2efe788c459f0c3062d02ec" integrity sha512-sPNTqiMokAvV048P2c9+foqVJzk49o6d4e0D/sq5jog3pw+4kBgyR0gaM1FM7Mx6Kzd9dztesh9oYz1LWWOpzw== dependencies: uuid "^8.0.0" -evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - evt@1.10.1: version "1.10.1" - resolved "https://registry.npmjs.org/evt/-/evt-1.10.1.tgz" + resolved "https://registry.npmjs.org/evt/-/evt-1.10.1.tgz#2f25b6f9588f4a8951aa541d968e2d5d1ea94fd8" integrity sha512-0vkCFzH3Q2Qb9gs3yav4p3uu+l4mcIfKPTRFTO1WHYZd0+O/ZR7BgzpuF+FbqOJ6r9q20/sDL/5TQM+de0/hyg== dependencies: minimal-polyfills "^2.1.5" @@ -5329,7 +5349,7 @@ evt@1.10.1: evt@1.9.12: version "1.9.12" - resolved "https://registry.npmjs.org/evt/-/evt-1.9.12.tgz" + resolved "https://registry.npmjs.org/evt/-/evt-1.9.12.tgz#8d06177259cbcb09ef936e18945bf7ddd087170c" integrity sha512-u8wC4Xif2pcDJ9cEm0wzWCIQb+Y214m1eUgsgm2hVIuXuvC6LToryA0Ecl1O8Slii2E9l6USLsyxXWntjlnIbw== dependencies: minimal-polyfills "^2.1.5" @@ -5337,7 +5357,7 @@ evt@1.9.12: execa@^3.0.0: version "3.4.0" - resolved "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz" + resolved "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== dependencies: cross-spawn "^7.0.0" @@ -5353,7 +5373,7 @@ execa@^3.0.0: execa@^5.0.0: version "5.1.1" - resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -5371,19 +5391,25 @@ exit@^0.1.2: resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" - integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== +expect@^29.0.0, expect@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== dependencies: - "@jest/types" "^27.5.1" - jest-get-type "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + +exponential-backoff@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" + integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== express-graphql@0.12.0: version "0.12.0" - resolved "https://registry.npmjs.org/express-graphql/-/express-graphql-0.12.0.tgz" + resolved "https://registry.npmjs.org/express-graphql/-/express-graphql-0.12.0.tgz#58deabc309909ca2c9fe2f83f5fbe94429aa23df" integrity sha512-DwYaJQy0amdy3pgNtiTDuGGM2BLdj+YO2SgbKoLliCfuHv3VVTt7vNG/ZqK2hRYjtYHE2t2KB705EU94mE64zg== dependencies: accepts "^1.3.7" @@ -5391,55 +5417,56 @@ express-graphql@0.12.0: http-errors "1.8.0" raw-body "^2.4.1" -express-rate-limit@^5.5.1: - version "5.5.1" - resolved "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.5.1.tgz" - integrity sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg== +express-rate-limit@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.0.1.tgz#933af24166990ea4fc8004335e6cd6c86fd31562" + integrity sha512-oTIPm094gh8c7nbShl4TNLqnayzOcbDGY7dCRnFqUAvptyb0pp5231LaH34JtvVEbZlOJMiixikU5AVK8VN3FA== -express@4.17.3: - version "4.17.3" - resolved "https://registry.npmjs.org/express/-/express-4.17.3.tgz" - integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== +express@4.18.2: + version "4.18.2" + resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.19.2" + body-parser "1.20.1" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.4.2" + cookie "0.5.0" cookie-signature "1.0.6" debug "2.6.9" - depd "~1.1.2" + depd "2.0.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "~1.1.2" + finalhandler "1.2.0" fresh "0.5.2" + http-errors "2.0.0" merge-descriptors "1.0.1" methods "~1.1.2" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" path-to-regexp "0.1.7" proxy-addr "~2.0.7" - qs "6.9.7" + qs "6.11.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.17.2" - serve-static "1.14.2" + send "0.18.0" + serve-static "1.15.0" setprototypeof "1.2.0" - statuses "~1.5.0" + statuses "2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" extend@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" + resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== external-editor@^3.0.3: version "3.1.0" - resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" + resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" @@ -5448,27 +5475,27 @@ external-editor@^3.0.3: extract-files@^11.0.0: version "11.0.0" - resolved "https://registry.npmjs.org/extract-files/-/extract-files-11.0.0.tgz" + resolved "https://registry.npmjs.org/extract-files/-/extract-files-11.0.0.tgz#b72d428712f787eef1f5193aff8ab5351ca8469a" integrity sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ== extsprintf@^1.2.0: version "1.4.1" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== eyes@^0.1.8: version "0.1.8" - resolved "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz" - integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= + resolved "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" + integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== fast-decode-uri-component@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz" + resolved "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@3.2.7: @@ -5483,9 +5510,9 @@ fast-glob@3.2.7: micromatch "^4.0.4" fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + version "3.3.1" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -5493,14 +5520,14 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-json-stringify@^2.5.2: version "2.7.13" - resolved "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-2.7.13.tgz" + resolved "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-2.7.13.tgz#277aa86c2acba4d9851bd6108ed657aa327ed8c0" integrity sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA== dependencies: ajv "^6.11.0" @@ -5508,39 +5535,53 @@ fast-json-stringify@^2.5.2: rfdc "^1.2.0" string-similarity "^4.0.1" -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-querystring@^1.1.1: + version "1.1.2" + resolved "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz#a6d24937b4fc6f791b4ee31dcb6f53aeafb89f53" + integrity sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg== + dependencies: + fast-decode-uri-component "^1.0.1" + fast-redact@^3.0.0: - version "3.1.1" - resolved "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.1.tgz" - integrity sha512-odVmjC8x8jNeMZ3C+rPMESzXVSEU8tSWSHv9HFxP2mm89G/1WwqhrerJDQm9Zus8X6aoRgQDThKqptdNA6bt+A== + version "3.3.0" + resolved "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz#7c83ce3a7be4898241a46560d51de10f653f7634" + integrity sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ== fast-safe-stringify@^2.0.8, fast-safe-stringify@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz" + resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== fast-text-encoding@^1.0.0, fast-text-encoding@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz" - integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== + version "1.0.6" + resolved "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867" + integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== + +fast-url-parser@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== + dependencies: + punycode "^1.3.2" fastify-error@^0.3.0: version "0.3.1" - resolved "https://registry.npmjs.org/fastify-error/-/fastify-error-0.3.1.tgz" + resolved "https://registry.npmjs.org/fastify-error/-/fastify-error-0.3.1.tgz#8eb993e15e3cf57f0357fc452af9290f1c1278d2" integrity sha512-oCfpcsDndgnDVgiI7bwFKAun2dO+4h84vBlkWsWnz/OUK9Reff5UFoFl241xTiLeHWX/vU9zkDVXqYUxjOwHcQ== fastify-warning@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/fastify-warning/-/fastify-warning-0.2.0.tgz" + resolved "https://registry.npmjs.org/fastify-warning/-/fastify-warning-0.2.0.tgz#e717776026a4493dc9a2befa44db6d17f618008f" integrity sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw== fastify@3.25.0: version "3.25.0" - resolved "https://registry.npmjs.org/fastify/-/fastify-3.25.0.tgz" + resolved "https://registry.npmjs.org/fastify/-/fastify-3.25.0.tgz#04b682fa738c6468bc36efba9f1e943609502111" integrity sha512-GblpjS7yuJ9jpkz1guHTyzlVQn9NYvGrMkDLtoxctEt7n1d6MSwA9i3p10HjNiY+zVurPf3YdOqXsJmVgAR3cg== dependencies: "@fastify/ajv-compiler" "^1.0.0" @@ -5560,22 +5601,22 @@ fastify@3.25.0: tiny-lru "^7.0.0" fastq@^1.6.0, fastq@^1.6.1: - version "1.13.0" - resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + version "1.15.0" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== dependencies: reusify "^1.0.4" fb-watchman@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" - integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + version "2.0.2" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" figures@3.2.0, figures@^3.0.0: version "3.2.0" - resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" + resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" @@ -5589,10 +5630,10 @@ file-entry-cache@^6.0.1: file-uri-to-path@1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz" + resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== -filelist@^1.0.1: +filelist@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== @@ -5601,27 +5642,27 @@ filelist@^1.0.1: fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== dependencies: debug "2.6.9" encodeurl "~1.0.2" escape-html "~1.0.3" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" - statuses "~1.5.0" + statuses "2.0.1" unpipe "~1.0.0" find-my-way@^4.1.0: version "4.5.1" - resolved "https://registry.npmjs.org/find-my-way/-/find-my-way-4.5.1.tgz" + resolved "https://registry.npmjs.org/find-my-way/-/find-my-way-4.5.1.tgz#758e959194b90aea0270db18fff75e2fceb2239f" integrity sha512-kE0u7sGoUFbMXcOG/xpkmz4sRLCklERnBcg7Ftuu1iAxsfEt2S46RLJ3Sq7vshsEy2wJT2hZxE58XZK27qa8kg== dependencies: fast-decode-uri-component "^1.0.1" @@ -5638,30 +5679,39 @@ find-replace@^3.0.0: find-up@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== dependencies: locate-path "^2.0.0" find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + findit2@^2.2.3: version "2.2.3" - resolved "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz" - integrity sha1-WKRmaX34piBc39vzlVNri9d3pfY= + resolved "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz#58a466697df8a6205cdfdbf395536b8bd777a5f6" + integrity sha512-lg/Moejf4qXovVutL0Lz4IsaPoNYMuxt4PA0nGqFxnJ1CTTGGlEO2wKgoDpwknhvZ8k4Q2F+eesgkLbG2Mxfog== flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + version "3.1.0" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz#0e54ab4a1a60fe87e2946b6b00657f1c99e1af3f" + integrity sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew== dependencies: - flatted "^3.1.0" + flatted "^3.2.7" + keyv "^4.5.3" rimraf "^3.0.2" flat@^5.0.2: @@ -5671,37 +5721,26 @@ flat@^5.0.2: flatstr@^1.0.12: version "1.0.12" - resolved "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz" + resolved "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz#c2ba6a08173edbb6c9640e3055b95e287ceb5931" integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw== -flatted@^3.1.0: - version "3.2.5" - resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" - integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== - -follow-redirects@^1.14.0, follow-redirects@^1.14.8: - version "1.14.9" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz" - integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== +flatted@^3.2.7: + version "3.2.9" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== -follow-redirects@^1.15.0: +follow-redirects@^1.14.0, follow-redirects@^1.14.8, follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== -form-data-encoder@^1.7.1: - version "1.7.2" - resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz" - integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== - -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" + cross-spawn "^7.0.0" + signal-exit "^4.0.1" form-data@^4.0.0: version "4.0.0" @@ -5712,43 +5751,35 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -formdata-node@^4.3.1: - version "4.3.2" - resolved "https://registry.npmjs.org/formdata-node/-/formdata-node-4.3.2.tgz" - integrity sha512-k7lYJyzDOSL6h917favP8j1L0/wNyylzU+x+1w4p5haGVHNlP58dbpdJhiCUsDbWsa9HwEtLp89obQgXl2e0qg== - dependencies: - node-domexception "1.0.0" - web-streams-polyfill "4.0.0-beta.1" - -formidable@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/formidable/-/formidable-2.0.1.tgz#4310bc7965d185536f9565184dee74fbb75557ff" - integrity sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ== +formidable@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89" + integrity sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g== dependencies: - dezalgo "1.0.3" - hexoid "1.0.0" - once "1.4.0" - qs "6.9.3" + dezalgo "^1.0.4" + hexoid "^1.0.0" + once "^1.4.0" + qs "^6.11.0" forwarded@0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== fresh@0.5.2: version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@^10.1.0: - version "10.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== +fs-extra@^11.1.0: + version "11.1.1" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -5765,7 +5796,7 @@ fs-extra@^7.0.0: fs-extra@^9.1.0: version "9.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== dependencies: at-least-node "^1.0.0" @@ -5775,7 +5806,7 @@ fs-extra@^9.1.0: fs-jetpack@^2.2.2: version "2.4.0" - resolved "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-2.4.0.tgz" + resolved "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-2.4.0.tgz#6080c4ab464a019d37a404baeb47f32af8835026" integrity sha512-S/o9Dd7K9A7gicVU32eT8G0kHcmSu0rCVdP79P0MWInKFb8XpTc8Syhoo66k9no+HDshtlh4pUJTws8X+8fdFQ== dependencies: minimatch "^3.0.2" @@ -5783,7 +5814,7 @@ fs-jetpack@^2.2.2: fs-jetpack@^4.1.0: version "4.3.1" - resolved "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-4.3.1.tgz" + resolved "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-4.3.1.tgz#cdfd4b64e6bfdec7c7dc55c76b39efaa7853bb20" integrity sha512-dbeOK84F6BiQzk2yqqCVwCPWTxAvVGJ3fMQc6E2wuEohS28mR6yHngbrKuVCK1KHRx/ccByDylqu4H5PCP2urQ== dependencies: minimatch "^3.0.2" @@ -5791,34 +5822,36 @@ fs-jetpack@^4.1.0: fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" + resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: minipass "^3.0.0" +fs-minipass@^3.0.0: + version "3.0.3" + resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54" + integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== + dependencies: + minipass "^7.0.3" + fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +fsevents@^2.3.2: + version "2.3.3" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== function-bind@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== - gauge@^3.0.0: version "3.0.2" - resolved "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz" + resolved "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== dependencies: aproba "^1.0.3 || ^2.0.0" @@ -5847,7 +5880,7 @@ gauge@^4.0.3: gaxios@^4.0.0: version "4.3.3" - resolved "https://registry.npmjs.org/gaxios/-/gaxios-4.3.3.tgz" + resolved "https://registry.npmjs.org/gaxios/-/gaxios-4.3.3.tgz#d44bdefe52d34b6435cc41214fdb160b64abfc22" integrity sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA== dependencies: abort-controller "^3.0.0" @@ -5858,7 +5891,7 @@ gaxios@^4.0.0: gcp-metadata@^4.0.0, gcp-metadata@^4.2.0: version "4.3.1" - resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz" + resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz#fb205fe6a90fef2fd9c85e6ba06e5559ee1eefa9" integrity sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A== dependencies: gaxios "^4.0.0" @@ -5871,17 +5904,18 @@ gensync@^1.0.0-beta.2: get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2: - version "1.1.1" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + version "1.2.1" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== dependencies: function-bind "^1.1.1" has "^1.0.3" - has-symbols "^1.0.1" + has-proto "^1.0.1" + has-symbols "^1.0.3" get-package-type@^0.1.0: version "0.1.0" @@ -5890,7 +5924,7 @@ get-package-type@^0.1.0: get-pkg-repo@^4.0.0: version "4.2.1" - resolved "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz" + resolved "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz#75973e1c8050c73f48190c52047c4cee3acbf385" integrity sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA== dependencies: "@hutson/parse-repository-url" "^3.0.0" @@ -5900,24 +5934,24 @@ get-pkg-repo@^4.0.0: get-port@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz" + resolved "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== get-stream@^5.0.0: version "5.2.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" get-stream@^6.0.0: version "6.0.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== git-raw-commits@^2.0.8: version "2.0.11" - resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz" + resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" integrity sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A== dependencies: dargs "^7.0.0" @@ -5928,15 +5962,15 @@ git-raw-commits@^2.0.8: git-remote-origin-url@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz" - integrity sha1-UoJlna4hBxRaERJhEq0yFuxfpl8= + resolved "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" + integrity sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw== dependencies: gitconfiglocal "^1.0.0" pify "^2.3.0" git-semver-tags@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz" + resolved "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" integrity sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA== dependencies: meow "^8.0.0" @@ -5959,19 +5993,19 @@ git-url-parse@^13.1.0: gitconfiglocal@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz" - integrity sha1-QdBF84UaXqiPA/JMocYXgRRGS5s= + resolved "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" + integrity sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ== dependencies: ini "^1.3.2" -glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.2: +glob-parent@^5.1.1, glob-parent@^5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob-parent@^6.0.1: +glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== @@ -6002,19 +6036,18 @@ glob@7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.2.0" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@^10.2.2: + version "10.3.4" + resolved "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz#c85c9c7ab98669102b6defda76d35c5b1ef9766f" + integrity sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ== dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" + foreground-child "^3.1.0" + jackspeak "^2.0.3" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" -glob@^7.1.2: +glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -6027,9 +6060,9 @@ glob@^7.1.2: path-is-absolute "^1.0.0" glob@^8.0.1: - version "8.0.3" - resolved "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e" - integrity sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ== + version "8.1.0" + resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -6039,7 +6072,7 @@ glob@^8.0.1: global@4.4.0: version "4.4.0" - resolved "https://registry.npmjs.org/global/-/global-4.4.0.tgz" + resolved "https://registry.npmjs.org/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== dependencies: min-document "^2.19.0" @@ -6050,16 +6083,16 @@ globals@^11.1.0: resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.15.0, globals@^13.6.0: - version "13.15.0" - resolved "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac" - integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== +globals@^13.19.0: + version "13.21.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz#163aae12f34ef502f5153cfbdd3600f36c63c571" + integrity sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg== dependencies: type-fest "^0.20.2" -globby@^11.0.2, globby@^11.0.4: +globby@^11.0.2, globby@^11.1.0: version "11.1.0" - resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" @@ -6071,7 +6104,7 @@ globby@^11.0.2, globby@^11.0.4: gluegun@4.7.0: version "4.7.0" - resolved "https://registry.npmjs.org/gluegun/-/gluegun-4.7.0.tgz" + resolved "https://registry.npmjs.org/gluegun/-/gluegun-4.7.0.tgz#d1e88828ec6737d966619fff07c04f7e689dc59e" integrity sha512-St+J/rly0FoWLeISgBGDuymwF3/b8OdmxBCbSvK1hXEoRbaaATiRpPepJSJWuRYR7cGR7Hy9drgQwGFBAolhbQ== dependencies: apisauce "^2.0.1" @@ -6108,7 +6141,7 @@ gluegun@4.7.0: google-auth-library@^7.0.0, google-auth-library@^7.14.0: version "7.14.1" - resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz" + resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz#e3483034162f24cc71b95c8a55a210008826213c" integrity sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA== dependencies: arrify "^2.0.0" @@ -6122,12 +6155,12 @@ google-auth-library@^7.0.0, google-auth-library@^7.14.0: lru-cache "^6.0.0" google-gax@^2.24.1: - version "2.30.2" - resolved "https://registry.npmjs.org/google-gax/-/google-gax-2.30.2.tgz" - integrity sha512-BCNCT26kb0iC52zj2SosyOZMhI5sVfXuul1h0Aw5uT9nGAbmS5eOvQ49ft53ft6XotDj11sUSDV6XESEiQqCqg== + version "2.30.5" + resolved "https://registry.npmjs.org/google-gax/-/google-gax-2.30.5.tgz#e836f984f3228900a8336f608c83d75f9cb73eff" + integrity sha512-Jey13YrAN2hfpozHzbtrwEfEHdStJh1GwaQ2+Akh1k0Tv/EuNVSuBtHZoKSBm5wBMvNsxTsEIZ/152NrYyZgxQ== dependencies: "@grpc/grpc-js" "~1.6.0" - "@grpc/proto-loader" "^0.6.1" + "@grpc/proto-loader" "^0.6.12" "@types/long" "^4.0.0" abort-controller "^3.0.0" duplexify "^4.0.0" @@ -6137,41 +6170,36 @@ google-gax@^2.24.1: node-fetch "^2.6.1" object-hash "^3.0.0" proto3-json-serializer "^0.1.8" - protobufjs "6.11.2" + protobufjs "6.11.3" retry-request "^4.0.0" google-p12-pem@^3.1.3: version "3.1.4" - resolved "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.4.tgz" + resolved "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.4.tgz#123f7b40da204de4ed1fbf2fd5be12c047fc8b3b" integrity sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg== dependencies: node-forge "^1.3.1" graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6, graceful-fs@^4.2.9: - version "4.2.10" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + version "4.2.11" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -graphql-executor@0.0.23: - version "0.0.23" - resolved "https://registry.npmjs.org/graphql-executor/-/graphql-executor-0.0.23.tgz" - integrity sha512-3Ivlyfjaw3BWmGtUSnMpP/a4dcXCp0mJtj0PiPG14OKUizaMKlSEX+LX2Qed0LrxwniIwvU6B4w/koVjEPyWJg== - -graphql-sse@^1.0.1: - version "1.2.0" - resolved "https://registry.npmjs.org/graphql-sse/-/graphql-sse-1.2.0.tgz" - integrity sha512-W6XLGLqmwSDUzyUF8dcEPwNmhYe8g90P+wWETZ+6cmk/2kS3zJ1SXgeglWQ2tFKC/FXrc68hTHdHdmLjetJn+Q== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== graphql-tag@2.12.6, graphql-tag@^2.12.3: version "2.12.6" - resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz" + resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== dependencies: tslib "^2.1.0" graphql-tools@8.2.6: version "8.2.6" - resolved "https://registry.npmjs.org/graphql-tools/-/graphql-tools-8.2.6.tgz" + resolved "https://registry.npmjs.org/graphql-tools/-/graphql-tools-8.2.6.tgz#5c6592ab3bfb3f96fd8070a500e7342f0ae9b7b1" integrity sha512-nbEbzkD/z8dmPxa+VlmP4+KmNl3wDXjsWEBHo8VZInEsHDk6w8Cm7lfy1lOlNe30ZXi7T4+fvqLIMQ2gSse+1Q== dependencies: "@graphql-tools/schema" "8.3.8" @@ -6179,19 +6207,19 @@ graphql-tools@8.2.6: optionalDependencies: "@apollo/client" "~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0" -graphql-ws@^5.4.1: - version "5.7.0" - resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.7.0.tgz" - integrity sha512-8yYuvnyqIjlJ/WfebOyu2GSOQeFauRxnfuTveY9yvrDGs2g3kR9Nv4gu40AKvRHbXlSJwTbMJ6dVxAtEyKwVRA== +graphql-ws@^5.14.0: + version "5.14.0" + resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.14.0.tgz#766f249f3974fc2c48fae0d1fb20c2c4c79cd591" + integrity sha512-itrUTQZP/TgswR4GSSYuwWUzrE/w5GhbwM2GX3ic2U7aw33jgEsayfIlvaj7/GcIvZgNMzsPTrE5hqPuFUiE5g== -graphql@16.3.0: - version "16.3.0" - resolved "https://registry.npmjs.org/graphql/-/graphql-16.3.0.tgz" - integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A== +graphql@16.8.0: + version "16.8.0" + resolved "https://registry.npmjs.org/graphql/-/graphql-16.8.0.tgz#374478b7f27b2dc6153c8f42c1b80157f79d79d4" + integrity sha512-0oKGaR+y3qcS5mCu1vb7KG+a89vjn06C7Ihq/dDl3jA+A8B3TKomvi3CiEcVLJQGalbu8F52LxkOym7U5sSfbg== gtoken@^5.0.4: version "5.3.2" - resolved "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz" + resolved "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz#deb7dc876abe002178e0515e383382ea9446d58f" integrity sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ== dependencies: gaxios "^4.0.0" @@ -6199,12 +6227,12 @@ gtoken@^5.0.4: jws "^4.0.0" handlebars@^4.7.7: - version "4.7.7" - resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + version "4.7.8" + resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== dependencies: minimist "^1.2.5" - neo-async "^2.6.0" + neo-async "^2.6.2" source-map "^0.6.1" wordwrap "^1.0.0" optionalDependencies: @@ -6212,67 +6240,63 @@ handlebars@^4.7.7: hard-rejection@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz" + resolved "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.1: +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== has-unicode@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== has@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" - resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" + resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: inherits "^2.0.3" minimalistic-assert "^1.0.1" -helmet@5.0.2: - version "5.0.2" - resolved "https://registry.npmjs.org/helmet/-/helmet-5.0.2.tgz" - integrity sha512-QWlwUZZ8BtlvwYVTSDTBChGf8EOcQ2LkGMnQJxSzD1mUu8CCjXJZq/BXP8eWw4kikRnzlhtYo3lCk0ucmYA3Vg== +helmet@7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/helmet/-/helmet-7.0.0.tgz#ac3011ba82fa2467f58075afa58a49427ba6212d" + integrity sha512-MsIgYmdBh460ZZ8cJC81q4XJknjG567wzEmv46WOBblDb6TUd3z8/GhgmsM9pn8g2B80tAJ4m5/d3Bi1KrSUBQ== -hexoid@1.0.0: +hexoid@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18" integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g== hmac-drbg@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== dependencies: hash.js "^1.0.3" minimalistic-assert "^1.0.0" @@ -6280,14 +6304,14 @@ hmac-drbg@^1.0.1: hoist-non-react-statics@^3.3.2: version "3.3.2" - resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" + resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== dependencies: react-is "^16.7.0" hosted-git-info@^2.1.4: version "2.8.9" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== hosted-git-info@^3.0.6: @@ -6299,7 +6323,7 @@ hosted-git-info@^3.0.6: hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: version "4.1.0" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== dependencies: lru-cache "^6.0.0" @@ -6311,26 +6335,19 @@ hosted-git-info@^5.0.0: dependencies: lru-cache "^7.5.1" -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== - dependencies: - whatwg-encoding "^1.0.5" - html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-cache-semantics@^4.1.0: - 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== +http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-errors@1.8.0: version "1.8.0" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== dependencies: depd "~1.1.2" @@ -6339,20 +6356,9 @@ http-errors@1.8.0: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-errors@1.8.1: - version "1.8.1" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz" - integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.1" - http-errors@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== dependencies: depd "2.0.0" @@ -6361,18 +6367,9 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - http-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== dependencies: "@tootallnate/once" "2" @@ -6381,7 +6378,7 @@ http-proxy-agent@^5.0.0: https-proxy-agent@^5.0.0: version "5.0.1" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" @@ -6389,38 +6386,38 @@ https-proxy-agent@^5.0.0: human-signals@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== humanize-ms@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz" - integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= + resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== dependencies: ms "^2.0.0" iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" iconv-lite@^0.6.2: version "0.6.3" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" ieee754@^1.1.13: version "1.2.1" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore-walk@^5.0.1: @@ -6430,19 +6427,14 @@ ignore-walk@^5.0.1: dependencies: minimatch "^5.0.1" -ignore@^5.0.4: - version "5.2.1" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz#c2b1f76cb999ede1502f3a226a9310fdfe88d46c" - integrity sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA== - -ignore@^5.1.8, ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +ignore@^5.0.4, ignore@^5.2.0, ignore@^5.2.4: + version "5.2.4" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== -import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: +import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" @@ -6450,7 +6442,7 @@ import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: import-local@^3.0.2: version "3.1.0" - resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== dependencies: pkg-dir "^4.2.0" @@ -6458,40 +6450,40 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== indent-string@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== infer-owner@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz" + resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== -inflection@^1.13.2: - version "1.13.2" - resolved "https://registry.npmjs.org/inflection/-/inflection-1.13.2.tgz" - integrity sha512-cmZlljCRTBFouT8UzMzrGcVEvkv6D/wBdcdKG7J1QH5cXjtU75Dm+P27v9EKu/Y43UYyCJd1WC4zLebRrC8NBw== +inflection@^1.13.4: + version "1.13.4" + resolved "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz#65aa696c4e2da6225b148d7a154c449366633a32" + integrity sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@^1.3.2, ini@^1.3.4: version "1.3.8" - resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== init-package-json@^3.0.2: @@ -6508,9 +6500,9 @@ init-package-json@^3.0.2: validate-npm-package-name "^4.0.0" inquirer@^8.2.4: - version "8.2.5" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" - integrity sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ== + version "8.2.6" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" + integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.1" @@ -6526,41 +6518,34 @@ inquirer@^8.2.4: string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" - wrap-ansi "^7.0.0" + wrap-ansi "^6.0.1" -ip@^1.1.5: - version "1.1.5" - resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== ipaddr.js@1.9.1: version "1.9.1" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-ci@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz" + resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== dependencies: ci-info "^2.0.0" -is-core-module@^2.5.0, is-core-module@^2.8.1: - version "2.9.0" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== +is-core-module@^2.13.0, is-core-module@^2.5.0, is-core-module@^2.8.1: + version "2.13.0" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== dependencies: has "^1.0.3" @@ -6571,17 +6556,17 @@ is-docker@^2.0.0, is-docker@^2.1.1: is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-generator-fn@^2.0.0: @@ -6589,65 +6574,65 @@ is-generator-fn@^2.0.0: resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-hex-prefixed@1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz" - integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= + resolved "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== is-interactive@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" + resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== is-lambda@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz" - integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= + resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== is-number@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-obj@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== is-plain-obj@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-plain-object@^2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-plain-object@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - is-ssh@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz#4f8220601d2839d8fa624b3106f8e8884f01b8b2" @@ -6657,25 +6642,25 @@ is-ssh@^1.4.0: is-stream-ended@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz" + resolved "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" integrity sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw== is-stream@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-text-path@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz" - integrity sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4= + resolved "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" + integrity sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w== dependencies: text-extensions "^1.0.0" is-typedarray@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== is-unicode-supported@^0.1.0: version "0.1.0" @@ -6691,30 +6676,35 @@ is-wsl@^2.2.0: isarray@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isobject@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== isomorphic-fetch@3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz" + resolved "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== dependencies: node-fetch "^2.6.1" whatwg-fetch "^3.4.1" +isomorphic-ws@5.0.0, isomorphic-ws@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" + integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== + isomorphic-ws@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz" + resolved "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: @@ -6722,10 +6712,10 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: - version "5.2.0" - resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f" - integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A== +istanbul-lib-instrument@^5.0.4: + version "5.2.1" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== dependencies: "@babel/core" "^7.12.3" "@babel/parser" "^7.14.7" @@ -6733,13 +6723,24 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-instrument@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz#7a8af094cbfff1d5bb280f62ce043695ae8dd5b8" + integrity sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + version "3.0.1" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" + make-dir "^4.0.0" supports-color "^7.1.0" istanbul-lib-source-maps@^4.0.0: @@ -6752,31 +6753,35 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.1.3: - version "3.1.4" - resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" - integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== + version "3.1.6" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" + integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -iterall@^1.2.1: - version "1.3.0" - resolved "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz" - integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== +jackspeak@^2.0.3: + version "2.3.3" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.3.tgz#95e4cbcc03b3eb357bf6bcce14a903fb3d1151e1" + integrity sha512-R2bUw+kVZFS/h1AZqBKrSgDmdmjApzgY0AlCPumopFiAlbUxE2gf+SCuBzQ0cP5hHmUmFYF5yw55T97Th5Kstg== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" jake@^10.8.5: - version "10.8.5" - resolved "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" - integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== + version "10.8.7" + resolved "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" + integrity sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w== dependencies: async "^3.2.3" chalk "^4.0.2" - filelist "^1.0.1" - minimatch "^3.0.4" + filelist "^1.0.4" + minimatch "^3.1.2" jayson@3.6.6: version "3.6.6" - resolved "https://registry.npmjs.org/jayson/-/jayson-3.6.6.tgz" + resolved "https://registry.npmjs.org/jayson/-/jayson-3.6.6.tgz#189984f624e398f831bd2be8e8c80eb3abf764a1" integrity sha512-f71uvrAWTtrwoww6MKcl9phQTC+56AopLyEenWvKVAIMz+q0oVGj6tenLZ7Z6UiPBkJtKLj4kt0tACllFQruGQ== dependencies: "@types/connect" "^3.4.33" @@ -6795,419 +6800,372 @@ jayson@3.6.6: uuid "^8.3.2" ws "^7.4.5" -jest-changed-files@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" - integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== dependencies: - "@jest/types" "^27.5.1" execa "^5.0.0" - throat "^6.0.1" - -jest-circus@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" - integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + jest-util "^29.7.0" + p-limit "^3.1.0" + +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - dedent "^0.7.0" - expect "^27.5.1" + dedent "^1.0.0" is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + pretty-format "^29.7.0" + pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" - throat "^6.0.1" -jest-cli@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" - integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== dependencies: - "@jest/core" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" chalk "^4.0.0" + create-jest "^29.7.0" exit "^0.1.2" - graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - prompts "^2.0.1" - yargs "^16.2.0" - -jest-config@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" - integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== - dependencies: - "@babel/core" "^7.8.0" - "@jest/test-sequencer" "^27.5.1" - "@jest/types" "^27.5.1" - babel-jest "^27.5.1" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + yargs "^17.3.1" + +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" - glob "^7.1.1" + glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-get-type "^27.5.1" - jest-jasmine2 "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runner "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^27.5.1" + pretty-format "^29.7.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== dependencies: chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-docblock@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" - integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== dependencies: detect-newline "^3.0.0" -jest-each@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" - integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" chalk "^4.0.0" - jest-get-type "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - -jest-environment-jsdom@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" - integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" - jsdom "^16.6.0" - -jest-environment-node@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" - integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" + +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-mock "^29.7.0" + jest-util "^29.7.0" -jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== -jest-haste-map@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" - integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== dependencies: - "@jest/types" "^27.5.1" - "@types/graceful-fs" "^4.1.2" + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^27.5.1" - jest-serializer "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" micromatch "^4.0.4" - walker "^1.0.7" + walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" - integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== dependencies: - "@jest/environment" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^27.5.1" - is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - throat "^6.0.1" - -jest-leak-detector@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" - integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== - dependencies: - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - -jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== dependencies: chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-message-util@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" - integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^27.5.1" + pretty-format "^29.7.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" - integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" "@types/node" "*" + jest-util "^29.7.0" jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + version "1.2.3" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== -jest-regex-util@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" - integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== -jest-resolve-dependencies@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" - integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== dependencies: - "@jest/types" "^27.5.1" - jest-regex-util "^27.5.1" - jest-snapshot "^27.5.1" + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" -jest-resolve@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" - integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== dependencies: - "@jest/types" "^27.5.1" chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" + jest-haste-map "^29.7.0" jest-pnp-resolver "^1.2.2" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-util "^29.7.0" + jest-validate "^29.7.0" resolve "^1.20.0" - resolve.exports "^1.1.0" + resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" - integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== dependencies: - "@jest/console" "^27.5.1" - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - emittery "^0.8.1" + emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-haste-map "^27.5.1" - jest-leak-detector "^27.5.1" - jest-message-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runtime "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - source-map-support "^0.5.6" - throat "^6.0.1" - -jest-runtime@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" - integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/globals" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - execa "^5.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" strip-bom "^4.0.0" -jest-serializer@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" - integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.9" - -jest-snapshot@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" - integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== dependencies: - "@babel/core" "^7.7.2" + "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.0.0" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/babel__traverse" "^7.0.4" - "@types/prettier" "^2.1.5" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.5.1" + expect "^29.7.0" graceful-fs "^4.2.9" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - jest-haste-map "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-util "^27.5.1" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" natural-compare "^1.4.0" - pretty-format "^27.5.1" - semver "^7.3.2" + pretty-format "^29.7.0" + semver "^7.5.3" -jest-util@^27.0.0, jest-util@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" - integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== +jest-util@^29.0.0, jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" - integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^27.5.1" + jest-get-type "^29.6.3" leven "^3.1.0" - pretty-format "^27.5.1" + pretty-format "^29.7.0" -jest-watcher@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" - integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== dependencies: - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^27.5.1" + emittery "^0.13.1" + jest-util "^29.7.0" string-length "^4.0.1" -jest-worker@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" + jest-util "^29.7.0" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" - integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== +jest@<30.0.0-0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== dependencies: - "@jest/core" "^27.5.1" + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" import-local "^3.0.2" - jest-cli "^27.5.1" + jest-cli "^29.7.0" js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" - resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz" + resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@4.1.0, js-yaml@^4.1.0: @@ -7227,42 +7185,9 @@ js-yaml@^3.10.0, js-yaml@^3.13.1: jsbi@^3.1.1: version "3.2.5" - resolved "https://registry.npmjs.org/jsbi/-/jsbi-3.2.5.tgz" + resolved "https://registry.npmjs.org/jsbi/-/jsbi-3.2.5.tgz#b37bb90e0e5c2814c1c2a1bcd8c729888a2e37d6" integrity sha512-aBE4n43IPvjaddScbvWRA2YlTzKEynHzu7MqOyTipdHucf/VxS63ViCjxYRg86M8Rxwbt/GfzHl1kKERkt45fQ== -jsdom@^16.6.0: - version "16.7.0" - resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -7270,29 +7195,34 @@ jsesc@^2.5.1: json-bigint@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz" + resolved "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== dependencies: bignumber.js "^9.0.0" +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" + resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema-traverse@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== json-stable-stringify-without-jsonify@^1.0.1: @@ -7307,20 +7237,13 @@ json-stringify-nice@^1.1.4: json-stringify-safe@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -json5@2.x, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" +json5@^2.2.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonc-parser@3.2.0: version "3.2.0" @@ -7336,7 +7259,7 @@ jsonfile@^4.0.0: jsonfile@^6.0.1: version "6.1.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" @@ -7345,22 +7268,22 @@ jsonfile@^6.0.1: jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" - resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== just-diff-apply@^5.2.0: - version "5.4.1" - resolved "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-5.4.1.tgz#1debed059ad009863b4db0e8d8f333d743cdd83b" - integrity sha512-AAV5Jw7tsniWwih8Ly3fXxEZ06y+6p5TwQMsw0dzZ/wPKilzyDgdAnL0Ug4NNIquPUOh1vfFWEHbmXUqM5+o8g== + version "5.5.0" + resolved "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-5.5.0.tgz#771c2ca9fa69f3d2b54e7c3f5c1dfcbcc47f9f0f" + integrity sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw== just-diff@^5.0.1: - version "5.1.1" - resolved "https://registry.npmjs.org/just-diff/-/just-diff-5.1.1.tgz#8da6414342a5ed6d02ccd64f5586cbbed3146202" - integrity sha512-u8HXJ3HlNrTzY7zrYYKjNEfBlyjqhdBkoyTVdjtn7p02RJD5NvR8rIClzeGA7t+UYP1/7eAkWNLU0+P3QrEqKQ== + version "5.2.0" + resolved "https://registry.npmjs.org/just-diff/-/just-diff-5.2.0.tgz#60dca55891cf24cd4a094e33504660692348a241" + integrity sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw== jwa@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz" + resolved "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== dependencies: buffer-equal-constant-time "1.0.1" @@ -7369,24 +7292,22 @@ jwa@^2.0.0: jws@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz" + resolved "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== dependencies: jwa "^2.0.0" safe-buffer "^5.0.1" -keccak@^3.0.0: - version "3.0.2" - resolved "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz" - integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== +keyv@^4.5.3: + version "4.5.3" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz#00873d2b046df737963157bd04f294ca818c9c25" + integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug== dependencies: - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - readable-stream "^3.6.0" + json-buffer "3.0.1" kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== kleur@^3.0.3: @@ -7436,14 +7357,6 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - libnpmaccess@^6.0.3: version "6.0.4" resolved "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-6.0.4.tgz#2dd158bd8a071817e2207d3b201d37cf1ad6ae6b" @@ -7466,24 +7379,29 @@ libnpmpublish@^6.0.4: ssri "^9.0.0" light-my-request@^4.2.0: - version "4.9.0" - resolved "https://registry.npmjs.org/light-my-request/-/light-my-request-4.9.0.tgz" - integrity sha512-b1U3z4OVPoO/KanT14NRkXMr9rRtXAiq0ORqNrqhDyb5bGkZjAdEc6GRN1GWCfgaLBG+aq73qkCLDNeB3c2sLw== + version "4.12.0" + resolved "https://registry.npmjs.org/light-my-request/-/light-my-request-4.12.0.tgz#fd59329a7b4f794842103c7bef69e12252478831" + integrity sha512-0y+9VIfJEsPVzK5ArSIJ8Dkxp8QMP7/aCuxCUtG/tr9a2NoOf/snATE/OUc05XUplJCEnRh6gTkH7xh9POt1DQ== dependencies: ajv "^8.1.0" - cookie "^0.4.0" + cookie "^0.5.0" process-warning "^1.0.0" set-cookie-parser "^2.4.1" lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +lines-and-columns@~2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b" + integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w== + load-json-file@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw== dependencies: graceful-fs "^4.1.2" parse-json "^4.0.0" @@ -7492,7 +7410,7 @@ load-json-file@^4.0.0: load-json-file@^6.2.0: version "6.2.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" integrity sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ== dependencies: graceful-fs "^4.1.15" @@ -7502,43 +7420,70 @@ load-json-file@^6.2.0: locate-path@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== dependencies: p-locate "^2.0.0" path-exists "^3.0.0" locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash.camelcase@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" - integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + +lodash.groupby@^4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1" + integrity sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw== + +lodash.isequal@4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== lodash.ismatch@^4.4.0: version "4.4.0" - resolved "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz" - integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= + resolved "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" + integrity sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g== lodash.kebabcase@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz" - integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= + resolved "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" + integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== lodash.lowercase@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/lodash.lowercase/-/lodash.lowercase-4.3.0.tgz" - integrity sha1-RlFaztSssLcJMTMzOvBo5MOxTp0= + resolved "https://registry.npmjs.org/lodash.lowercase/-/lodash.lowercase-4.3.0.tgz#46515aced4acb0b7093133333af068e4c3b14e9d" + integrity sha512-UcvP1IZYyDKyEL64mmrwoA1AbFu5ahojhTtkOUr1K9dbuxzS9ev8i4TxMMGCqRC9TE8uDaSoufNAXxRPNTseVA== lodash.lowerfirst@^4.3.1: version "4.3.1" - resolved "https://registry.npmjs.org/lodash.lowerfirst/-/lodash.lowerfirst-4.3.1.tgz" - integrity sha1-3jx7EuAsZSSgBZwvbLfFxSZVoT0= + resolved "https://registry.npmjs.org/lodash.lowerfirst/-/lodash.lowerfirst-4.3.1.tgz#de3c7b12e02c6524a0059c2f6cb7c5c52655a13d" + integrity sha512-UUKX7VhP1/JL54NXg2aq/E1Sfnjjes8fNYTNkPU8ZmsaVeBvPHKdbNaN79Re5XRL01u6wbq3j0cbYZj71Fcu5w== + +lodash.mapvalues@^4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" + integrity sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ== lodash.memoize@4.x: version "4.1.2" @@ -7552,77 +7497,82 @@ lodash.merge@^4.6.2: lodash.pad@^4.5.1: version "4.5.1" - resolved "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz" - integrity sha1-QzCUmoM6fI2iLMIPaibE1Z3runA= + resolved "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz#4330949a833a7c8da22cc20f6a26c4d59debba70" + integrity sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg== lodash.padend@^4.6.1: version "4.6.1" - resolved "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz" - integrity sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4= + resolved "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e" + integrity sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw== lodash.padstart@^4.6.1: version "4.6.1" - resolved "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz" - integrity sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs= + resolved "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b" + integrity sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw== lodash.repeat@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz" - integrity sha1-/H3oEx2MisB+S0n3T/6CnR8r7EQ= - -lodash.set@^4.3.2: - version "4.3.2" - resolved "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" - integrity sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg== + resolved "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz#fc7de8131d8c8ac07e4b49f74ffe829d1f2bec44" + integrity sha512-eWsgQW89IewS95ZOcr15HHCX6FVDxq3f2PNUIng3fyzsPev9imFQxIYdFZ6crl8L56UR6ZlGDLcEb3RZsCSSqw== lodash.snakecase@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz" - integrity sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40= + resolved "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== lodash.startcase@^4.4.0: version "4.4.0" - resolved "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz" - integrity sha1-lDbjTtJgk+1/+uGTYUQ1CRXZrdg= + resolved "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" + integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== lodash.trim@^4.5.1: version "4.5.1" - resolved "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz" - integrity sha1-NkJefukL5KpeJ7zruFt9EepHqlc= + resolved "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz#36425e7ee90be4aa5e27bcebb85b7d11ea47aa57" + integrity sha512-nJAlRl/K+eiOehWKDzoBVrSMhK0K3A3YQsUNXHQa5yIrKBAhsZgSu3KoAFoFT+mEgiyBHddZ0pRk1ITpIp90Wg== lodash.trimend@^4.5.1: version "4.5.1" - resolved "https://registry.npmjs.org/lodash.trimend/-/lodash.trimend-4.5.1.tgz" - integrity sha1-EoBENyhrmMrYmWt5QU4RMAEUCC8= + resolved "https://registry.npmjs.org/lodash.trimend/-/lodash.trimend-4.5.1.tgz#12804437286b98cad8996b79414e11300114082f" + integrity sha512-lsD+k73XztDsMBKPKvzHXRKFNMohTjoTKIIo4ADLn5dA65LZ1BqlAvSXhR2rPEC3BgAUQnzMnorqDtqn2z4IHA== lodash.trimstart@^4.5.1: version "4.5.1" - resolved "https://registry.npmjs.org/lodash.trimstart/-/lodash.trimstart-4.5.1.tgz" - integrity sha1-j/TexTLYJIavWVc8OURZFOlEp/E= + resolved "https://registry.npmjs.org/lodash.trimstart/-/lodash.trimstart-4.5.1.tgz#8ff4dec532d82486af59573c39445914e944a7f1" + integrity sha512-b/+D6La8tU76L/61/aN0jULWHkT0EeJCmVstPBn/K9MtD2qBW83AsBNrr63dKuWYwVMO7ucv13QNO/Ek/2RKaQ== lodash.truncate@^4.4.2: version "4.4.2" - resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= + resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== lodash.uppercase@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/lodash.uppercase/-/lodash.uppercase-4.3.0.tgz" - integrity sha1-xASr/RRp+Tkx+bskz2zH1XBZvHM= + resolved "https://registry.npmjs.org/lodash.uppercase/-/lodash.uppercase-4.3.0.tgz#c404abfd1469f93931f9bb24cf6cc7d57059bc73" + integrity sha512-+Nbnxkj7s8K5U8z6KnEYPGUOGp3woZbB7Ecs7v3LkkjLQSm2kP9SKIILitN1ktn2mB/tmM9oSlku06I+/lH7QA== lodash.upperfirst@^4.3.1: version "4.3.1" - resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz" - integrity sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984= + resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" + integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== + +lodash.xor@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.xor/-/lodash.xor-4.5.0.tgz#4d48ed7e98095b0632582ba714d3ff8ae8fb1db6" + integrity sha512-sVN2zimthq7aZ5sPGXnSz32rZPuqcparVW50chJQe+mzTYV+IsxSsl/2gnkWWE2Of7K3myBQBqtLKOUEHJKRsQ== + +lodash.zip@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" + integrity sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg== -lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: +lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== dependencies: chalk "^2.4.2" @@ -7637,36 +7587,53 @@ log-symbols@^4.1.0: long@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/long/-/long-4.0.0.tgz" + resolved "https://registry.npmjs.org/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== +long@^5.0.0: + version "5.2.3" + resolved "https://registry.npmjs.org/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" + integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== + loose-envify@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: - version "7.14.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz#8da8d2f5f59827edb388e63e459ac23d6d408fea" - integrity sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA== + version "7.18.3" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + +"lru-cache@^9.1.1 || ^10.0.0": + version "10.0.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" + integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== lru_map@^0.3.3: version "0.3.3" - resolved "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz" - integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= + resolved "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== make-dir@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== dependencies: pify "^4.0.1" @@ -7674,17 +7641,24 @@ make-dir@^2.1.0: make-dir@^3.0.0, make-dir@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + make-error@1.x, make-error@^1.1.1: version "1.3.6" resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: +make-fetch-happen@^10.0.6: version "10.2.1" resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== @@ -7706,6 +7680,27 @@ make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: socks-proxy-agent "^7.0.0" ssri "^9.0.0" +make-fetch-happen@^11.0.3: + version "11.1.1" + resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" + integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== + dependencies: + agentkeepalive "^4.2.1" + cacache "^17.0.0" + http-cache-semantics "^4.1.1" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^7.7.1" + minipass "^5.0.0" + minipass-fetch "^3.0.0" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + promise-retry "^2.0.1" + socks-proxy-agent "^7.0.0" + ssri "^10.0.0" + makeerror@1.0.12: version "1.0.12" resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -7715,31 +7710,22 @@ makeerror@1.0.12: map-obj@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== map-obj@^4.0.0: version "4.3.0" - resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - media-typer@0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== meow@^8.0.0: version "8.1.2" - resolved "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz" + resolved "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== dependencies: "@types/minimist" "^1.2.0" @@ -7756,23 +7742,23 @@ meow@^8.0.0: merge-descriptors@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" - resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== merkletreejs@^0.2.13: - version "0.2.31" - resolved "https://registry.npmjs.org/merkletreejs/-/merkletreejs-0.2.31.tgz" - integrity sha512-dnK2sE43OebmMe5Qnq1wXvvMIjZjm1u6CcB2KeW6cghlN4p21OpCUr2p56KTVf20KJItNChVsGnimcscp9f+yw== + version "0.2.32" + resolved "https://registry.npmjs.org/merkletreejs/-/merkletreejs-0.2.32.tgz#cf1c0760e2904e4a1cc269108d6009459fd06223" + integrity sha512-TostQBiwYRIwSE5++jGmacu3ODcKAgqb0Y/pnIohXS7sWxh1gCkSptbmF1a43faehRDpcHf7J/kv0Ml2D/zblQ== dependencies: bignumber.js "^9.0.1" buffer-reverse "^1.0.1" @@ -7780,19 +7766,24 @@ merkletreejs@^0.2.13: treeify "^1.1.0" web3-utils "^1.3.4" -meros@^1.1.4: - version "1.2.0" - resolved "https://registry.npmjs.org/meros/-/meros-1.2.0.tgz" - integrity sha512-3QRZIS707pZQnijHdhbttXRWwrHhZJ/gzolneoxKVz9N/xmsvY/7Ls8lpnI9gxbgxjcHsAVEW3mgwiZCo6kkJQ== +meros@^1.2.1: + version "1.3.0" + resolved "https://registry.npmjs.org/meros/-/meros-1.3.0.tgz#c617d2092739d55286bf618129280f362e6242f2" + integrity sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w== methods@^1.1.2, methods@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micro-ftch@^0.3.1: + version "0.3.1" + resolved "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f" + integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg== micromatch@^4.0.4: version "4.0.5" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: braces "^3.0.2" @@ -7800,19 +7791,19 @@ micromatch@^4.0.4: mime-db@1.52.0: version "1.52.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" mime@1.6.0: version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@2.6.0: @@ -7822,35 +7813,35 @@ mime@2.6.0: mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== min-document@^2.19.0: version "2.19.0" - resolved "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz" - integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + resolved "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ== dependencies: dom-walk "^0.1.0" min-indent@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" + resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimal-polyfills@^2.1.5: - version "2.2.1" - resolved "https://registry.npmjs.org/minimal-polyfills/-/minimal-polyfills-2.2.1.tgz" - integrity sha512-WLmHQrsZob4rVYf8yHapZPNJZ3sspGa/sN8abuSD59b0FifDEE7HMfLUi24z7mPZqTpBXy4Svp+iGvAmclCmXg== +minimal-polyfills@^2.1.5, minimal-polyfills@^2.2.3: + version "2.2.3" + resolved "https://registry.npmjs.org/minimal-polyfills/-/minimal-polyfills-2.2.3.tgz#22af58de16807b325f29b83ca38ffb83e75ec3f4" + integrity sha512-oxdmJ9cL+xV72h0xYxp4tP2d5/fTBpP45H8DIOn9pASuF8a3IYTf+25fMGDYGiWW+MFsuog6KD6nfmhZJQ+uUw== minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== minimalistic-crypto-utils@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== minimatch@3.0.5: version "3.0.5" @@ -7859,23 +7850,30 @@ minimatch@3.0.5: dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" minimatch@^5.0.1: - version "5.1.1" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz#6c9dffcf9927ff2a31e74b5af11adf8b9604b022" - integrity sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g== + version "5.1.6" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.1: + version "9.0.3" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== dependencies: brace-expansion "^2.0.1" minimist-options@4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz" + resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== dependencies: arrify "^1.0.1" @@ -7883,13 +7881,13 @@ minimist-options@4.1.0: kind-of "^6.0.3" minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + version "1.2.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== minipass-collect@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz" + resolved "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== dependencies: minipass "^3.0.0" @@ -7905,16 +7903,27 @@ minipass-fetch@^2.0.3: optionalDependencies: encoding "^0.1.13" +minipass-fetch@^3.0.0: + version "3.0.4" + resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz#4d4d9b9f34053af6c6e597a64be8e66e42bf45b7" + integrity sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg== + dependencies: + minipass "^7.0.3" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + minipass-flush@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz" + resolved "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== dependencies: minipass "^3.0.0" minipass-json-stream@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz" + resolved "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== dependencies: jsonparse "^1.3.1" @@ -7922,42 +7931,38 @@ minipass-json-stream@^1.0.1: minipass-pipeline@^1.2.4: version "1.2.4" - resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz" + resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== dependencies: minipass "^3.0.0" minipass-sized@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz" + resolved "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== dependencies: minipass "^3.0.0" -minipass@^3.0.0, minipass@^3.1.1: - version "3.1.6" - resolved "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz" - integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== - dependencies: - yallist "^4.0.0" - -minipass@^3.1.6: +minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: version "3.3.6" resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== dependencies: yallist "^4.0.0" -minipass@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz#7cebb0f9fa7d56f0c5b17853cbe28838a8dbbd3b" - integrity sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw== - dependencies: - yallist "^4.0.0" +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz#05ea638da44e475037ed94d1c7efcc76a25e1974" + integrity sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg== minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: minipass "^3.0.0" @@ -7965,7 +7970,7 @@ minizlib@^2.1.1, minizlib@^2.1.2: mkdirp-infer-owner@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz" + resolved "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" integrity sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw== dependencies: chownr "^2.0.0" @@ -7974,29 +7979,29 @@ mkdirp-infer-owner@^2.0.0: mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== modify-values@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz" + resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -moment-timezone@^0.5.34: - version "0.5.34" - resolved "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz" - integrity sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg== +moment-timezone@^0.5.43: + version "0.5.43" + resolved "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz#3dd7f3d0c67f78c23cd1906b9b2137a09b3c4790" + integrity sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ== dependencies: - moment ">= 2.9.0" + moment "^2.29.4" -"moment@>= 2.9.0", moment@^2.29.1: - version "2.29.3" - resolved "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz" - integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw== +moment@^2.29.4: + version "2.29.4" + resolved "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== morgan@1.10.0: version "1.10.0" - resolved "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz" + resolved "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== dependencies: basic-auth "~2.0.1" @@ -8007,7 +8012,7 @@ morgan@1.10.0: morgan@1.9.1: version "1.9.1" - resolved "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz" + resolved "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz#0a8d16734a1d9afbc824b99df87e738e58e2da59" integrity sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA== dependencies: basic-auth "~2.0.0" @@ -8018,22 +8023,22 @@ morgan@1.9.1: ms@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== ms@2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== ms@2.1.3, ms@^2.0.0: version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== multimatch@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz" + resolved "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== dependencies: "@types/minimatch" "^3.0.3" @@ -8044,13 +8049,13 @@ multimatch@^5.0.0: mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" - resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.14.0: - version "2.15.0" - resolved "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz" - integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== + version "2.18.0" + resolved "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" + integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== natural-compare@^1.4.0: version "1.4.0" @@ -8062,72 +8067,58 @@ negotiator@0.6.3, negotiator@^0.6.3: resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -neo-async@^2.6.0: +neo-async@^2.6.2: version "2.6.2" - resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== ngeohash@0.6.3: version "0.6.3" - resolved "https://registry.npmjs.org/ngeohash/-/ngeohash-0.6.3.tgz" + resolved "https://registry.npmjs.org/ngeohash/-/ngeohash-0.6.3.tgz#10b1e80be5488262ec95c56cf2dbb6c45fbdf245" integrity sha512-kltF0cOxgx1AbmVzKxYZaoB0aj7mOxZeHaerEtQV0YaqnkXNq26WWqMmJ6lTqShYxVRWZ/mwvvTrNeOwdslWiw== -nock@13.2.4: - version "13.2.4" - resolved "https://registry.npmjs.org/nock/-/nock-13.2.4.tgz#43a309d93143ee5cdcca91358614e7bde56d20e1" - integrity sha512-8GPznwxcPNCH/h8B+XZcKjYPXnUV5clOKCjAqyjsiqA++MpNx9E9+t8YPp0MbThO+KauRo7aZJ1WuIZmOrT2Ug== +nock@13.3.3: + version "13.3.3" + resolved "https://registry.npmjs.org/nock/-/nock-13.3.3.tgz#179759c07d3f88ad3e794ace885629c1adfd3fe7" + integrity sha512-z+KUlILy9SK/RjpeXDiDUEAq4T94ADPHE3qaRkf66mpEhzc/ytOMm3Bwdrbq6k1tMWkbdujiKim3G2tfQARuJw== dependencies: debug "^4.1.0" json-stringify-safe "^5.0.1" - lodash.set "^4.3.2" + lodash "^4.17.21" propagate "^2.0.0" -node-addon-api@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz" - integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== - node-addon-api@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== -node-domexception@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" - integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== - -node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7: - version "2.6.7" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== +node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" node-forge@^1.3.1: version "1.3.1" - resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -node-gyp-build@^4.2.0: - version "4.4.0" - resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz" - integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== - node-gyp-build@^4.3.0: - version "4.5.0" - resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + version "4.6.1" + resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz#24b6d075e5e391b8d5539d98c7fc5c210cac8a3e" + integrity sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ== node-gyp@^9.0.0: - version "9.3.0" - resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-9.3.0.tgz#f8eefe77f0ad8edb3b3b898409b53e697642b319" - integrity sha512-A6rJWfXFz7TQNjpldJ915WFb1LnhO4lIve3ANPbWreuEoLoKlFT3sxIepPBkLhM27crW8YmN+pjlgbasH6cH/Q== + version "9.4.0" + resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz#2a7a91c7cba4eccfd95e949369f27c9ba704f369" + integrity sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg== dependencies: env-paths "^2.2.0" + exponential-backoff "^3.1.1" glob "^7.1.4" graceful-fs "^4.2.6" - make-fetch-happen "^10.0.3" + make-fetch-happen "^11.0.3" nopt "^6.0.0" npmlog "^6.0.0" rimraf "^3.0.2" @@ -8148,14 +8139,14 @@ node-pre-gyp-github@1.4.4: "@octokit/rest" "18.12.0" commander "7.2.0" -node-releases@^2.0.5: - version "2.0.5" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" - integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== nopt@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz" + resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== dependencies: abbrev "1" @@ -8169,7 +8160,7 @@ nopt@^6.0.0: normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" @@ -8179,7 +8170,7 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: normalize-package-data@^3.0.0: version "3.0.3" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== dependencies: hosted-git-info "^4.0.1" @@ -8197,14 +8188,14 @@ normalize-package-data@^4.0.0: semver "^7.3.5" validate-npm-package-license "^3.0.4" -normalize-path@^3.0.0, normalize-path@~3.0.0: +normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== npm-bundled@^1.1.1: version "1.1.2" - resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz" + resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== dependencies: npm-normalize-package-bin "^1.0.1" @@ -8225,7 +8216,7 @@ npm-install-checks@^5.0.0: npm-normalize-package-bin@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz" + resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== npm-normalize-package-bin@^2.0.0: @@ -8287,14 +8278,14 @@ npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1, npm-registry-fetch@^13.3 npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" npmlog@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz" + resolved "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== dependencies: are-we-there-yet "^2.0.0" @@ -8314,31 +8305,25 @@ npmlog@^6.0.0, npmlog@^6.0.2: number-to-bn@1.7.0: version "1.7.0" - resolved "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz" - integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= + resolved "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig== dependencies: bn.js "4.11.6" strip-hex-prefix "1.0.0" -nwsapi@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" - integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== - -nx@15.3.0, "nx@>=14.8.6 < 16": - version "15.3.0" - resolved "https://registry.npmjs.org/nx/-/nx-15.3.0.tgz#50916064145cf33ba68fb8bd03ff8ffc2b9ebc7b" - integrity sha512-5tBrEF2zDkGBDfe8wThazJqBDhsVkRrxc6OttzfBmkXP4VPp8w5MMtUEOry181AXKfjDGkw//UnCSkUNynTDlw== +nx@15.9.7, "nx@>=14.8.6 < 16": + version "15.9.7" + resolved "https://registry.npmjs.org/nx/-/nx-15.9.7.tgz#f0e713cedb8637a517d9c4795c99afec4959a1b6" + integrity sha512-1qlEeDjX9OKZEryC8i4bA+twNg+lB5RKrozlNwWx/lLJHqWPUfvUTvxh+uxlPYL9KzVReQjUuxMLFMsHNqWUrA== dependencies: - "@nrwl/cli" "15.3.0" - "@nrwl/tao" "15.3.0" + "@nrwl/cli" "15.9.7" + "@nrwl/tao" "15.9.7" "@parcel/watcher" "2.0.4" "@yarnpkg/lockfile" "^1.1.0" - "@yarnpkg/parsers" "^3.0.0-rc.18" + "@yarnpkg/parsers" "3.0.0-rc.46" "@zkochan/js-yaml" "0.0.6" axios "^1.0.0" - chalk "4.1.0" - chokidar "^3.5.1" + chalk "^4.1.0" cli-cursor "3.1.0" cli-spinners "2.6.1" cliui "^7.0.2" @@ -8347,122 +8332,121 @@ nx@15.3.0, "nx@>=14.8.6 < 16": fast-glob "3.2.7" figures "3.2.0" flat "^5.0.2" - fs-extra "^10.1.0" + fs-extra "^11.1.0" glob "7.1.4" ignore "^5.0.4" js-yaml "4.1.0" jsonc-parser "3.2.0" + lines-and-columns "~2.0.3" minimatch "3.0.5" npm-run-path "^4.0.1" open "^8.4.0" - semver "7.3.4" + semver "7.5.4" string-width "^4.2.3" strong-log-transformer "^2.1.0" tar-stream "~2.2.0" tmp "~0.2.1" - tsconfig-paths "^3.9.0" + tsconfig-paths "^4.1.2" tslib "^2.3.0" v8-compile-cache "2.3.0" yargs "^17.6.2" yargs-parser "21.1.1" + optionalDependencies: + "@nrwl/nx-darwin-arm64" "15.9.7" + "@nrwl/nx-darwin-x64" "15.9.7" + "@nrwl/nx-linux-arm-gnueabihf" "15.9.7" + "@nrwl/nx-linux-arm64-gnu" "15.9.7" + "@nrwl/nx-linux-arm64-musl" "15.9.7" + "@nrwl/nx-linux-x64-gnu" "15.9.7" + "@nrwl/nx-linux-x64-musl" "15.9.7" + "@nrwl/nx-win32-arm64-msvc" "15.9.7" + "@nrwl/nx-win32-x64-msvc" "15.9.7" object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-hash@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== object-inspect@^1.9.0: - version "1.12.0" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz" - integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + version "1.12.3" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== on-exit-leak-free@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz" + resolved "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209" integrity sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg== -on-finished@^2.3.0: +on-finished@2.4.1, on-finished@^2.3.0: version "2.4.1" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" on-finished@~2.3.0: version "2.3.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== dependencies: ee-first "1.1.1" on-headers@~1.0.1, on-headers@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" + resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== -once@1.4.0, once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" open@^8.4.0: - version "8.4.0" - resolved "https://registry.npmjs.org/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" - integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== + version "8.4.2" + resolved "https://registry.npmjs.org/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== dependencies: define-lazy-prop "^2.0.0" is-docker "^2.1.1" is-wsl "^2.2.0" optimism@^0.16.1: - version "0.16.1" - resolved "https://registry.npmjs.org/optimism/-/optimism-0.16.1.tgz" - integrity sha512-64i+Uw3otrndfq5kaoGNoY7pvOhSsjFEN4bdEFh80MWVk/dbgJfMv7VFDeCT8LxNAlEVhQmdVEbfE7X2nWNIIg== + version "0.16.2" + resolved "https://registry.npmjs.org/optimism/-/optimism-0.16.2.tgz#519b0c78b3b30954baed0defe5143de7776bf081" + integrity sha512-zWNbgWj+3vLEjZNIh/okkY2EUfX+vB9TJopzIZwT1xxaMqC5hRLLraePod4c5n4He08xuXNH+zhKFFCu390wiQ== dependencies: - "@wry/context" "^0.6.0" + "@wry/context" "^0.7.0" "@wry/trie" "^0.3.0" -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" ora@^4.0.0: version "4.1.1" - resolved "https://registry.npmjs.org/ora/-/ora-4.1.1.tgz" + resolved "https://registry.npmjs.org/ora/-/ora-4.1.1.tgz#566cc0348a15c36f5f0e979612842e02ba9dddbc" integrity sha512-sjYP8QyVWBpBZWD6Vr1M/KwknSw6kJOz41tvGMlwWeClHBtYKTbHMki1PsLZnxKpXMPbTKv9b3pjQu3REib96A== dependencies: chalk "^3.0.0" @@ -8491,86 +8475,93 @@ ora@^5.4.1: os-tmpdir@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== p-filter@2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz" + resolved "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz#1b1472562ae7a0f742f0f3d3d3718ea66ff9c09c" integrity sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw== dependencies: p-map "^2.0.0" p-finally@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== p-finally@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz" + resolved "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== -p-limit@3.1.0, p-limit@^3.0.0: +p-limit@3.1.0, p-limit@^3.0.0, p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-limit@^1.1.0: version "1.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== dependencies: p-try "^1.0.0" p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-locate@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== dependencies: p-limit "^1.1.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map-series@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/p-map-series/-/p-map-series-2.1.0.tgz" + resolved "https://registry.npmjs.org/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== p-map@4.0.0, p-map@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" + resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" p-map@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz" + resolved "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== p-pipe@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz" + resolved "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== p-queue@6.6.2, p-queue@^6.6.2: version "6.6.2" - resolved "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz" + resolved "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== dependencies: eventemitter3 "^4.0.4" @@ -8578,12 +8569,12 @@ p-queue@6.6.2, p-queue@^6.6.2: p-reduce@2.1.0, p-reduce@^2.0.0, p-reduce@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz" + resolved "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" integrity sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw== p-retry@4.6.1: version "4.6.1" - resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz" + resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz#8fcddd5cdf7a67a0911a9cf2ef0e5df7f602316c" integrity sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA== dependencies: "@types/retry" "^0.12.0" @@ -8596,31 +8587,31 @@ p-timeout@4.1.0: p-timeout@^3.2.0: version "3.2.0" - resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz" + resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== dependencies: p-finally "^1.0.0" p-try@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== p-try@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== p-waterfall@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/p-waterfall/-/p-waterfall-2.1.1.tgz" + resolved "https://registry.npmjs.org/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" integrity sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw== dependencies: p-reduce "^2.0.0" packet-reader@1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz" + resolved "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== pacote@^13.0.3, pacote@^13.6.1: @@ -8652,7 +8643,7 @@ pacote@^13.0.3, pacote@^13.6.1: parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" @@ -8667,21 +8658,21 @@ parse-conflict-json@^2.0.1: just-diff-apply "^5.2.0" parse-duration@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/parse-duration/-/parse-duration-1.0.2.tgz" - integrity sha512-Dg27N6mfok+ow1a2rj/nRjtCfaKrHUZV2SJpEn/s8GaVUSlf4GGRCRP1c13Hj+wfPKVMrFDqLMLITkYKgKxyyg== + version "1.1.0" + resolved "https://registry.npmjs.org/parse-duration/-/parse-duration-1.1.0.tgz#5192084c5d8f2a3fd676d04a451dbd2e05a1819c" + integrity sha512-z6t9dvSJYaPoQq7quMzdEagSFtpGu+utzHqqxmpVWNNZRIXnvqyCvn9XsTdh7c/w0Bqmdz3RB3YnRaKtpRtEXQ== parse-json@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== dependencies: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -8691,7 +8682,7 @@ parse-json@^5.0.0, parse-json@^5.2.0: parse-ms@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz" + resolved "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== parse-path@^7.0.0: @@ -8708,99 +8699,101 @@ parse-url@^8.1.0: dependencies: parse-path "^7.0.0" -parse5@6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== - parseurl@~1.3.3: version "1.3.3" - resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +parsimmon@^1.18.1: + version "1.18.1" + resolved "https://registry.npmjs.org/parsimmon/-/parsimmon-1.18.1.tgz#d8dd9c28745647d02fc6566f217690897eed7709" + integrity sha512-u7p959wLfGAhJpSDJVYXoyMCXWYwHia78HhRBWqk7AIbxdmlrfdp5wX0l3xv/iTSH5HvhN9K7o26hwwpgS5Nmw== + path-exists@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== + dependencies: + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@0.1.7: version "0.1.7" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== path-type@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz" + resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== dependencies: pify "^3.0.0" path-type@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pbkdf2@^3.0.17: - version "3.1.2" - resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz" - integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" +pg-cloudflare@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98" + integrity sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q== -pg-connection-string@^2.5.0: - version "2.5.0" - resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz" - integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ== +pg-connection-string@^2.6.1, pg-connection-string@^2.6.2: + version "2.6.2" + resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz#713d82053de4e2bd166fab70cd4f26ad36aab475" + integrity sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA== pg-hstore@2.3.4: version "2.3.4" - resolved "https://registry.npmjs.org/pg-hstore/-/pg-hstore-2.3.4.tgz" + resolved "https://registry.npmjs.org/pg-hstore/-/pg-hstore-2.3.4.tgz#4425e3e2a3e15d2a334c35581186c27cf2e9b8dd" integrity sha512-N3SGs/Rf+xA1M2/n0JBiXFDVMzdekwLZLAO0g7mpDY9ouX+fDI7jS6kTq3JujmYbtNSJ53TJ0q4G98KVZSM4EA== dependencies: underscore "^1.13.1" pg-int8@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz" + resolved "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== -pg-pool@^3.5.1: - version "3.5.1" - resolved "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.1.tgz" - integrity sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ== +pg-pool@^3.6.1: + version "3.6.1" + resolved "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz#5a902eda79a8d7e3c928b77abf776b3cb7d351f7" + integrity sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og== -pg-protocol@^1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz" - integrity sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ== +pg-protocol@^1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz#4c91613c0315349363af2084608db843502f8833" + integrity sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q== pg-types@^2.1.0: version "2.2.0" - resolved "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz" + resolved "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== dependencies: pg-int8 "1.0.1" @@ -8809,22 +8802,24 @@ pg-types@^2.1.0: postgres-date "~1.0.4" postgres-interval "^1.1.0" -pg@8.7.3: - version "8.7.3" - resolved "https://registry.npmjs.org/pg/-/pg-8.7.3.tgz" - integrity sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw== +pg@8.11.3: + version "8.11.3" + resolved "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz#d7db6e3fe268fcedd65b8e4599cda0b8b4bf76cb" + integrity sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g== dependencies: buffer-writer "2.0.0" packet-reader "1.0.0" - pg-connection-string "^2.5.0" - pg-pool "^3.5.1" - pg-protocol "^1.5.0" + pg-connection-string "^2.6.2" + pg-pool "^3.6.1" + pg-protocol "^1.6.0" pg-types "^2.1.0" pgpass "1.x" + optionalDependencies: + pg-cloudflare "^1.1.1" pgpass@1.x: version "1.0.5" - resolved "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz" + resolved "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d" integrity sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug== dependencies: split2 "^4.1.0" @@ -8834,34 +8829,34 @@ picocolors@^1.0.0: resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pify@^2.3.0: version "2.3.0" - resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== pify@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== pify@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" + resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pify@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz" + resolved "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== pino-abstract-transport@v0.5.0: version "0.5.0" - resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz" + resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz#4b54348d8f73713bfd14e3dc44228739aa13d9c0" integrity sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ== dependencies: duplexify "^4.1.2" @@ -8869,24 +8864,24 @@ pino-abstract-transport@v0.5.0: pino-multi-stream@6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/pino-multi-stream/-/pino-multi-stream-6.0.0.tgz" + resolved "https://registry.npmjs.org/pino-multi-stream/-/pino-multi-stream-6.0.0.tgz#2116bca740cb5eb606f430b20fd480f4944b2b99" integrity sha512-oCuTtaDSUB5xK1S45r9oWE0Dj8RWdHVvaGTft5pO/rmzgIqQRkilf5Ooilz3uRm0IYj8sPRho3lVx48LCmXjvQ== dependencies: pino "^7.0.0" pino-std-serializers@^3.1.0: version "3.2.0" - resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz" + resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz#b56487c402d882eb96cd67c257868016b61ad671" integrity sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg== pino-std-serializers@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz" + resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz#1791ccd2539c091ae49ce9993205e2cd5dbba1e2" integrity sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q== pino@7.6.0: version "7.6.0" - resolved "https://registry.npmjs.org/pino/-/pino-7.6.0.tgz" + resolved "https://registry.npmjs.org/pino/-/pino-7.6.0.tgz#f9abd13e221e15855e3cf6e88a2b6280527b3800" integrity sha512-CCCdryvM/chT0CDt9jQ1//z62RpSXPrzUFUpY4b8eKCVq3T2T3UF6DomoczkPze9d6VFiTyVF6Y8A6F9iAyAxg== dependencies: fast-redact "^3.0.0" @@ -8902,7 +8897,7 @@ pino@7.6.0: pino@^6.13.0: version "6.14.0" - resolved "https://registry.npmjs.org/pino/-/pino-6.14.0.tgz" + resolved "https://registry.npmjs.org/pino/-/pino-6.14.0.tgz#b745ea87a99a6c4c9b374e4f29ca7910d4c69f78" integrity sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg== dependencies: fast-redact "^3.0.0" @@ -8914,9 +8909,9 @@ pino@^6.13.0: sonic-boom "^1.0.2" pino@^7.0.0: - version "7.10.0" - resolved "https://registry.npmjs.org/pino/-/pino-7.10.0.tgz" - integrity sha512-T6R92jy/APDElEuOk0gqa4nds3ZgqFbHde2X0g8XorlyPlVGlr9T5KQphtp72a3ByKOdZMg/gM/0IprpGQfTWg== + version "7.11.0" + resolved "https://registry.npmjs.org/pino/-/pino-7.11.0.tgz#0f0ea5c4683dc91388081d44bff10c83125066f6" + integrity sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg== dependencies: atomic-sleep "^1.0.0" fast-redact "^3.0.0" @@ -8931,47 +8926,47 @@ pino@^7.0.0: thread-stream "^0.15.1" pirates@^4.0.4: - version "4.0.5" - resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + version "4.0.6" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== pkg-dir@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" pluralize@^8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz" + resolved "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== postgres-array@~2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz" + resolved "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== postgres-bytea@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz" - integrity sha1-AntTPAqokOJtFy1Hz5zOzFIazTU= + resolved "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w== postgres-date@~1.0.4: version "1.0.7" - resolved "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz" + resolved "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8" integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== postgres-interval@^1.1.0: version "1.2.0" - resolved "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz" + resolved "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== dependencies: xtend "^4.0.0" pprof@3.2.0: version "3.2.0" - resolved "https://registry.npmjs.org/pprof/-/pprof-3.2.0.tgz" + resolved "https://registry.npmjs.org/pprof/-/pprof-3.2.0.tgz#5a60638dc51a61128a3d57c74514e8fd99e93722" integrity sha512-yhORhVWefg94HZgjVa6CDtYSNZJnJzZ82d4pkmrZJxf1/Y29Me/uHYLEVo6KawKKFhQywl5cGbkdnVx9bZoMew== dependencies: "@mapbox/node-pre-gyp" "^1.0.0" @@ -8990,33 +8985,28 @@ prelude-ls@^1.2.1: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== - -prettier@2.6.2: - version "2.6.2" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" - integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== +prettier@3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643" + integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg== prettier@^2.3.1: - version "2.7.0" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.7.0.tgz#a4fdae07e5596c51c9857ea676cd41a0163879d6" - integrity sha512-nwoX4GMFgxoPC6diHvSwmK/4yU8FFH3V8XWtLQrbj4IBsK2pkYhG4kf/ljF/haaZ/aii+wNJqISrCDPgxGWDVQ== + version "2.8.8" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== -pretty-format@^27.0.0, pretty-format@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" - integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== dependencies: - ansi-regex "^5.0.1" + "@jest/schemas" "^29.6.3" ansi-styles "^5.0.0" - react-is "^17.0.1" + react-is "^18.0.0" pretty-ms@^7.0.0: version "7.0.1" - resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz" + resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== dependencies: parse-ms "^2.1.0" @@ -9028,23 +9018,23 @@ proc-log@^2.0.0, proc-log@^2.0.1: process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== process-warning@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz" + resolved "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== process@^0.11.10: version "0.11.10" - resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -prom-client@14.0.1: - version "14.0.1" - resolved "https://registry.npmjs.org/prom-client/-/prom-client-14.0.1.tgz" - integrity sha512-HxTArb6fkOntQHoRGvv4qd/BkorjliiuO2uSWC2KC17MUTKYttWdDoXX/vxOhQdkoECEM9BBH0pj2l8G8kev6w== +prom-client@14.2.0: + version "14.2.0" + resolved "https://registry.npmjs.org/prom-client/-/prom-client-14.2.0.tgz#ca94504e64156f6506574c25fb1c34df7812cf11" + integrity sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA== dependencies: tdigest "^0.1.1" @@ -9054,18 +9044,18 @@ promise-all-reject-late@^1.0.0: integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== promise-call-limit@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-1.0.1.tgz#4bdee03aeb85674385ca934da7114e9bcd3c6e24" - integrity sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q== + version "1.0.2" + resolved "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-1.0.2.tgz#f64b8dd9ef7693c9c7613e7dfe8d6d24de3031ea" + integrity sha512-1vTUnfI2hzui8AEIixbdAJlFY4LFDXqQswy/2eOlThAscXCY4It8FdVuI0fMJGAB2aWGbdQf/gv0skKYXmdrHA== promise-inflight@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== promise-retry@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz" + resolved "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== dependencies: err-code "^2.0.2" @@ -9081,14 +9071,14 @@ prompts@^2.0.1: promzard@^0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz" - integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4= + resolved "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" + integrity sha512-JZeYqd7UAcHCwI+sTOeUDYkvEU+1bQ7iE0UT1MgB/tERkAPkesW46MrpIySzODi+owTjZtiF8Ay5j9m60KmMBw== dependencies: read "1" prop-types@^15.7.2: version "15.8.1" - resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" @@ -9102,20 +9092,20 @@ propagate@^2.0.0: proto-list@~1.2.1: version "1.2.4" - resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= + resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== proto3-json-serializer@^0.1.8: - version "0.1.8" - resolved "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.8.tgz" - integrity sha512-ACilkB6s1U1gWnl5jtICpnDai4VCxmI9GFxuEaYdxtDG2oVI3sVFIUsvUZcQbJgtPM6p+zqKbjTKQZp6Y4FpQw== + version "0.1.9" + resolved "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.9.tgz#705ddb41b009dd3e6fcd8123edd72926abf65a34" + integrity sha512-A60IisqvnuI45qNRygJjrnNjX2TMdQGMY+57tR3nul3ZgO2zXkR9OGR8AXxJhkqx84g0FTnrfi3D5fWMSdANdQ== dependencies: protobufjs "^6.11.2" -protobufjs@6.11.2, protobufjs@^6.10.0, protobufjs@^6.11.2, protobufjs@~6.11.0: - version "6.11.2" - resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz" - integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== +protobufjs@6.11.3: + version "6.11.3" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" + integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -9131,6 +9121,43 @@ protobufjs@6.11.2, protobufjs@^6.10.0, protobufjs@^6.11.2, protobufjs@~6.11.0: "@types/node" ">=13.7.0" long "^4.0.0" +protobufjs@^6.11.2, protobufjs@^6.11.3, protobufjs@~6.11.0: + version "6.11.4" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" + integrity sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + +protobufjs@^7.2.4: + version "7.2.5" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz#45d5c57387a6d29a17aab6846dcc283f9b8e7f2d" + integrity sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + protocols@^2.0.0, protocols@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" @@ -9138,7 +9165,7 @@ protocols@^2.0.0, protocols@^2.0.1: proxy-addr@^2.0.7, proxy-addr@~2.0.7: version "2.0.7" - resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: forwarded "0.2.0" @@ -9149,14 +9176,9 @@ proxy-from-env@^1.1.0: resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -psl@^1.1.33: - version "1.8.0" - resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - pump@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" @@ -9164,108 +9186,100 @@ pump@^3.0.0: pumpify@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz" + resolved "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz#abfc7b5a621307c728b551decbbefb51f0e4aa1e" integrity sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw== dependencies: duplexify "^4.1.1" inherits "^2.0.3" pump "^3.0.0" -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== -q@^1.5.1: - version "1.5.1" - resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -qs@6.9.3: - version "6.9.3" - resolved "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e" - integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw== +pure-rand@^6.0.0: + version "6.0.3" + resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz#3c9e6b53c09e52ac3cedffc85ab7c1c7094b38cb" + integrity sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w== -qs@6.9.6: - version "6.9.6" - resolved "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz" - integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== +q@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -qs@6.9.7: - version "6.9.7" - resolved "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz" - integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== +qs@6.11.0: + version "6.11.0" + resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" -qs@^6.10.3: - version "6.10.5" - resolved "https://registry.npmjs.org/qs/-/qs-6.10.5.tgz#974715920a80ff6a262264acd2c7e6c2a53282b4" - integrity sha512-O5RlPh0VFtR78y79rgcgKK4wbAI0C5zGVLztOIdpWX6ep368q5Hv6XRxDvXuZ9q3C6v+e3n8UfZZJw7IIG27eQ== +qs@^6.11.0: + version "6.11.2" + resolved "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== dependencies: side-channel "^1.0.4" queue-microtask@^1.1.2, queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== quick-format-unescaped@^4.0.3: version "4.0.4" - resolved "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz" + resolved "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== quick-lru@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== ramda@^0.24.1: version "0.24.1" - resolved "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz" - integrity sha1-w7d1UZfzW43DUCIoJixMkd22uFc= + resolved "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" + integrity sha512-HEm619G8PaZMfkqCa23qiOe7r3R0brPu7ZgOsgKUsnvLhd0qhc/vTjkUovomgPWa5ECBa08fJZixth9LaoBo5w== ramdasauce@^2.1.0: version "2.1.3" - resolved "https://registry.npmjs.org/ramdasauce/-/ramdasauce-2.1.3.tgz" + resolved "https://registry.npmjs.org/ramdasauce/-/ramdasauce-2.1.3.tgz#acb45ecc7e4fc4d6f39e19989b4a16dff383e9c2" integrity sha512-Ml3CPim4SKwmg5g9UI77lnRSeKr/kQw7YhQ6rfdMcBYy6DMlwmkEwQqjygJ3OhxPR+NfFfpjKl3Tf8GXckaqqg== dependencies: ramda "^0.24.1" randombytes@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" range-parser@~1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.2: - version "2.4.2" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz" - integrity sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ== - dependencies: - bytes "3.1.1" - http-errors "1.8.1" - iconv-lite "0.4.24" - unpipe "1.0.0" - -raw-body@2.4.3: - version "2.4.3" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz" - integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== dependencies: bytes "3.1.2" - http-errors "1.8.1" + http-errors "2.0.0" iconv-lite "0.4.24" unpipe "1.0.0" -raw-body@^2.4.1: - version "2.5.1" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== +raw-body@2.5.2, raw-body@^2.4.1: + version "2.5.2" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== dependencies: bytes "3.1.2" http-errors "2.0.0" @@ -9274,13 +9288,13 @@ raw-body@^2.4.1: react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" - resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== read-cmd-shim@^3.0.0: version "3.0.1" @@ -9307,15 +9321,15 @@ read-package-json@^5.0.0, read-package-json@^5.0.1: read-pkg-up@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz" - integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + integrity sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw== dependencies: find-up "^2.0.0" read-pkg "^3.0.0" read-pkg-up@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== dependencies: find-up "^4.1.0" @@ -9324,7 +9338,7 @@ read-pkg-up@^7.0.1: read-pkg@5.2.0, read-pkg@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== dependencies: "@types/normalize-package-data" "^2.4.0" @@ -9334,8 +9348,8 @@ read-pkg@5.2.0, read-pkg@^5.2.0: read-pkg@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA== dependencies: load-json-file "^4.0.0" normalize-package-data "^2.3.2" @@ -9343,24 +9357,24 @@ read-pkg@^3.0.0: read@1, read@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/read/-/read-1.0.7.tgz" - integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= + resolved "https://registry.npmjs.org/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + integrity sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ== dependencies: mute-stream "~0.0.4" "readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + version "2.3.8" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -9380,21 +9394,14 @@ readdir-scoped-modules@^1.1.0: graceful-fs "^4.1.2" once "^1.3.0" -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - real-require@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz" + resolved "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381" integrity sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg== redent@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz" + resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== dependencies: indent-string "^4.0.0" @@ -9405,55 +9412,50 @@ reduce-flatten@^2.0.0: resolved "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== -regexpp@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-from-string@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== resolve@^1.10.0, resolve@^1.20.0: - version "1.22.0" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== + version "1.22.6" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362" + integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== dependencies: - is-core-module "^2.8.1" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" @@ -9461,17 +9463,17 @@ restore-cursor@^3.1.0: ret@~0.2.0: version "0.2.2" - resolved "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz" + resolved "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz#b6861782a1f4762dce43402a71eb7a283f44573c" integrity sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ== -retry-as-promised@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-5.0.0.tgz" - integrity sha512-6S+5LvtTl2ggBumk04hBo/4Uf6fRJUwIgunGZ7CYEBCeufGFW1Pu6ucUf/UskHeWOIsUcLOGLFXPig5tR5V1nA== +retry-as-promised@^7.0.4: + version "7.0.4" + resolved "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz#9df73adaeea08cb2948b9d34990549dc13d800a2" + integrity sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA== retry-request@^4.0.0, retry-request@^4.2.2: version "4.2.2" - resolved "https://registry.npmjs.org/retry-request/-/retry-request-4.2.2.tgz" + resolved "https://registry.npmjs.org/retry-request/-/retry-request-4.2.2.tgz#b7d82210b6d2651ed249ba3497f07ea602f1a903" integrity sha512-xA93uxUD/rogV7BV59agW/JHPGXeREMWiZc9jhcwY4YdZ7QOtC7qbomYg0n4wyk2lJhggjvKvhNX8wln/Aldhg== dependencies: debug "^4.1.1" @@ -9479,267 +9481,216 @@ retry-request@^4.0.0, retry-request@^4.2.2: retry@^0.12.0: version "0.12.0" - resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== retry@^0.13.1: version "0.13.1" - resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" + resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== reusify@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rfdc@^1.1.4, rfdc@^1.2.0: version "1.3.0" - resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== rimraf@^2.6.3: version "2.7.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -rlp@^2.2.4: - version "2.2.7" - resolved "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz" - integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== - dependencies: - bn.js "^5.2.0" - run-async@^2.4.0: version "2.4.1" - resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" + resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-exclusive@^2.2.14: - version "2.2.14" - resolved "https://registry.npmjs.org/run-exclusive/-/run-exclusive-2.2.14.tgz" - integrity sha512-NHaQfB3zPJFx7p4M06AcmoK8xz/h8YDMCdy3jxfyoC9VqIbl1U+DiVjUuAYZBRMwvj5qkQnOUGfsmyUC4k46dg== + version "2.2.19" + resolved "https://registry.npmjs.org/run-exclusive/-/run-exclusive-2.2.19.tgz#37a2fb6e3671f8ae0d63521ebd1865fc796cf307" + integrity sha512-K3mdoAi7tjJ/qT7Flj90L7QyPozwUaAG+CVhkdDje4HLKXUYC3N/Jzkau3flHVDLQVhiHBtcimVodMjN9egYbA== dependencies: - minimal-polyfills "^2.1.5" + minimal-polyfills "^2.2.3" run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" rxjs@^7.5.5: - version "7.6.0" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz#361da5362b6ddaa691a2de0b4f2d32028f1eb5a2" - integrity sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ== + version "7.8.1" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex2@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz" + resolved "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz#b287524c397c7a2994470367e0185e1916b1f5b9" integrity sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ== dependencies: ret "~0.2.0" safe-stable-stringify@^2.1.0: - version "2.3.1" - resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz" - integrity sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg== + version "2.4.3" + resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" - -scrypt-js@3.0.1, scrypt-js@^3.0.0: +scrypt-js@3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz" + resolved "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== -secp256k1@^4.0.1: - version "4.0.3" - resolved "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz" - integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== - dependencies: - elliptic "^6.5.4" - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - secure-json-parse@^2.0.0: - version "2.4.0" - resolved "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz" - integrity sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg== + version "2.7.0" + resolved "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" + integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== semver-store@^0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz" + resolved "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz#ce602ff07df37080ec9f4fb40b29576547befbe9" integrity sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg== "semver@2 || 3 || 4 || 5", semver@^5.6.0: - version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@7.3.4: - version "7.3.4" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== +semver@7.5.4, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== - dependencies: - lru-cache "^6.0.0" - -semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -send@0.17.2: - version "0.17.2" - resolved "https://registry.npmjs.org/send/-/send-0.17.2.tgz" - integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== +send@0.18.0: + version "0.18.0" + resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== dependencies: debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" + depd "2.0.0" + destroy "1.2.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "1.8.1" + http-errors "2.0.0" mime "1.6.0" ms "2.1.3" - on-finished "~2.3.0" + on-finished "2.4.1" range-parser "~1.2.1" - statuses "~1.5.0" + statuses "2.0.1" sequelize-pool@^7.1.0: version "7.1.0" - resolved "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz" + resolved "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz#210b391af4002762f823188fd6ecfc7413020768" integrity sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg== -sequelize@6.19.0: - version "6.19.0" - resolved "https://registry.npmjs.org/sequelize/-/sequelize-6.19.0.tgz#bea0ac091d89cbbc94cabe0797b8c1359734e2e6" - integrity sha512-B3oGIdpYBERDjRDm74h7Ky67f6ZLcmBXOA7HscYObiOSo4pD7VBc9mtm44wNV7unc0uk8I1d30nbZBTQCE377A== +sequelize@6.33.0: + version "6.33.0" + resolved "https://registry.npmjs.org/sequelize/-/sequelize-6.33.0.tgz#45c70d52687f1c7eae3a0496f21f7055d99b46da" + integrity sha512-GkeCbqgaIcpyZ1EyXrDNIwktbfMldHAGOVXHGM4x8bxGSRAOql5htDWofPvwpfL/FoZ59CaFmfO3Mosv1lDbQw== dependencies: - "@types/debug" "^4.1.7" - "@types/validator" "^13.7.1" - debug "^4.3.3" - dottie "^2.0.2" - inflection "^1.13.2" + "@types/debug" "^4.1.8" + "@types/validator" "^13.7.17" + debug "^4.3.4" + dottie "^2.0.6" + inflection "^1.13.4" lodash "^4.17.21" - moment "^2.29.1" - moment-timezone "^0.5.34" - pg-connection-string "^2.5.0" - retry-as-promised "^5.0.0" - semver "^7.3.5" + moment "^2.29.4" + moment-timezone "^0.5.43" + pg-connection-string "^2.6.1" + retry-as-promised "^7.0.4" + semver "^7.5.4" sequelize-pool "^7.1.0" toposort-class "^1.0.1" uuid "^8.3.2" - validator "^13.7.0" + validator "^13.9.0" wkx "^0.5.0" -serve-static@1.14.2: - version "1.14.2" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz" - integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.17.2" + send "0.18.0" set-blocking@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== set-cookie-parser@^2.4.1: - version "2.4.8" - resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz" - integrity sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg== - -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + version "2.6.0" + resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz#131921e50f62ff1a66a461d7d62d7b21d5d15a51" + integrity sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ== setprototypeof@1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - shallow-clone@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== dependencies: kind-of "^6.0.2" shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== side-channel@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" @@ -9748,9 +9699,14 @@ side-channel@^1.0.4: signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-wcswidth@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz#8ab18ac0ae342f9d9b629604e54d2aa1ecb018b2" @@ -9763,12 +9719,12 @@ sisteransi@^1.0.5: slash@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" @@ -9777,7 +9733,7 @@ slice-ansi@^4.0.0: smart-buffer@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" + resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== socks-proxy-agent@^7.0.0: @@ -9790,109 +9746,116 @@ socks-proxy-agent@^7.0.0: socks "^2.6.2" socks@^2.6.2: - version "2.6.2" - resolved "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz" - integrity sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA== + version "2.7.1" + resolved "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" + integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== dependencies: - ip "^1.1.5" + ip "^2.0.0" smart-buffer "^4.2.0" sonic-boom@^1.0.2: version "1.4.1" - resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz" + resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz#d35d6a74076624f12e6f917ade7b9d75e918f53e" integrity sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg== dependencies: atomic-sleep "^1.0.0" flatstr "^1.0.12" sonic-boom@^2.2.1: - version "2.7.0" - resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.7.0.tgz" - integrity sha512-Ynxp0OGQG91wvDjCbFlRMHbSUmDq7dE/EgDeUJ/j+Q9x1FVkFry20cjLykxRSmlm3QS0B4JYAKE8239XKN4SHQ== + version "2.8.0" + resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz#c1def62a77425090e6ad7516aad8eb402e047611" + integrity sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg== dependencies: atomic-sleep "^1.0.0" sort-keys@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz" - integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= + resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + integrity sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg== dependencies: is-plain-obj "^1.0.0" sort-keys@^4.0.0: version "4.2.0" - resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz" + resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== dependencies: is-plain-obj "^2.0.0" -source-map-support@^0.5.6: - version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.7.3: - version "0.7.3" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + version "0.7.4" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + version "3.2.0" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: version "2.3.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.11" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz" - integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== + version "3.0.14" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.14.tgz#d16b09577b7de461c8c3e69734c4b9b467ac791f" + integrity sha512-U0eS5wcpu/O2/QZk6PcAMOA8H3ZuvRe4mFHA3Q+LNl1SRDmfQ+mD3RoD6tItqnvqubJ32m/zV2Z/ikSmxccD1Q== split2@^3.0.0, split2@^3.1.1: version "3.2.2" - resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz" + resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== dependencies: readable-stream "^3.0.0" split2@^4.0.0, split2@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz" - integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ== + version "4.2.0" + resolved "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== split@^1.0.0, split@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz" + resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== dependencies: through "2" sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +ssri@^10.0.0: + version "10.0.5" + resolved "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz#e49efcd6e36385196cb515d3a2ad6c3f0265ef8c" + integrity sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A== + dependencies: + minipass "^7.0.3" ssri@^9.0.0, ssri@^9.0.1: version "9.0.1" @@ -9902,38 +9865,43 @@ ssri@^9.0.0, ssri@^9.0.1: minipass "^3.1.1" stack-utils@^2.0.3: - version "2.0.5" - resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" - integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== + version "2.0.6" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" statuses@2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: +"statuses@>= 1.5.0 < 2": version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== stream-events@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz" + resolved "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== dependencies: stubs "^3.0.0" stream-shift@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz" + resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + string-argv@~0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz" - integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== + version "0.3.2" + resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== string-format@^2.0.0: version "2.0.0" @@ -9950,12 +9918,12 @@ string-length@^4.0.1: string-similarity@^4.0.1: version "4.0.4" - resolved "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz" + resolved "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz#42d01ab0b34660ea8a018da8f56a3309bb8b2a5b" integrity sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ== -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -9964,77 +9932,93 @@ string-similarity@^4.0.1: string-width@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" + resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== dependencies: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" string_decoder@~1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== dependencies: ansi-regex "^3.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== dependencies: - ansi-regex "^5.0.1" + ansi-regex "^6.0.1" strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== strip-bom@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-hex-prefix@1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz" - integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= + resolved "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A== dependencies: is-hex-prefixed "1.0.0" strip-indent@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== strong-log-transformer@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz" + resolved "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== dependencies: duplexer "^0.1.1" @@ -10043,55 +10027,43 @@ strong-log-transformer@^2.1.0: stubs@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz" - integrity sha1-6NK6H6nJBXAwPAMLaQD31fiavls= - -subscriptions-transport-ws@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz" - integrity sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ== - dependencies: - backo2 "^1.0.2" - eventemitter3 "^3.1.0" - iterall "^1.2.1" - symbol-observable "^1.0.4" - ws "^5.2.0 || ^6.0.0 || ^7.0.0" + resolved "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" + integrity sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw== -superagent@^7.1.0: - version "7.1.6" - resolved "https://registry.npmjs.org/superagent/-/superagent-7.1.6.tgz#64f303ed4e4aba1e9da319f134107a54cacdc9c6" - integrity sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g== +superagent@^8.0.5: + version "8.1.2" + resolved "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz#03cb7da3ec8b32472c9d20f6c2a57c7f3765f30b" + integrity sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA== dependencies: component-emitter "^1.3.0" - cookiejar "^2.1.3" + cookiejar "^2.1.4" debug "^4.3.4" fast-safe-stringify "^2.1.1" form-data "^4.0.0" - formidable "^2.0.1" + formidable "^2.1.2" methods "^1.1.2" mime "2.6.0" - qs "^6.10.3" - readable-stream "^3.6.0" - semver "^7.3.7" + qs "^6.11.0" + semver "^7.3.8" -supertest@6.2.2: - version "6.2.2" - resolved "https://registry.npmjs.org/supertest/-/supertest-6.2.2.tgz#04a5998fd3efaff187cb69f07a169755d655b001" - integrity sha512-wCw9WhAtKJsBvh07RaS+/By91NNE0Wh0DN19/hWPlBOU8tAfOtbZoVSV4xXeoKoxgPx0rx2y+y+8660XtE7jzg== +supertest@6.3.3: + version "6.3.3" + resolved "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz#42f4da199fee656106fd422c094cf6c9578141db" + integrity sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA== dependencies: methods "^1.1.2" - superagent "^7.1.0" + superagent "^8.0.5" supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" @@ -10103,42 +10075,16 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" - integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -symbol-observable@^1.0.4: - version "1.2.0" - resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz" - integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== - symbol-observable@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz" + resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - -sync-fetch@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.3.1.tgz" - integrity sha512-xj5qiCDap/03kpci5a+qc5wSJjc8ZSixgG2EUmH1B8Ea2sfWclQA7eH40hiHPCtkCn6MCk4Wb+dqcXdCy2PP3g== - dependencies: - buffer "^5.7.0" - node-fetch "^2.6.1" - table-layout@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" @@ -10151,7 +10097,7 @@ table-layout@^1.0.2: table@6.7.5: version "6.7.5" - resolved "https://registry.npmjs.org/table/-/table-6.7.5.tgz" + resolved "https://registry.npmjs.org/table/-/table-6.7.5.tgz#f04478c351ef3d8c7904f0e8be90a1b62417d238" integrity sha512-LFNeryOqiQHqCVKzhkymKwt6ozeRhlm8IL1mE8rNUurkir4heF6PzMyRgaTa4tlyPTGGgXuvVOF/OLWiH09Lqw== dependencies: ajv "^8.0.1" @@ -10171,40 +10117,28 @@ tar-stream@~2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.1.0, tar@^6.1.11: - version "6.1.11" - resolved "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz" - integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^3.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - -tar@^6.1.2: - version "6.1.13" - resolved "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" - integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== +tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: + version "6.2.0" + resolved "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73" + integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" - minipass "^4.0.0" + minipass "^5.0.0" minizlib "^2.1.1" mkdirp "^1.0.3" yallist "^4.0.0" tdigest@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz" - integrity sha1-Ljyyw56kSeVdHmzZEReszKRYgCE= + version "0.1.2" + resolved "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz#96c64bac4ff10746b910b0e23b515794e12faced" + integrity sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA== dependencies: - bintrees "1.0.1" + bintrees "1.0.2" teeny-request@^7.0.0: version "7.2.0" - resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-7.2.0.tgz" + resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-7.2.0.tgz#41347ece068f08d741e7b86df38a4498208b2633" integrity sha512-SyY0pek1zWsi0LRVAALem+avzMLc33MKW/JLLakdP4s9+D7+jHcy5x6P+h94g2QNZsAqQNfX5lsbd3WSeJXrrw== dependencies: http-proxy-agent "^5.0.0" @@ -10215,16 +10149,8 @@ teeny-request@^7.0.0: temp-dir@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz" - integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= - -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== - dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" + resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" + integrity sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ== test-exclude@^6.0.0: version "6.0.0" @@ -10237,7 +10163,7 @@ test-exclude@^6.0.0: text-extensions@^1.0.0: version "1.9.0" - resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz" + resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== text-table@^0.2.0: @@ -10247,26 +10173,21 @@ text-table@^0.2.0: thread-stream@^0.13.0: version "0.13.2" - resolved "https://registry.npmjs.org/thread-stream/-/thread-stream-0.13.2.tgz" + resolved "https://registry.npmjs.org/thread-stream/-/thread-stream-0.13.2.tgz#de8ea87584baee625c631947ec73494aa86131c8" integrity sha512-woZFt0cLFkPdhsa+IGpRo1jiSouaHxMIljzTgt30CMjBWoUYbbcHqnunW5Yv+BXko9H05MVIcxMipI3Jblallw== dependencies: real-require "^0.1.0" thread-stream@^0.15.1: version "0.15.2" - resolved "https://registry.npmjs.org/thread-stream/-/thread-stream-0.15.2.tgz" + resolved "https://registry.npmjs.org/thread-stream/-/thread-stream-0.15.2.tgz#fb95ad87d2f1e28f07116eb23d85aba3bc0425f4" integrity sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA== dependencies: real-require "^0.1.0" -throat@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" - integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== - through2@^2.0.0: version "2.0.5" - resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" + resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== dependencies: readable-stream "~2.3.6" @@ -10274,7 +10195,7 @@ through2@^2.0.0: through2@^3.0.1: version "3.0.2" - resolved "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz" + resolved "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== dependencies: inherits "^2.0.4" @@ -10282,34 +10203,34 @@ through2@^3.0.1: through2@^4.0.0: version "4.0.2" - resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz" + resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== dependencies: readable-stream "3" through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== tiny-invariant@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz" - integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== + version "1.3.1" + resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" + integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== tiny-lru@^7.0.0: version "7.0.6" - resolved "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz" + resolved "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz#b0c3cdede1e5882aa2d1ae21cb2ceccf2a331f24" integrity sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow== tiny-warning@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz" + resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== tmp@^0.0.33: version "0.0.33" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" @@ -10333,55 +10254,39 @@ to-fast-properties@^2.0.0: to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" toformat@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/toformat/-/toformat-2.0.0.tgz" + resolved "https://registry.npmjs.org/toformat/-/toformat-2.0.0.tgz#7a043fd2dfbe9021a4e36e508835ba32056739d8" integrity sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ== toidentifier@1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== toidentifier@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== toposort-class@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz" - integrity sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg= - -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.1.2" - -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" + resolved "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz#7ffd1f78c8be28c3ba45cd4e1a3f5ee193bd9988" + integrity sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg== tr46@~0.0.3: version "0.0.3" - resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== treeify@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz" + resolved "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== treeverse@^2.0.0: @@ -10391,13 +10296,18 @@ treeverse@^2.0.0: trim-newlines@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== +ts-api-utils@^1.0.1: + version "1.0.3" + resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" + integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== + ts-command-line-args@^2.2.0: - version "2.3.1" - resolved "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.3.1.tgz#b6188e42efc6cf7a8898e438a873fbb15505ddd6" - integrity sha512-FR3y7pLl/fuUNSmnPhfLArGqRrpojQgIEEOVzYx9DhTmfIN7C9RWSfpkJEF4J+Gk7aVx5pak8I7vWZsaN4N84g== + version "2.5.1" + resolved "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz#e64456b580d1d4f6d948824c274cf6fa5f45f7f0" + integrity sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw== dependencies: chalk "^4.1.0" command-line-args "^5.1.1" @@ -10405,9 +10315,9 @@ ts-command-line-args@^2.2.0: string-format "^2.0.0" ts-custom-error@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.2.0.tgz" - integrity sha512-cBvC2QjtvJ9JfWLvstVnI45Y46Y5dMxIaG1TDMGAD/R87hpvqFL+7LhvUDhnRCfOnx/xitollFWWvUKKKhbN0A== + version "3.3.1" + resolved "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.3.1.tgz#8bd3c8fc6b8dc8e1cb329267c45200f1e17a65d1" + integrity sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A== ts-essentials@^7.0.1: version "7.0.3" @@ -10416,31 +10326,31 @@ ts-essentials@^7.0.1: ts-invariant@^0.4.0: version "0.4.4" - resolved "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.4.4.tgz" + resolved "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.4.4.tgz#97a523518688f93aafad01b0e80eb803eb2abd86" integrity sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA== dependencies: tslib "^1.9.3" ts-invariant@^0.9.4: version "0.9.4" - resolved "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.9.4.tgz" + resolved "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.9.4.tgz#42ac6c791aade267dd9dc65276549df5c5d71cac" integrity sha512-63jtX/ZSwnUNi/WhXjnK8kz4cHHpYS60AnmA6ixz17l7E12a5puCWFlNpkne5Rl0J8TBPVHpGjsj4fxs8ObVLQ== dependencies: tslib "^2.1.0" -ts-jest@27.1.4: - version "27.1.4" - resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz#84d42cf0f4e7157a52e7c64b1492c46330943e00" - integrity sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ== +ts-jest@29.1.1: + version "29.1.1" + resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" + integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" - jest-util "^27.0.0" - json5 "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" lodash.memoize "4.x" make-error "1.x" - semver "7.x" - yargs-parser "20.x" + semver "^7.5.3" + yargs-parser "^21.0.1" ts-node@10.7.0: version "10.7.0" @@ -10461,38 +10371,30 @@ ts-node@10.7.0: v8-compile-cache-lib "^3.0.0" yn "3.1.1" -tsconfig-paths@^3.9.0: - version "3.14.1" - resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== +tsconfig-paths@^4.1.2: + version "4.2.0" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.1" + json5 "^2.2.2" minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.3: +tslib@^1.10.0, tslib@^1.9.3: version "1.14.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@~2.3.0: +tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0: + version "2.6.2" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +tslib@~2.3.0: version "2.3.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -tslib@^2.4.0: - version "2.4.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== - -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -10500,13 +10402,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== - dependencies: - prelude-ls "~1.1.2" - type-detect@4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -10514,7 +10409,7 @@ type-detect@4.0.8: type-fest@^0.18.0: version "0.18.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== type-fest@^0.20.2: @@ -10524,32 +10419,32 @@ type-fest@^0.20.2: type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-fest@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" integrity sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw== type-fest@^0.6.0: version "0.6.0" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== type-fest@^0.8.1: version "0.8.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-fest@^2.0.0: - version "2.12.2" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-2.12.2.tgz" - integrity sha512-qt6ylCGpLjZ7AaODxbpyBZSs9fCI9SkL3Z9q2oxMBQhs/uyY+VD8jHA8ULCGmWQJlBgqvO3EJeAngOHD8zQCrQ== + version "2.19.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== type-is@~1.6.18: version "1.6.18" - resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" + resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" @@ -10573,25 +10468,25 @@ typechain@8.0.0: typedarray-to-buffer@^3.1.5: version "3.1.5" - resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" + resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== dependencies: is-typedarray "^1.0.0" typedarray@^0.0.6: version "0.0.6" - resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@4.6.3: - version "4.6.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c" - integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw== +typescript@5.2.2: + version "5.2.2" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== "typescript@^3 || ^4": - version "4.9.4" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" - integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== + version "4.9.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== typical@^4.0.0: version "4.0.0" @@ -10604,13 +10499,13 @@ typical@^5.2.0: integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== uglify-js@^3.1.4: - version "3.15.4" - resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz" - integrity sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA== + version "3.17.4" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== umzug@3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/umzug/-/umzug-3.0.0.tgz" + resolved "https://registry.npmjs.org/umzug/-/umzug-3.0.0.tgz#de19183c0bce7e79d432f746ad423b68b38d485a" integrity sha512-uHeJsOJ7qtoIkZmtknpg+iSlI25m7glBA237pYp5fMsEKnRfRY+F4FP8eGx28g6b0VujkniC6Zlz09lbDFuBbw== dependencies: "@rushstack/ts-command-line" "^4.7.7" @@ -10622,14 +10517,9 @@ umzug@3.0.0: verror "^1.10.0" underscore@^1.13.1: - version "1.13.2" - resolved "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz" - integrity sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g== - -undici@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/undici/-/undici-5.0.0.tgz" - integrity sha512-VhUpiZ3No1DOPPQVQnsDZyfcbTTcHdcgWej1PdFnSvOeJmOVDgiOHkunJmBLfmjt4CqgPQddPVjSWW0dsTs5Yg== + version "1.13.6" + resolved "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" + integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== unique-filename@^2.0.0: version "2.0.1" @@ -10638,6 +10528,13 @@ unique-filename@^2.0.0: dependencies: unique-slug "^3.0.0" +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== + dependencies: + unique-slug "^4.0.0" + unique-slug@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" @@ -10645,56 +10542,76 @@ unique-slug@^3.0.0: dependencies: imurmurhash "^0.1.4" +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== + dependencies: + imurmurhash "^0.1.4" + universal-user-agent@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" + resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== -universalify@^0.1.0, universalify@^0.1.2: +universalify@^0.1.0: version "0.1.2" resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== universalify@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== upath@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz" + resolved "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== +update-browserslist-db@^1.0.11: + version "1.0.11" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" +urlpattern-polyfill@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-9.0.0.tgz#bc7e386bb12fd7898b58d1509df21d3c29ab3460" + integrity sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g== + utf8@3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz" + resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== utils-merge@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== uuid@^8.0.0, uuid@^8.3.2: version "8.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== v8-compile-cache-lib@^3.0.0: @@ -10702,23 +10619,23 @@ v8-compile-cache-lib@^3.0.0: resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-compile-cache@2.3.0, v8-compile-cache@^2.0.3: +v8-compile-cache@2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== -v8-to-istanbul@^8.1.0: - version "8.1.1" - resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" - integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== +v8-to-istanbul@^9.0.1: + version "9.1.0" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== dependencies: + "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" - source-map "^0.7.3" validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" @@ -10726,8 +10643,8 @@ validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: validate-npm-package-name@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz" - integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= + resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" + integrity sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw== dependencies: builtins "^1.0.3" @@ -10738,50 +10655,41 @@ validate-npm-package-name@^4.0.0: dependencies: builtins "^5.0.0" -validator@^13.7.0: - version "13.7.0" - resolved "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz" - integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== +validator@^13.9.0: + version "13.11.0" + resolved "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b" + integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ== -value-or-promise@1.0.11, value-or-promise@^1.0.11: +value-or-promise@1.0.11: version "1.0.11" - resolved "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz" + resolved "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140" integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg== +value-or-promise@^1.0.11, value-or-promise@^1.0.12: + version "1.0.12" + resolved "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c" + integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== + vary@^1, vary@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== verror@^1.10.0: version "1.10.1" - resolved "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz" + resolved "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb" integrity sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg== dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" extsprintf "^1.2.0" -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== - dependencies: - xml-name-validator "^3.0.0" - walk-up-path@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e" integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== -walker@^1.0.7: +walker@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== @@ -10790,29 +10698,20 @@ walker@^1.0.7: wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== dependencies: defaults "^1.0.3" -web-streams-polyfill@4.0.0-beta.1: - version "4.0.0-beta.1" - resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.1.tgz" - integrity sha512-3ux37gEX670UUphBF9AMCq8XM6iQ8Ac6A+DSRRjDoRBm1ufCkaCDdNVbaqq60PsEkdNlLKrGtv/YBP4EJXqNtQ== - -web-streams-polyfill@^3.2.0: - version "3.2.1" - resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz" - integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== - web3-utils@^1.3.4: - version "1.7.3" - resolved "https://registry.npmjs.org/web3-utils/-/web3-utils-1.7.3.tgz" - integrity sha512-g6nQgvb/bUpVUIxJE+ezVN+rYwYmlFyMvMIRSuqpi1dk6ApDD00YNArrk7sPcZnjvxOJ76813Xs2vIN2rgh4lg== + version "1.10.2" + resolved "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.2.tgz#361103d28a94d5e2a87ba15d776a62c33303eb44" + integrity sha512-TdApdzdse5YR+5GCX/b/vQnhhbj1KSAtfrDtRW7YS0kcWp1gkJsN62gw6GzCaNTeXookB7UrLtmDUuMv65qgow== dependencies: - bn.js "^4.11.9" + "@ethereumjs/util" "^8.1.0" + bn.js "^5.2.1" ethereum-bloom-filters "^1.0.6" - ethereumjs-util "^7.1.0" + ethereum-cryptography "^2.1.2" ethjs-unit "0.1.6" number-to-bn "1.7.0" randombytes "^2.1.0" @@ -10820,56 +10719,25 @@ web3-utils@^1.3.4: webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== whatwg-fetch@^3.4.1: - version "3.6.2" - resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz" - integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== - -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + version "3.6.19" + resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.19.tgz#caefd92ae630b91c07345537e67f8354db470973" + integrity sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw== whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^8.0.0, whatwg-url@^8.5.0: - version "8.7.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - which@^2.0.0, which@^2.0.1, which@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" @@ -10883,25 +10751,20 @@ wide-align@^1.1.2, wide-align@^1.1.5: wkx@^0.5.0: version "0.5.0" - resolved "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz" + resolved "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz#c6c37019acf40e517cc6b94657a25a3d4aa33e8c" integrity sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg== dependencies: "@types/node" "*" wonka@^4.0.14: version "4.0.15" - resolved "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz" + resolved "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz#9aa42046efa424565ab8f8f451fcca955bf80b89" integrity sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg== -word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - wordwrap@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== wordwrapjs@^4.0.0: version "4.0.1" @@ -10911,23 +10774,41 @@ wordwrapjs@^4.0.0: reduce-flatten "^2.0.0" typical "^5.2.0" -wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^6.0.1: + version "6.2.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^2.4.2: version "2.4.3" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== dependencies: graceful-fs "^4.1.11" @@ -10936,7 +10817,7 @@ write-file-atomic@^2.4.2: write-file-atomic@^3.0.0: version "3.0.3" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== dependencies: imurmurhash "^0.1.4" @@ -10944,7 +10825,7 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.0, write-file-atomic@^4.0.1: +write-file-atomic@^4.0.0, write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== @@ -10954,7 +10835,7 @@ write-file-atomic@^4.0.0, write-file-atomic@^4.0.1: write-json-file@^3.2.0: version "3.2.0" - resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz" + resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" integrity sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ== dependencies: detect-indent "^5.0.0" @@ -10966,7 +10847,7 @@ write-json-file@^3.2.0: write-json-file@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-4.3.0.tgz" + resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-4.3.0.tgz#908493d6fd23225344af324016e4ca8f702dd12d" integrity sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ== dependencies: detect-indent "^6.0.0" @@ -10978,7 +10859,7 @@ write-json-file@^4.3.0: write-pkg@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/write-pkg/-/write-pkg-4.0.0.tgz" + resolved "https://registry.npmjs.org/write-pkg/-/write-pkg-4.0.0.tgz#675cc04ef6c11faacbbc7771b24c0abbf2a20039" integrity sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA== dependencies: sort-keys "^2.0.0" @@ -10987,90 +10868,80 @@ write-pkg@^4.0.0: ws@7.4.6: version "7.4.6" - resolved "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz" + resolved "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -"ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.4.5: - version "7.5.7" - resolved "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz" - integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== - -ws@^7.4.6: - version "7.5.8" - resolved "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz#ac2729881ab9e7cbaf8787fe3469a48c5c7f636a" - integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw== - -ws@^8.3.0: - version "8.5.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== +ws@8.14.1: + version "8.14.1" + resolved "https://registry.npmjs.org/ws/-/ws-8.14.1.tgz#4b9586b4f70f9e6534c7bb1d3dc0baa8b8cf01e0" + integrity sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A== -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +ws@^7.4.5: + version "7.5.9" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +ws@^8.12.0, ws@^8.13.0: + version "8.14.2" + resolved "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@1.10.2, yaml@^1.10.0, yaml@^1.7.2: version "1.10.2" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yaml@^2.0.0-10: - version "2.0.0-10" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.0.0-10.tgz#d5b59e2d14b8683313a534f2bbc648e211a2753e" - integrity sha512-FHV8s5ODFFQXX/enJEU2EkanNl1UDBUz8oa4k5Qo/sR+Iq7VmhCDkRMb0/mjJCNeAWQ31W8WV6PYStDE4d9EIw== + version "2.3.2" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz#f522db4313c671a0ca963a75670f1c12ea909144" + integrity sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg== yargs-parser@20.2.4: version "20.2.4" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: - version "20.2.9" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-parser@21.1.1, yargs-parser@^21.1.1: +yargs-parser@21.1.1, yargs-parser@^21.0.0, yargs-parser@^21.0.1, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs-parser@^16.1.0: version "16.1.0" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.1.0.tgz" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.1.0.tgz#73747d53ae187e7b8dbe333f95714c76ea00ecf1" integrity sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^21.0.0: - version "21.0.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz" - integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== +yargs-parser@^20.2.2, yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== yargs@17.4.1: version "17.4.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz#ebe23284207bb75cee7c408c33e722bfb27b5284" integrity sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g== dependencies: cliui "^7.0.2" @@ -11083,7 +10954,7 @@ yargs@17.4.1: yargs@^16.2.0: version "16.2.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" + resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: cliui "^7.0.2" @@ -11094,10 +10965,10 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.6.2: - version "17.6.2" - resolved "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" - integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== +yargs@^17.3.1, yargs@^17.6.2, yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" escalade "^3.1.1" @@ -11114,25 +10985,35 @@ yn@3.1.1: yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== zen-observable-ts@^0.8.21: version "0.8.21" - resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz" + resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz#85d0031fbbde1eba3cd07d3ba90da241215f421d" integrity sha512-Yj3yXweRc8LdRMrCC8nIc4kkjWecPAUVh0TI0OUrWXx6aX790vLcDlWca6I4vsyCGH3LpWxq0dJRcMOFoVqmeg== dependencies: tslib "^1.9.3" zen-observable "^0.8.0" zen-observable-ts@^1.2.0: - version "1.2.3" - resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.3.tgz" - integrity sha512-hc/TGiPkAWpByykMwDcem3SdUgA4We+0Qb36bItSuJC9xD0XVBZoFHYoadAomDSNf64CG8Ydj0Qb8Od8BUWz5g== + version "1.2.5" + resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58" + integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg== dependencies: zen-observable "0.8.15" zen-observable@0.8.15, zen-observable@^0.8.0: version "0.8.15" - resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz" + resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== + +zod-validation-error@^1.3.0: + version "1.5.0" + resolved "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-1.5.0.tgz#2b355007a1c3b7fb04fa476bfad4e7b3fd5491e3" + integrity sha512-/7eFkAI4qV0tcxMBB/3+d2c1P6jzzZYdYSlBuAklzMuCrJu5bzJfHS0yVAS87dRHVlhftd6RFJDIvv03JgkSbw== + +zod@^3.21.4: + version "3.22.2" + resolved "https://registry.npmjs.org/zod/-/zod-3.22.2.tgz#3add8c682b7077c05ac6f979fea6998b573e157b" + integrity sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg== From b63399d63070a1804daee8a5a86cb1a863d5ea10 Mon Sep 17 00:00:00 2001 From: tilacog Date: Tue, 17 Oct 2023 11:04:03 -0300 Subject: [PATCH 02/16] agent, common: post merge fixes --- .../indexer-agent/src/__tests__/indexer.ts | 2 - packages/indexer-common/package.json | 1 + packages/indexer-common/src/errors.ts | 1 + .../src/indexer-management/allocations.ts | 8 - .../src/indexer-management/subgraphs.ts | 334 ------------------ packages/indexer-common/src/subgraphs.ts | 117 ++++++ yarn.lock | 5 + 7 files changed, 124 insertions(+), 344 deletions(-) delete mode 100644 packages/indexer-common/src/indexer-management/subgraphs.ts diff --git a/packages/indexer-agent/src/__tests__/indexer.ts b/packages/indexer-agent/src/__tests__/indexer.ts index 212b6a48c..fcb2ad7fe 100644 --- a/packages/indexer-agent/src/__tests__/indexer.ts +++ b/packages/indexer-agent/src/__tests__/indexer.ts @@ -135,7 +135,6 @@ const setup = async () => { }) sequelize = await connectDatabase(__DATABASE__) models = defineIndexerManagementModels(sequelize) - const ipfsEndpoint = 'https://ipfs.network.thegraph.com' // TODO: make this configurable and use within graft auto-resolver queryFeeModels = defineQueryFeeModels(sequelize) sequelize = await sequelize.sync({ force: true }) @@ -183,7 +182,6 @@ const setup = async () => { }, }) - const autoGraftResolverLimit = 1 // TODO: use a sensible value in tests. (Do we need graft auto-resolver in tests?) const network = await Network.create( logger, networkSpecification, diff --git a/packages/indexer-common/package.json b/packages/indexer-common/package.json index ce2d9ea5c..b025e64d0 100644 --- a/packages/indexer-common/package.json +++ b/packages/indexer-common/package.json @@ -54,6 +54,7 @@ "parsimmon": "^1.18.1", "sequelize": "6.33.0", "ts-custom-error": "^3.2.0", + "yaml": "^2.3.3", "zod": "^3.21.4" }, "devDependencies": { diff --git a/packages/indexer-common/src/errors.ts b/packages/indexer-common/src/errors.ts index 2b1575190..4a39d6ec9 100644 --- a/packages/indexer-common/src/errors.ts +++ b/packages/indexer-common/src/errors.ts @@ -85,6 +85,7 @@ export enum IndexerErrorCode { IE072 = 'IE072', IE073 = 'IE073', IE074 = 'IE074', + IE075 = 'IE075', } export const INDEXER_ERROR_MESSAGES: Record = { diff --git a/packages/indexer-common/src/indexer-management/allocations.ts b/packages/indexer-common/src/indexer-management/allocations.ts index 7b729b004..2d0872796 100644 --- a/packages/indexer-common/src/indexer-management/allocations.ts +++ b/packages/indexer-common/src/indexer-management/allocations.ts @@ -325,14 +325,6 @@ export class AllocationManager { const currentEpoch = await this.network.contracts.epochManager.currentEpoch() - // Ensure graft dependency is resolved - await this.subgraphManager.resolveGrafting( - logger, - this.models, - deployment, - indexNode, - 0, - ) // Ensure subgraph is deployed before allocating await this.graphNode.ensure( `indexer-agent/${deployment.ipfsHash.slice(-10)}`, diff --git a/packages/indexer-common/src/indexer-management/subgraphs.ts b/packages/indexer-common/src/indexer-management/subgraphs.ts deleted file mode 100644 index 8e6625248..000000000 --- a/packages/indexer-common/src/indexer-management/subgraphs.ts +++ /dev/null @@ -1,334 +0,0 @@ -import { - indexerError, - IndexerErrorCode, - IndexerManagementModels, - IndexingDecisionBasis, - IndexingRuleAttributes, - SubgraphIdentifierType, - upsertIndexingRule, - fetchIndexingRules, - INDEXING_RULE_GLOBAL, - IndexingStatusResolver, -} from '@graphprotocol/indexer-common' -import { Logger, SubgraphDeploymentID } from '@graphprotocol/common-ts' -import jayson, { Client as RpcClient } from 'jayson/promise' -import pTimeout from 'p-timeout' -import fetch from 'isomorphic-fetch' -import yaml from 'yaml' - -export class SubgraphManager { - client: RpcClient - indexNodeIDs: string[] - statusResolver: IndexingStatusResolver - autoGraftResolverLimit: number - ipfsEndpoint?: string - - constructor( - endpoint: string, - indexNodeIDs: string[], - statusResolver: IndexingStatusResolver, - ipfsUrl?: string, - autoGraftResolverLimit?: number, - ) { - if (endpoint.startsWith('https')) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - this.client = jayson.Client.https(endpoint as any) - } else { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - this.client = jayson.Client.http(endpoint as any) - } - this.indexNodeIDs = indexNodeIDs - this.statusResolver = statusResolver - this.ipfsEndpoint = ipfsUrl + '/api/v0/cat?arg=' - this.autoGraftResolverLimit = autoGraftResolverLimit ?? 0 - } - - async createSubgraph(logger: Logger, name: string): Promise { - try { - logger.info(`Create subgraph name`, { name }) - const response = await this.client.request('subgraph_create', { name }) - if (response.error) { - throw response.error - } - logger.info(`Successfully created subgraph name`, { name }) - } catch (error) { - if (error.message.includes('already exists')) { - logger.debug(`Subgraph name already exists`, { name }) - } - throw error - } - } - - async deploy( - logger: Logger, - models: IndexerManagementModels, - name: string, - deployment: SubgraphDeploymentID, - indexNode: string | undefined, - ): Promise { - try { - let targetNode: string - if (indexNode) { - targetNode = indexNode - if (!this.indexNodeIDs.includes(targetNode)) { - logger.warn( - `Specified deployment target node not present in indexNodeIDs supplied at startup, proceeding with deploy to target node anyway.`, - { - targetNode: indexNode, - indexNodeIDs: this.indexNodeIDs, - }, - ) - } - } else { - targetNode = - this.indexNodeIDs[Math.floor(Math.random() * this.indexNodeIDs.length)] - } - logger.info(`Deploy subgraph`, { - name, - deployment: deployment.display, - targetNode, - }) - const requestPromise = this.client.request('subgraph_deploy', { - name, - ipfs_hash: deployment.ipfsHash, - node_id: targetNode, - }) - // Timeout deployment after 2 minutes - const response = await pTimeout(requestPromise, 120000) - - if (response.error) { - throw indexerError(IndexerErrorCode.IE026, response.error) - } - logger.info(`Successfully deployed subgraph`, { - name, - deployment: deployment.display, - endpoints: response.result, - }) - - // Will be useful for supporting deploySubgraph resolver - const indexingRules = (await fetchIndexingRules(models, false)) - .filter((rule) => rule.identifier != INDEXING_RULE_GLOBAL) - .map((rule) => new SubgraphDeploymentID(rule.identifier)) - if (!indexingRules.includes(deployment)) { - const offchainIndexingRule = { - identifier: deployment.ipfsHash, - identifierType: SubgraphIdentifierType.DEPLOYMENT, - decisionBasis: IndexingDecisionBasis.OFFCHAIN, - } as Partial - await upsertIndexingRule(logger, models, offchainIndexingRule) - } - } catch (error) { - const err = indexerError(IndexerErrorCode.IE026, error) - logger.error(`Failed to deploy subgraph deployment`, { - name, - deployment: deployment.display, - err, - }) - throw err - } - } - - async remove( - logger: Logger, - models: IndexerManagementModels, - deployment: SubgraphDeploymentID, - ): Promise { - try { - logger.info(`Remove subgraph deployment`, { - deployment: deployment.display, - }) - const response = await this.client.request('subgraph_reassign', { - node_id: 'removed', - ipfs_hash: deployment.ipfsHash, - }) - if (response.error) { - throw response.error - } - logger.info(`Successfully removed subgraph deployment`, { - deployment: deployment.display, - }) - - if ( - await models.IndexingRule.findOne({ - where: { identifier: deployment.ipfsHash }, - }) - ) { - logger.info( - `Remove indexing rules, so indexer-agent will not attempt to redeploy`, - ) - await models.IndexingRule.destroy({ - where: { - identifier: deployment.ipfsHash, - }, - }) - logger.info(`Sucessfully removed indexing rule for '${deployment.ipfsHash}'`) - } - } catch (error) { - const err = indexerError(IndexerErrorCode.IE027, error) - logger.error(`Failed to remove subgraph deployment`, { - deployment: deployment.display, - err, - }) - } - } - - async reassign( - logger: Logger, - deployment: SubgraphDeploymentID, - indexNode: string | undefined, - ): Promise { - let targetNode: string - if (indexNode) { - targetNode = indexNode - if (!this.indexNodeIDs.includes(targetNode)) { - logger.warn( - `Specified deployment target node not present in indexNodeIDs supplied at startup, proceeding with deploy to target node anyway.`, - { - targetNode: indexNode, - indexNodeIDs: this.indexNodeIDs, - }, - ) - } - } else { - targetNode = this.indexNodeIDs[Math.floor(Math.random() * this.indexNodeIDs.length)] - } - try { - logger.info(`Reassign subgraph deployment`, { - deployment: deployment.display, - targetNode, - }) - const response = await this.client.request('subgraph_reassign', { - node_id: targetNode, - ipfs_hash: deployment.ipfsHash, - }) - if (response.error) { - throw response.error - } - } catch (error) { - if (error.message.includes('unchanged')) { - logger.debug(`Subgraph deployment assignment unchanged`, { - deployment: deployment.display, - targetNode, - }) - throw error - } - const err = indexerError(IndexerErrorCode.IE028, error) - logger.error(`Failed to reassign subgraph deployment`, { - deployment: deployment.display, - err, - }) - throw err - } - } - - async ensure( - logger: Logger, - models: IndexerManagementModels, - name: string, - deployment: SubgraphDeploymentID, - indexNode: string | undefined, - ): Promise { - try { - await this.createSubgraph(logger, name) - await this.deploy(logger, models, name, deployment, indexNode) - await this.reassign(logger, deployment, indexNode) - } catch (error) { - const err = indexerError(IndexerErrorCode.IE020, error) - logger.error(`Failed to ensure subgraph deployment is indexing`, { - name, - deployment: deployment.display, - targetNode: indexNode, - err, - }) - throw error - } - } - - // Simple fetch for subgraph manifest - async subgraphManifest(targetDeployment: SubgraphDeploymentID) { - const ipfsFile = await fetch(this.ipfsEndpoint + targetDeployment.ipfsHash, { - method: 'POST', - redirect: 'follow', - }) - return yaml.parse(await ipfsFile.text()) - } - - // Recursive function for targetDeployment resolve grafting, add depth until reached to resolverDepth - async resolveGrafting( - logger: Logger, - models: IndexerManagementModels, - targetDeployment: SubgraphDeploymentID, - indexNode: string | undefined, - depth: number, - ): Promise { - const manifest = await this.subgraphManifest(targetDeployment) - const name = `indexer-agent/${targetDeployment.ipfsHash.slice(-10)}` - - // No grafting or at root of dependency - if (!manifest.features || !manifest.features.includes('grafting')) { - if (depth) { - await this.ensure(logger, models, name, targetDeployment, indexNode) - } - return - } - // Default limit set to 0, disable auto-resolve of grafting dependencies - if (depth >= this.autoGraftResolverLimit) { - throw indexerError( - IndexerErrorCode.IE074, - `Grafting depth reached limit for auto resolve`, - ) - } - - try { - const baseDeployment = new SubgraphDeploymentID(manifest.graft.base) - let baseName = name.replace(this.depthRegex, `/depth-${depth}`) - if (baseName === name) { - // add depth suffix if didn't have one from targetDeployment - baseName += `/depth-${depth}` - } - await this.resolveGrafting(logger, models, baseDeployment, indexNode, depth + 1) - - // If base deployment has synced upto the graft block, then ensure the target deployment - // Otherwise just log to come back later - const graftStatus = await this.statusResolver.indexingStatus([baseDeployment]) - // If base deployment synced to required block, try to sync the target and - // turn off syncing for the base deployment - if ( - graftStatus[0].chains[0].latestBlock && - graftStatus[0].chains[0].latestBlock.number >= manifest.graft.block - ) { - await this.ensure(logger, models, name, targetDeployment, indexNode) - // At this point, can safely set NEVER to graft base deployment - await this.stop_sync(logger, baseDeployment, models) - } else { - logger.debug( - `Graft base deployment has yet to reach the graft block, try again later`, - ) - } - } catch { - throw indexerError( - IndexerErrorCode.IE074, - `Base deployment hasn't synced to the graft block, try again later`, - ) - } - } - - /** - * Matches "/depth-" followed by one or more digits - */ - depthRegex = /\/depth-\d+/ - - async stop_sync( - logger: Logger, - deployment: SubgraphDeploymentID, - models: IndexerManagementModels, - ) { - const neverIndexingRule = { - identifier: deployment.ipfsHash, - identifierType: SubgraphIdentifierType.DEPLOYMENT, - decisionBasis: IndexingDecisionBasis.NEVER, - } as Partial - - await upsertIndexingRule(logger, models, neverIndexingRule) - } -} diff --git a/packages/indexer-common/src/subgraphs.ts b/packages/indexer-common/src/subgraphs.ts index f06e03bd0..e6e53f713 100644 --- a/packages/indexer-common/src/subgraphs.ts +++ b/packages/indexer-common/src/subgraphs.ts @@ -11,6 +11,11 @@ import { DocumentNode, print } from 'graphql' import gql from 'graphql-tag' import { QueryResult } from './network-subgraph' import { mergeSelectionSets, sleep } from './utils' +import { IndexerManagementModels } from './indexer-management/models' +import { upsertIndexingRule } from './indexer-management/rules' +import * as yaml from 'yaml' +import { indexerError, IndexerErrorCode } from './errors' +import { GraphNode } from './graph-node' export enum SubgraphIdentifierType { DEPLOYMENT = 'deployment', @@ -522,3 +527,115 @@ export class SubgraphFreshnessChecker { } } } + +// Recursive function for targetDeployment resolve grafting, add depth until reached to resolverDepth +export async function resolveGrafting( + logger: Logger, + models: IndexerManagementModels, + ipfsEndpoint: URL, + targetDeployment: SubgraphDeploymentID, + depth: number, + autoGraftResolverLimit: number, + graphNode: GraphNode, +): Promise { + const manifest = await fetchSubgraphManifest(ipfsEndpoint, targetDeployment) + const name = `indexer-agent/${targetDeployment.ipfsHash.slice(-10)}` + + // No grafting or at root of dependency + if (!manifest.features || !manifest.features.includes('grafting')) { + if (depth) { + await graphNode.ensure(name, targetDeployment) + } + return + } + // Default limit set to 0, disable auto-resolve of grafting dependencies + if (depth >= autoGraftResolverLimit) { + throw indexerError( + IndexerErrorCode.IE074, + `Grafting depth reached limit for auto resolve`, + ) + } + + try { + const baseDeployment = new SubgraphDeploymentID(manifest.graft.base) + let baseName = name.replace(depthRegex, `/depth-${depth}`) + if (baseName === name) { + // add depth suffix if didn't have one from targetDeployment + baseName += `/depth-${depth}` + } + await resolveGrafting( + logger, + models, + ipfsEndpoint, + baseDeployment, + depth + 1, + autoGraftResolverLimit, + graphNode, + ) + + // If base deployment has synced upto the graft block, then ensure the target deployment + // Otherwise just log to come back later + const graftStatus = await graphNode.indexingStatus([baseDeployment]) + // If base deployment synced to required block, try to sync the target and + // turn off syncing for the base deployment + if ( + graftStatus[0].chains[0].latestBlock && + graftStatus[0].chains[0].latestBlock.number >= manifest.graft.block + ) { + await graphNode.ensure(name, targetDeployment) + // At this point, can safely set NEVER to graft base deployment + await stopSync(logger, baseDeployment, models) + } else { + logger.debug( + `Graft base deployment has yet to reach the graft block, try again later`, + ) + } + } catch { + throw indexerError( + IndexerErrorCode.IE074, + `Base deployment hasn't synced to the graft block, try again later`, + ) + } +} + +/** + * Matches "/depth-" followed by one or more digits + */ +const depthRegex = /\/depth-\d+/ + +// TODO: This should be under a dedicated class like Agent or Operator +async function stopSync( + logger: Logger, + deployment: SubgraphDeploymentID, + models: IndexerManagementModels, +) { + const neverIndexingRule = { + identifier: deployment.ipfsHash, + identifierType: SubgraphIdentifierType.DEPLOYMENT, + decisionBasis: IndexingDecisionBasis.NEVER, + } as Partial + + await upsertIndexingRule(logger, models, neverIndexingRule) +} + +interface Graft { + base: string + block: number +} + +interface SubgraphManifest { + features: string[] + graft: Graft +} + +// Simple fetch for subgraph manifest +async function fetchSubgraphManifest( + ipfsEndpoint: URL, + targetDeployment: SubgraphDeploymentID, +): Promise { + const ipfsFile = await fetch(ipfsEndpoint + targetDeployment.ipfsHash, { + method: 'POST', + redirect: 'follow', + }) + return yaml.parse(await ipfsFile.text()) as SubgraphManifest // TODO: validate this value using Zod +} diff --git a/yarn.lock b/yarn.lock index 2e1e757a4..18a14fc06 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10916,6 +10916,11 @@ yaml@^2.0.0-10: resolved "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz#f522db4313c671a0ca963a75670f1c12ea909144" integrity sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg== +yaml@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.3.tgz#01f6d18ef036446340007db8e016810e5d64aad9" + integrity sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ== + yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" From a7dca07e7ded4cd862da72c6fceffdb409eef2c0 Mon Sep 17 00:00:00 2001 From: tilacog Date: Tue, 17 Oct 2023 11:16:59 -0300 Subject: [PATCH 03/16] agent: reintroduce auto-grafting options --- .../src/commands/common-options.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/indexer-agent/src/commands/common-options.ts b/packages/indexer-agent/src/commands/common-options.ts index 9d40e82b7..17f717330 100644 --- a/packages/indexer-agent/src/commands/common-options.ts +++ b/packages/indexer-agent/src/commands/common-options.ts @@ -137,6 +137,25 @@ export function injectCommonStartupOptions(argv: Argv): Argv { coerce: parseDeploymentManagementMode, group: 'Indexer Infrastructure', }) + .option('ipfs-endpoint', { + description: `Endpoint to an ipfs node to quickly query subgraph manifest data`, + type: 'string', + default: 'https://ipfs.network.thegraph.com', + group: 'Indexer Infrastructure', + coerce: arg => new URL(arg), + }) + .option('grafting-auto-resolve-depth', { + description: `Maximum depth of grafting dependency to automatically resolve`, + type: 'number', + default: 0, + group: 'Indexer Infrastructure', + coerce: (value: number) => { + if (value < 0) { + throw new Error('Number must be a non-negative value') + } + return value + }, + }) .config({ key: 'config-file', description: 'Indexer agent configuration file (YAML format)', From e7eb2ece7adad517f5b7e5180e7106bc5eee5aa4 Mon Sep 17 00:00:00 2001 From: tilacog Date: Tue, 17 Oct 2023 12:28:06 -0300 Subject: [PATCH 04/16] common: improve error handling on fetchSubgraphManifest function --- .../src/network-specification.ts | 2 +- packages/indexer-common/src/subgraphs.ts | 78 +++++++++++++++---- packages/indexer-common/src/types.ts | 12 +++ 3 files changed, 75 insertions(+), 17 deletions(-) diff --git a/packages/indexer-common/src/network-specification.ts b/packages/indexer-common/src/network-specification.ts index 620c7a39a..b5e437e1b 100644 --- a/packages/indexer-common/src/network-specification.ts +++ b/packages/indexer-common/src/network-specification.ts @@ -161,7 +161,7 @@ function transformNetworkIdentifier(input: string, ctx: z.RefinementCtx): string } } -function transformIpfsHash(input: string, ctx: z.RefinementCtx): string { +export function transformIpfsHash(input: string, ctx: z.RefinementCtx): string { try { return validateIpfsHash(input) } catch (e) { diff --git a/packages/indexer-common/src/subgraphs.ts b/packages/indexer-common/src/subgraphs.ts index e6e53f713..6cecc9543 100644 --- a/packages/indexer-common/src/subgraphs.ts +++ b/packages/indexer-common/src/subgraphs.ts @@ -1,7 +1,7 @@ import { base58 } from 'ethers/lib/utils' import { BigNumber, utils } from 'ethers' import { Logger, SubgraphDeploymentID } from '@graphprotocol/common-ts' -import { SubgraphDeployment } from './types' +import { SubgraphDeployment, SubgraphManifestSchema } from './types' import { INDEXING_RULE_GLOBAL, IndexingDecisionBasis, @@ -16,6 +16,7 @@ import { upsertIndexingRule } from './indexer-management/rules' import * as yaml from 'yaml' import { indexerError, IndexerErrorCode } from './errors' import { GraphNode } from './graph-node' +import { z } from 'zod' export enum SubgraphIdentifierType { DEPLOYMENT = 'deployment', @@ -538,7 +539,7 @@ export async function resolveGrafting( autoGraftResolverLimit: number, graphNode: GraphNode, ): Promise { - const manifest = await fetchSubgraphManifest(ipfsEndpoint, targetDeployment) + const manifest = await fetchSubgraphManifest(ipfsEndpoint, targetDeployment, logger) const name = `indexer-agent/${targetDeployment.ipfsHash.slice(-10)}` // No grafting or at root of dependency @@ -556,6 +557,11 @@ export async function resolveGrafting( ) } + // Ensure manifest.graft field exists + if (!manifest.graft) { + throw new Error(`Expected subgraph manifest to contain a 'graft' field`) + } + try { const baseDeployment = new SubgraphDeploymentID(manifest.graft.base) let baseName = name.replace(depthRegex, `/depth-${depth}`) @@ -618,24 +624,64 @@ async function stopSync( await upsertIndexingRule(logger, models, neverIndexingRule) } -interface Graft { - base: string - block: number -} - -interface SubgraphManifest { - features: string[] - graft: Graft -} - +type SubgraphManifest = z.infer // Simple fetch for subgraph manifest async function fetchSubgraphManifest( ipfsEndpoint: URL, targetDeployment: SubgraphDeploymentID, + parentLogger: Logger, ): Promise { - const ipfsFile = await fetch(ipfsEndpoint + targetDeployment.ipfsHash, { - method: 'POST', - redirect: 'follow', + // Build IPFS search URL + const subgraphManifestSearchURL = new URL('/api/v0/cat', ipfsEndpoint) + subgraphManifestSearchURL.searchParams.set('arg', targetDeployment.ipfsHash) + + const logger = parentLogger.child({ + function: 'fetchSubgraphManifest', + subgraphDeployment: SubgraphDeploymentID, + subgraphManifestSearchURL, }) - return yaml.parse(await ipfsFile.text()) as SubgraphManifest // TODO: validate this value using Zod + + // Fetch file on IPFS + let ipfsFile + try { + logger.trace('Fetching subgraph manifest file on IPFS') + ipfsFile = await fetch(subgraphManifestSearchURL, { + method: 'POST', + redirect: 'follow', + }) + } catch (error) { + const message = 'Failed to fetch subgraph manifest file on IPFS' + logger.error(message, { error }) + throw indexerError(IndexerErrorCode.IE074, { message, error }) + } + + // Resolve file's text content + let text + try { + text = await ipfsFile!.text() + } catch (error) { + const message = `Failed to resolve subgraph manifest file text content` + logger.error(message, { error }) + throw indexerError(IndexerErrorCode.IE074, { message, error }) + } + + // Parse text as a YAML file + let yamlContent + try { + yamlContent = yaml.parse(text!) + } catch (error) { + const message = `Failed to read file contents as valid YAML` + logger.error(message, { error }) + throw indexerError(IndexerErrorCode.IE074, { message, error }) + } + + // Validate YAML contents + const validationResult = SubgraphManifestSchema.safeParse(yamlContent) + if (validationResult.success) { + return validationResult.data + } else { + const message = `Failed to validate yaml contents as a subgraph manifest` + logger.error(message) + throw indexerError(IndexerErrorCode.IE074, message) + } } diff --git a/packages/indexer-common/src/types.ts b/packages/indexer-common/src/types.ts index cfd62747a..f3e97fc9e 100644 --- a/packages/indexer-common/src/types.ts +++ b/packages/indexer-common/src/types.ts @@ -1,5 +1,7 @@ import { SubgraphDeploymentID } from '@graphprotocol/common-ts' import { BigNumber, providers } from 'ethers' +import { z } from 'zod' +import { transformIpfsHash } from './network-specification' export enum AllocationManagementMode { AUTO = 'auto', @@ -106,3 +108,13 @@ export function parseDeploymentManagementMode(input: string): DeploymentManageme throw new Error(`Invalid value for deployment management mode: ${input}`) } } + +const Graft = z.object({ + base: z.string().transform(transformIpfsHash), + block: z.number().positive(), +}) + +export const SubgraphManifestSchema = z.object({ + features: z.array(z.string()).optional(), + graft: Graft.optional(), +}) From 0d8de2468582bc465e55f54857e5b0e88f981c6b Mon Sep 17 00:00:00 2001 From: tilacog Date: Mon, 23 Oct 2023 19:51:32 -0300 Subject: [PATCH 05/16] common: refactor resolveGrafting function to return a list of grafts --- .../src/__tests__/grafting.test.ts | 76 ++++++++++ .../src/__tests__/subgraph.test.ts | 1 + packages/indexer-common/src/subgraphs.ts | 143 ++++++------------ 3 files changed, 126 insertions(+), 94 deletions(-) create mode 100644 packages/indexer-common/src/__tests__/grafting.test.ts diff --git a/packages/indexer-common/src/__tests__/grafting.test.ts b/packages/indexer-common/src/__tests__/grafting.test.ts new file mode 100644 index 000000000..052f5c048 --- /dev/null +++ b/packages/indexer-common/src/__tests__/grafting.test.ts @@ -0,0 +1,76 @@ +import { resolveGrafting, GraftableSubgraph } from '../subgraphs' +import { SubgraphDeploymentID } from '@graphprotocol/common-ts' + +// Create a mock for the fetchSubgraphManifest function +const fakeSubgraphManifestResolver = jest.fn() + +// Fake IPFS Hashes: +const target = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrZX' +const base1 = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrYj' +const base2 = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrYk' +const base3 = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrYn' + +describe('resolveGrafting', () => { + beforeEach(() => { + jest.clearAllMocks() + const mockManifests = [ + { + features: ['grafting'], + graft: { block: 30, base: base1 }, + }, + { + features: ['grafting'], + graft: { block: 20, base: base2 }, + }, + { + features: ['grafting'], + graft: { block: 10, base: base3 }, + }, + { features: [], graft: null }, + ] + fakeSubgraphManifestResolver + .mockImplementationOnce(() => mockManifests[0]) + .mockImplementationOnce(() => mockManifests[1]) + .mockImplementationOnce(() => mockManifests[2]) + .mockImplementationOnce(() => mockManifests[3]) + }) + + test('should resolve grafting with multiple iterations', async () => { + const targetDeployment = new SubgraphDeploymentID(target) + + const result: GraftableSubgraph[] = await resolveGrafting( + fakeSubgraphManifestResolver, + targetDeployment, + ) + + expect(result).toEqual([ + { + deployment: targetDeployment, + graft: { block: 30, base: new SubgraphDeploymentID(base1) }, + }, + { + deployment: new SubgraphDeploymentID(base1), + graft: { block: 20, base: new SubgraphDeploymentID(base2) }, + }, + { + deployment: new SubgraphDeploymentID(base2), + graft: { block: 10, base: new SubgraphDeploymentID(base3) }, + }, + { deployment: new SubgraphDeploymentID(base3), graft: null }, + ]) + expect(fakeSubgraphManifestResolver).toHaveBeenCalledTimes(4) + }) + + test('should resolve grafting when max iterations are reached', async () => { + const targetDeployment = new SubgraphDeploymentID(target) + expect(() => + resolveGrafting( + fakeSubgraphManifestResolver, + targetDeployment, + 2, // Set maxIterations to 2 + ), + ).rejects.toThrow( + `Failed to find a graft root for target subgraph deployment (${target}) after 2 iterations.`, + ) + }) +}) diff --git a/packages/indexer-common/src/__tests__/subgraph.test.ts b/packages/indexer-common/src/__tests__/subgraph.test.ts index fb85fcb19..23653713f 100644 --- a/packages/indexer-common/src/__tests__/subgraph.test.ts +++ b/packages/indexer-common/src/__tests__/subgraph.test.ts @@ -15,6 +15,7 @@ export const mockProvider: ProviderInterface & any = { } export const mockLogger: LoggerInterface & any = { + child: jest.fn(), trace: jest.fn(), error: jest.fn(), warn: jest.fn(), diff --git a/packages/indexer-common/src/subgraphs.ts b/packages/indexer-common/src/subgraphs.ts index 6cecc9543..238ff43de 100644 --- a/packages/indexer-common/src/subgraphs.ts +++ b/packages/indexer-common/src/subgraphs.ts @@ -11,11 +11,8 @@ import { DocumentNode, print } from 'graphql' import gql from 'graphql-tag' import { QueryResult } from './network-subgraph' import { mergeSelectionSets, sleep } from './utils' -import { IndexerManagementModels } from './indexer-management/models' -import { upsertIndexingRule } from './indexer-management/rules' import * as yaml from 'yaml' import { indexerError, IndexerErrorCode } from './errors' -import { GraphNode } from './graph-node' import { z } from 'zod' export enum SubgraphIdentifierType { @@ -343,6 +340,7 @@ export interface LoggerInterface { trace(msg: string, o?: object, ...args: any[]): void error(msg: string, o?: object, ...args: any[]): void warn(msg: string, o?: object, ...args: any[]): void + child(bindings: Record): Logger } export interface SubgraphQueryInterface { @@ -529,107 +527,64 @@ export class SubgraphFreshnessChecker { } } -// Recursive function for targetDeployment resolve grafting, add depth until reached to resolverDepth -export async function resolveGrafting( - logger: Logger, - models: IndexerManagementModels, - ipfsEndpoint: URL, - targetDeployment: SubgraphDeploymentID, - depth: number, - autoGraftResolverLimit: number, - graphNode: GraphNode, -): Promise { - const manifest = await fetchSubgraphManifest(ipfsEndpoint, targetDeployment, logger) - const name = `indexer-agent/${targetDeployment.ipfsHash.slice(-10)}` - - // No grafting or at root of dependency - if (!manifest.features || !manifest.features.includes('grafting')) { - if (depth) { - await graphNode.ensure(name, targetDeployment) - } - return - } - // Default limit set to 0, disable auto-resolve of grafting dependencies - if (depth >= autoGraftResolverLimit) { - throw indexerError( - IndexerErrorCode.IE074, - `Grafting depth reached limit for auto resolve`, - ) - } +export interface GraftBase { + block: number + base: SubgraphDeploymentID +} - // Ensure manifest.graft field exists - if (!manifest.graft) { - throw new Error(`Expected subgraph manifest to contain a 'graft' field`) - } +export interface GraftableSubgraph { + deployment: SubgraphDeploymentID + graft: GraftBase | null // Root subgraph does not have a graft base +} - try { - const baseDeployment = new SubgraphDeploymentID(manifest.graft.base) - let baseName = name.replace(depthRegex, `/depth-${depth}`) - if (baseName === name) { - // add depth suffix if didn't have one from targetDeployment - baseName += `/depth-${depth}` - } - await resolveGrafting( - logger, - models, - ipfsEndpoint, - baseDeployment, - depth + 1, - autoGraftResolverLimit, - graphNode, - ) +type SubgraphManifestResolver = ( + subgraphID: SubgraphDeploymentID, +) => Promise - // If base deployment has synced upto the graft block, then ensure the target deployment - // Otherwise just log to come back later - const graftStatus = await graphNode.indexingStatus([baseDeployment]) - // If base deployment synced to required block, try to sync the target and - // turn off syncing for the base deployment - if ( - graftStatus[0].chains[0].latestBlock && - graftStatus[0].chains[0].latestBlock.number >= manifest.graft.block - ) { - await graphNode.ensure(name, targetDeployment) - // At this point, can safely set NEVER to graft base deployment - await stopSync(logger, baseDeployment, models) +// Resolves all graft dependencies for a given subgraph +export async function resolveGrafting( + subgraphManifestResolver: SubgraphManifestResolver, + targetDeployment: SubgraphDeploymentID, + maxIterations: number = 100, +): Promise { + const graftBases: GraftableSubgraph[] = [] + + let iterationCount = 0 + let deployment = targetDeployment + while (iterationCount < maxIterations) { + const manifest = await subgraphManifestResolver(deployment) + let graft: GraftBase | null = null + if (manifest.features?.includes('grafting') && manifest.graft) { + // Found a graft base + const base = new SubgraphDeploymentID(manifest.graft.base) + graft = { block: manifest.graft.block, base } + graftBases.push({ deployment, graft }) + deployment = base } else { - logger.debug( - `Graft base deployment has yet to reach the graft block, try again later`, - ) + // Reached root subgraph, stop iterating + iterationCount = maxIterations + graftBases.push({ deployment, graft }) } - } catch { - throw indexerError( - IndexerErrorCode.IE074, - `Base deployment hasn't synced to the graft block, try again later`, - ) + iterationCount++ } -} -/** - * Matches "/depth-" followed by one or more digits - */ -const depthRegex = /\/depth-\d+/ + // Check if we have found the graft root + if (graftBases.length > 0 && graftBases[graftBases.length - 1].graft !== null) { + throw new Error( + `Failed to find a graft root for target subgraph deployment (${targetDeployment.ipfsHash}) after ${iterationCount} iterations.`, + ) + } -// TODO: This should be under a dedicated class like Agent or Operator -async function stopSync( - logger: Logger, - deployment: SubgraphDeploymentID, - models: IndexerManagementModels, -) { - const neverIndexingRule = { - identifier: deployment.ipfsHash, - identifierType: SubgraphIdentifierType.DEPLOYMENT, - decisionBasis: IndexingDecisionBasis.NEVER, - } as Partial - - await upsertIndexingRule(logger, models, neverIndexingRule) + return graftBases } type SubgraphManifest = z.infer + // Simple fetch for subgraph manifest -async function fetchSubgraphManifest( +export async function fetchSubgraphManifest( ipfsEndpoint: URL, targetDeployment: SubgraphDeploymentID, - parentLogger: Logger, + parentLogger: LoggerInterface, ): Promise { // Build IPFS search URL const subgraphManifestSearchURL = new URL('/api/v0/cat', ipfsEndpoint) @@ -652,7 +607,7 @@ async function fetchSubgraphManifest( } catch (error) { const message = 'Failed to fetch subgraph manifest file on IPFS' logger.error(message, { error }) - throw indexerError(IndexerErrorCode.IE074, { message, error }) + throw indexerError(IndexerErrorCode.IE075, { message, error }) } // Resolve file's text content @@ -662,7 +617,7 @@ async function fetchSubgraphManifest( } catch (error) { const message = `Failed to resolve subgraph manifest file text content` logger.error(message, { error }) - throw indexerError(IndexerErrorCode.IE074, { message, error }) + throw indexerError(IndexerErrorCode.IE075, { message, error }) } // Parse text as a YAML file @@ -672,7 +627,7 @@ async function fetchSubgraphManifest( } catch (error) { const message = `Failed to read file contents as valid YAML` logger.error(message, { error }) - throw indexerError(IndexerErrorCode.IE074, { message, error }) + throw indexerError(IndexerErrorCode.IE075, { message, error }) } // Validate YAML contents @@ -682,6 +637,6 @@ async function fetchSubgraphManifest( } else { const message = `Failed to validate yaml contents as a subgraph manifest` logger.error(message) - throw indexerError(IndexerErrorCode.IE074, message) + throw indexerError(IndexerErrorCode.IE075, message) } } From 1e6582cb5dec16427b130b664243281fd62e61f1 Mon Sep 17 00:00:00 2001 From: tilacog Date: Tue, 24 Oct 2023 16:44:01 -0300 Subject: [PATCH 06/16] common: implement getIndexingStatusOfGraftableSubgraph function --- packages/indexer-common/src/graph-node.ts | 17 +++++++--- packages/indexer-common/src/subgraphs.ts | 41 ++++++++++++++++++++++- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/packages/indexer-common/src/graph-node.ts b/packages/indexer-common/src/graph-node.ts index 5157ea4a2..e8b895b59 100644 --- a/packages/indexer-common/src/graph-node.ts +++ b/packages/indexer-common/src/graph-node.ts @@ -60,7 +60,11 @@ export const parseGraphQLBlockPointer = (block: any): BlockPointer | null => } : null -export class GraphNode { +export interface GraphNodeInterface { + indexingStatus(deployments: SubgraphDeploymentID[]): Promise +} + +export class GraphNode implements GraphNodeInterface { admin: RpcClient private queryBaseURL: URL status: Client @@ -442,9 +446,14 @@ export class GraphNode { }` const queryIndexingStatuses = async () => { - const result = await this.status - .query(query, { deployments: deployments.map((id) => id.ipfsHash) }) - .toPromise() + let result + try { + result = await this.status + .query(query, { deployments: deployments.map((id) => id.ipfsHash) }) + .toPromise() + } catch (error) { + throw indexerError(IndexerErrorCode.IE018, error) + } return ( result.data.indexingStatuses diff --git a/packages/indexer-common/src/subgraphs.ts b/packages/indexer-common/src/subgraphs.ts index 238ff43de..1998ad209 100644 --- a/packages/indexer-common/src/subgraphs.ts +++ b/packages/indexer-common/src/subgraphs.ts @@ -1,7 +1,7 @@ import { base58 } from 'ethers/lib/utils' import { BigNumber, utils } from 'ethers' import { Logger, SubgraphDeploymentID } from '@graphprotocol/common-ts' -import { SubgraphDeployment, SubgraphManifestSchema } from './types' +import { SubgraphDeployment, SubgraphManifestSchema, BlockPointer } from './types' import { INDEXING_RULE_GLOBAL, IndexingDecisionBasis, @@ -13,6 +13,7 @@ import { QueryResult } from './network-subgraph' import { mergeSelectionSets, sleep } from './utils' import * as yaml from 'yaml' import { indexerError, IndexerErrorCode } from './errors' +import { GraphNodeInterface } from './graph-node' import { z } from 'zod' export enum SubgraphIdentifierType { @@ -640,3 +641,41 @@ export async function fetchSubgraphManifest( throw indexerError(IndexerErrorCode.IE075, message) } } + +interface IndexingStatus { + latestBlock: BlockPointer | null + health: string + synced: boolean +} + +interface SubgraphGraftStatus extends GraftableSubgraph { + indexingStatus: IndexingStatus | null +} + +export async function getIndexingStatusOfGraftableSubgraph( + subgraph: GraftableSubgraph, + graphNode: GraphNodeInterface, +): Promise { + let response + try { + response = await graphNode.indexingStatus([subgraph.deployment]) + } catch (error) { + const message = `Failed to fetch indexing status when resolving subgraph grafts` + // TODO: log this error + throw indexerError(IndexerErrorCode.IE075, { message, error }) + } + let indexingStatus: IndexingStatus | null = null + if (response && response.length) { + const subgraphIndexingStatus = response[0] + let latestBlock: BlockPointer | null = null + if (subgraphIndexingStatus.chains && subgraphIndexingStatus.chains.length) { + latestBlock = subgraphIndexingStatus.chains[0].latestBlock + } + indexingStatus = { + health: subgraphIndexingStatus.health, + synced: subgraphIndexingStatus.synced, + latestBlock, + } + } + return { ...subgraph, indexingStatus } +} From a6ce65c96e98e5f81b1b0793cd89f5053d3678d3 Mon Sep 17 00:00:00 2001 From: tilacog Date: Tue, 24 Oct 2023 17:04:37 -0300 Subject: [PATCH 07/16] common: move grafting items to a dedicated module --- .../src/__tests__/grafting.test.ts | 2 +- packages/indexer-common/src/grafting.ts | 90 ++++++++++++++++++ packages/indexer-common/src/subgraphs.ts | 95 +------------------ packages/indexer-common/src/types.ts | 2 + 4 files changed, 94 insertions(+), 95 deletions(-) create mode 100644 packages/indexer-common/src/grafting.ts diff --git a/packages/indexer-common/src/__tests__/grafting.test.ts b/packages/indexer-common/src/__tests__/grafting.test.ts index 052f5c048..a5c28669f 100644 --- a/packages/indexer-common/src/__tests__/grafting.test.ts +++ b/packages/indexer-common/src/__tests__/grafting.test.ts @@ -1,4 +1,4 @@ -import { resolveGrafting, GraftableSubgraph } from '../subgraphs' +import { resolveGrafting, GraftableSubgraph } from '../grafting' import { SubgraphDeploymentID } from '@graphprotocol/common-ts' // Create a mock for the fetchSubgraphManifest function diff --git a/packages/indexer-common/src/grafting.ts b/packages/indexer-common/src/grafting.ts new file mode 100644 index 000000000..ecdc0a948 --- /dev/null +++ b/packages/indexer-common/src/grafting.ts @@ -0,0 +1,90 @@ +import { SubgraphDeploymentID } from '@graphprotocol/common-ts' +import { GraphNodeInterface } from './graph-node' +import { BlockPointer, SubgraphManifest } from './types' +import { indexerError, IndexerErrorCode } from './errors' + +export interface GraftBase { + block: number + base: SubgraphDeploymentID +} + +export interface GraftableSubgraph { + deployment: SubgraphDeploymentID + graft: GraftBase | null // Root subgraph does not have a graft base +} + +type SubgraphManifestResolver = ( + subgraphID: SubgraphDeploymentID, +) => Promise + +interface IndexingStatus { + latestBlock: BlockPointer | null + health: string + synced: boolean +} + +interface SubgraphGraftStatus extends GraftableSubgraph { + indexingStatus: IndexingStatus | null +} + +// Resolves all graft dependencies for a given subgraph +export async function resolveGrafting( + subgraphManifestResolver: SubgraphManifestResolver, + targetDeployment: SubgraphDeploymentID, + maxIterations: number = 100, +): Promise { + const graftBases: GraftableSubgraph[] = [] + let iterationCount = 0 + let deployment = targetDeployment + while (iterationCount < maxIterations) { + const manifest = await subgraphManifestResolver(deployment) + let graft: GraftBase | null = null + if (manifest.features?.includes('grafting') && manifest.graft) { + // Found a graft base + const base = new SubgraphDeploymentID(manifest.graft.base) + graft = { block: manifest.graft.block, base } + graftBases.push({ deployment, graft }) + deployment = base + } else { + // Reached root subgraph, stop iterating + iterationCount = maxIterations + graftBases.push({ deployment, graft }) + } + iterationCount++ + } + // Check if we have found the graft root + if (graftBases.length > 0 && graftBases[graftBases.length - 1].graft !== null) { + throw new Error( + `Failed to find a graft root for target subgraph deployment (${targetDeployment.ipfsHash}) after ${iterationCount} iterations.`, + ) + } + return graftBases +} + +export async function getIndexingStatusOfGraftableSubgraph( + subgraph: GraftableSubgraph, + graphNode: GraphNodeInterface, +): Promise { + let response + try { + response = await graphNode.indexingStatus([subgraph.deployment]) + } catch (error) { + const message = `Failed to fetch indexing status when resolving subgraph grafts` + // TODO: log this error + throw indexerError(IndexerErrorCode.IE075, { message, error }) + } + let indexingStatus: IndexingStatus | null = null + if (response && response.length) { + const subgraphIndexingStatus = response[0] + let latestBlock: BlockPointer | null = null + if (subgraphIndexingStatus.chains && subgraphIndexingStatus.chains.length) { + latestBlock = subgraphIndexingStatus.chains[0].latestBlock + } + indexingStatus = { + health: subgraphIndexingStatus.health, + synced: subgraphIndexingStatus.synced, + latestBlock, + } + } + return { ...subgraph, indexingStatus } +} diff --git a/packages/indexer-common/src/subgraphs.ts b/packages/indexer-common/src/subgraphs.ts index 1998ad209..df842ee18 100644 --- a/packages/indexer-common/src/subgraphs.ts +++ b/packages/indexer-common/src/subgraphs.ts @@ -1,7 +1,7 @@ import { base58 } from 'ethers/lib/utils' import { BigNumber, utils } from 'ethers' import { Logger, SubgraphDeploymentID } from '@graphprotocol/common-ts' -import { SubgraphDeployment, SubgraphManifestSchema, BlockPointer } from './types' +import { SubgraphDeployment, SubgraphManifest, SubgraphManifestSchema } from './types' import { INDEXING_RULE_GLOBAL, IndexingDecisionBasis, @@ -13,8 +13,6 @@ import { QueryResult } from './network-subgraph' import { mergeSelectionSets, sleep } from './utils' import * as yaml from 'yaml' import { indexerError, IndexerErrorCode } from './errors' -import { GraphNodeInterface } from './graph-node' -import { z } from 'zod' export enum SubgraphIdentifierType { DEPLOYMENT = 'deployment', @@ -528,59 +526,6 @@ export class SubgraphFreshnessChecker { } } -export interface GraftBase { - block: number - base: SubgraphDeploymentID -} - -export interface GraftableSubgraph { - deployment: SubgraphDeploymentID - graft: GraftBase | null // Root subgraph does not have a graft base -} - -type SubgraphManifestResolver = ( - subgraphID: SubgraphDeploymentID, -) => Promise - -// Resolves all graft dependencies for a given subgraph -export async function resolveGrafting( - subgraphManifestResolver: SubgraphManifestResolver, - targetDeployment: SubgraphDeploymentID, - maxIterations: number = 100, -): Promise { - const graftBases: GraftableSubgraph[] = [] - - let iterationCount = 0 - let deployment = targetDeployment - while (iterationCount < maxIterations) { - const manifest = await subgraphManifestResolver(deployment) - let graft: GraftBase | null = null - if (manifest.features?.includes('grafting') && manifest.graft) { - // Found a graft base - const base = new SubgraphDeploymentID(manifest.graft.base) - graft = { block: manifest.graft.block, base } - graftBases.push({ deployment, graft }) - deployment = base - } else { - // Reached root subgraph, stop iterating - iterationCount = maxIterations - graftBases.push({ deployment, graft }) - } - iterationCount++ - } - - // Check if we have found the graft root - if (graftBases.length > 0 && graftBases[graftBases.length - 1].graft !== null) { - throw new Error( - `Failed to find a graft root for target subgraph deployment (${targetDeployment.ipfsHash}) after ${iterationCount} iterations.`, - ) - } - - return graftBases -} - -type SubgraphManifest = z.infer - // Simple fetch for subgraph manifest export async function fetchSubgraphManifest( ipfsEndpoint: URL, @@ -641,41 +586,3 @@ export async function fetchSubgraphManifest( throw indexerError(IndexerErrorCode.IE075, message) } } - -interface IndexingStatus { - latestBlock: BlockPointer | null - health: string - synced: boolean -} - -interface SubgraphGraftStatus extends GraftableSubgraph { - indexingStatus: IndexingStatus | null -} - -export async function getIndexingStatusOfGraftableSubgraph( - subgraph: GraftableSubgraph, - graphNode: GraphNodeInterface, -): Promise { - let response - try { - response = await graphNode.indexingStatus([subgraph.deployment]) - } catch (error) { - const message = `Failed to fetch indexing status when resolving subgraph grafts` - // TODO: log this error - throw indexerError(IndexerErrorCode.IE075, { message, error }) - } - let indexingStatus: IndexingStatus | null = null - if (response && response.length) { - const subgraphIndexingStatus = response[0] - let latestBlock: BlockPointer | null = null - if (subgraphIndexingStatus.chains && subgraphIndexingStatus.chains.length) { - latestBlock = subgraphIndexingStatus.chains[0].latestBlock - } - indexingStatus = { - health: subgraphIndexingStatus.health, - synced: subgraphIndexingStatus.synced, - latestBlock, - } - } - return { ...subgraph, indexingStatus } -} diff --git a/packages/indexer-common/src/types.ts b/packages/indexer-common/src/types.ts index f3e97fc9e..c0885a60d 100644 --- a/packages/indexer-common/src/types.ts +++ b/packages/indexer-common/src/types.ts @@ -118,3 +118,5 @@ export const SubgraphManifestSchema = z.object({ features: z.array(z.string()).optional(), graft: Graft.optional(), }) + +export type SubgraphManifest = z.infer From 1867f41a7c1e56da26764371a31198c383a3ab31 Mon Sep 17 00:00:00 2001 From: tilacog Date: Tue, 24 Oct 2023 17:08:05 -0300 Subject: [PATCH 08/16] common: rename resolveGrafting function to discoverGraftBases --- packages/indexer-common/src/__tests__/grafting.test.ts | 6 +++--- packages/indexer-common/src/grafting.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/indexer-common/src/__tests__/grafting.test.ts b/packages/indexer-common/src/__tests__/grafting.test.ts index a5c28669f..e41f67be3 100644 --- a/packages/indexer-common/src/__tests__/grafting.test.ts +++ b/packages/indexer-common/src/__tests__/grafting.test.ts @@ -1,4 +1,4 @@ -import { resolveGrafting, GraftableSubgraph } from '../grafting' +import { discoverGraftBases, GraftableSubgraph } from '../grafting' import { SubgraphDeploymentID } from '@graphprotocol/common-ts' // Create a mock for the fetchSubgraphManifest function @@ -38,7 +38,7 @@ describe('resolveGrafting', () => { test('should resolve grafting with multiple iterations', async () => { const targetDeployment = new SubgraphDeploymentID(target) - const result: GraftableSubgraph[] = await resolveGrafting( + const result: GraftableSubgraph[] = await discoverGraftBases( fakeSubgraphManifestResolver, targetDeployment, ) @@ -64,7 +64,7 @@ describe('resolveGrafting', () => { test('should resolve grafting when max iterations are reached', async () => { const targetDeployment = new SubgraphDeploymentID(target) expect(() => - resolveGrafting( + discoverGraftBases( fakeSubgraphManifestResolver, targetDeployment, 2, // Set maxIterations to 2 diff --git a/packages/indexer-common/src/grafting.ts b/packages/indexer-common/src/grafting.ts index ecdc0a948..1211c8a0a 100644 --- a/packages/indexer-common/src/grafting.ts +++ b/packages/indexer-common/src/grafting.ts @@ -27,8 +27,8 @@ interface SubgraphGraftStatus extends GraftableSubgraph { indexingStatus: IndexingStatus | null } -// Resolves all graft dependencies for a given subgraph -export async function resolveGrafting( +// Discovers all graft dependencies for a given subgraph +export async function discoverGraftBases( subgraphManifestResolver: SubgraphManifestResolver, targetDeployment: SubgraphDeploymentID, maxIterations: number = 100, From 302db9b21bb3a0b595c6b53bd549c2a5111dc656 Mon Sep 17 00:00:00 2001 From: tilacog Date: Tue, 24 Oct 2023 18:33:13 -0300 Subject: [PATCH 09/16] common: implement resolveGraftedSubgraphDeployment function --- packages/indexer-common/src/grafting.ts | 87 +++++++++++++++++++++---- packages/indexer-common/src/types.ts | 12 ++++ 2 files changed, 87 insertions(+), 12 deletions(-) diff --git a/packages/indexer-common/src/grafting.ts b/packages/indexer-common/src/grafting.ts index 1211c8a0a..e4a115a83 100644 --- a/packages/indexer-common/src/grafting.ts +++ b/packages/indexer-common/src/grafting.ts @@ -1,8 +1,23 @@ import { SubgraphDeploymentID } from '@graphprotocol/common-ts' import { GraphNodeInterface } from './graph-node' -import { BlockPointer, SubgraphManifest } from './types' +import { + BlockPointer, + SubgraphDeploymentDecision, + SubgraphDeploymentDecisionKind, + SubgraphManifest, +} from './types' import { indexerError, IndexerErrorCode } from './errors' +type SubgraphManifestResolver = ( + subgraphID: SubgraphDeploymentID, +) => Promise + +interface IndexingStatus { + latestBlock: BlockPointer | null + health: string + synced: boolean +} + export interface GraftBase { block: number base: SubgraphDeploymentID @@ -13,18 +28,17 @@ export interface GraftableSubgraph { graft: GraftBase | null // Root subgraph does not have a graft base } -type SubgraphManifestResolver = ( - subgraphID: SubgraphDeploymentID, -) => Promise - -interface IndexingStatus { - latestBlock: BlockPointer | null - health: string - synced: boolean +interface GraftableSubgraphStatus extends GraftableSubgraph { + indexingStatus: IndexingStatus | null } -interface SubgraphGraftStatus extends GraftableSubgraph { - indexingStatus: IndexingStatus | null +// TODO: use this type instead of a plain list. +// Benefits: No need to check for graft base block on the adjacent sibling. +interface SubgraphGraftLineage { + target: SubgraphDeploymentID + root: GraftBase + // list of descending graft bases, except the root. + bases: GraftBase[] } // Discovers all graft dependencies for a given subgraph @@ -64,7 +78,7 @@ export async function discoverGraftBases( export async function getIndexingStatusOfGraftableSubgraph( subgraph: GraftableSubgraph, graphNode: GraphNodeInterface, -): Promise { +): Promise { let response try { response = await graphNode.indexingStatus([subgraph.deployment]) @@ -88,3 +102,52 @@ export async function getIndexingStatusOfGraftableSubgraph( } return { ...subgraph, indexingStatus } } + +export function resolveGraftedSubgraphDeployment( + subgraphLineage: GraftableSubgraphStatus[], +): SubgraphDeploymentDecision[] { + const deploymentDecisions: SubgraphDeploymentDecision[] = [] + + // Check lineage size before making any assumptions. + if (subgraphLineage.length < 2) { + throw new Error( + `Invalid input: Expected at least two members in graft lineage but got ${subgraphLineage.length}`, + ) + } + // Check for any unsynced base. + // Iterate backwards while ignoring the target deployment (first element). + for (let i = subgraphLineage.length - 1; i > 1; i--) { + const graft = subgraphLineage[i] + + // Block height is stored in the previous element in the lineage list. + // Since we are skipping the root (last element), the graft info is expected to be present. + const desiredBlockHeight = subgraphLineage[i - 1].graft!.block + + if (!graft.indexingStatus || !graft.indexingStatus.latestBlock) { + // Graph Node is not aware of this subgraph deployment. We must deploy it and look no further. + deploymentDecisions.push({ + deployment: graft.deployment, + deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, + }) + break + } else { + // Deployment exists. + + // Is it sufficiently synced? + if (graft.indexingStatus.latestBlock.number >= desiredBlockHeight) { + // If so, we can stop syncing it. + deploymentDecisions.push({ + deployment: graft.deployment, + deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + }) + continue + } + + // Is it healthy? + if (graft.indexingStatus.health !== 'healthy') { + throw new Error(`Unhealthy graft base: ${graft.deployment}`) + } + } + } + return deploymentDecisions +} diff --git a/packages/indexer-common/src/types.ts b/packages/indexer-common/src/types.ts index c0885a60d..d5896f05b 100644 --- a/packages/indexer-common/src/types.ts +++ b/packages/indexer-common/src/types.ts @@ -120,3 +120,15 @@ export const SubgraphManifestSchema = z.object({ }) export type SubgraphManifest = z.infer + +export enum SubgraphDeploymentDecisionKind { + CREATE = 'create', + DEPLOY = 'deploy', + REMOVE = 'remove', + // Possible new members: PAUSE, DROP, NOOP +} + +export interface SubgraphDeploymentDecision { + deployment: SubgraphDeploymentID + deploymentDecision: SubgraphDeploymentDecisionKind +} From 3e8c30b7155204a4cd5cc2ac397dc122b17601cb Mon Sep 17 00:00:00 2001 From: tilacog Date: Wed, 25 Oct 2023 12:09:39 -0300 Subject: [PATCH 10/16] common: refactor grafting types --- .../src/__tests__/grafting.test.ts | 39 +++--- packages/indexer-common/src/grafting.ts | 115 ++++++++++-------- 2 files changed, 81 insertions(+), 73 deletions(-) diff --git a/packages/indexer-common/src/__tests__/grafting.test.ts b/packages/indexer-common/src/__tests__/grafting.test.ts index e41f67be3..3b7062b33 100644 --- a/packages/indexer-common/src/__tests__/grafting.test.ts +++ b/packages/indexer-common/src/__tests__/grafting.test.ts @@ -1,5 +1,6 @@ -import { discoverGraftBases, GraftableSubgraph } from '../grafting' +import { discoverLineage, SubgraphLineage } from '../grafting' import { SubgraphDeploymentID } from '@graphprotocol/common-ts' +import { indexerError, IndexerErrorCode } from '../errors' // Create a mock for the fetchSubgraphManifest function const fakeSubgraphManifestResolver = jest.fn() @@ -10,7 +11,7 @@ const base1 = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrYj' const base2 = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrYk' const base3 = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrYn' -describe('resolveGrafting', () => { +describe('discoverLineage', () => { beforeEach(() => { jest.clearAllMocks() const mockManifests = [ @@ -38,39 +39,37 @@ describe('resolveGrafting', () => { test('should resolve grafting with multiple iterations', async () => { const targetDeployment = new SubgraphDeploymentID(target) - const result: GraftableSubgraph[] = await discoverGraftBases( + const result: SubgraphLineage = await discoverLineage( fakeSubgraphManifestResolver, targetDeployment, ) - expect(result).toEqual([ - { - deployment: targetDeployment, - graft: { block: 30, base: new SubgraphDeploymentID(base1) }, - }, - { - deployment: new SubgraphDeploymentID(base1), - graft: { block: 20, base: new SubgraphDeploymentID(base2) }, - }, - { - deployment: new SubgraphDeploymentID(base2), - graft: { block: 10, base: new SubgraphDeploymentID(base3) }, - }, - { deployment: new SubgraphDeploymentID(base3), graft: null }, - ]) + const expected = { + target: targetDeployment, + bases: [ + { deployment: new SubgraphDeploymentID(base1), block: 30 }, + { deployment: new SubgraphDeploymentID(base2), block: 20 }, + { deployment: new SubgraphDeploymentID(base3), block: 10 }, + ], + } + + expect(result).toStrictEqual(expected) expect(fakeSubgraphManifestResolver).toHaveBeenCalledTimes(4) }) test('should resolve grafting when max iterations are reached', async () => { const targetDeployment = new SubgraphDeploymentID(target) expect(() => - discoverGraftBases( + discoverLineage( fakeSubgraphManifestResolver, targetDeployment, 2, // Set maxIterations to 2 ), ).rejects.toThrow( - `Failed to find a graft root for target subgraph deployment (${target}) after 2 iterations.`, + indexerError( + IndexerErrorCode.IE075, + `Failed to find the graft root for target subgraph deployment (${target}) after 2 iterations.`, + ), ) }) }) diff --git a/packages/indexer-common/src/grafting.ts b/packages/indexer-common/src/grafting.ts index e4a115a83..c4f109dd0 100644 --- a/packages/indexer-common/src/grafting.ts +++ b/packages/indexer-common/src/grafting.ts @@ -8,83 +8,94 @@ import { } from './types' import { indexerError, IndexerErrorCode } from './errors' +// Any type that can return a SubgraphManifest when given a +// SubgraphDeploymentID as input. type SubgraphManifestResolver = ( subgraphID: SubgraphDeploymentID, ) => Promise +// Syncing status obtained from Graph-Node. interface IndexingStatus { latestBlock: BlockPointer | null health: string synced: boolean } +// Naive grafting information: contains only the parent subgraph +// deployment and its block height. export interface GraftBase { block: number - base: SubgraphDeploymentID + deployment: SubgraphDeploymentID } -export interface GraftableSubgraph { - deployment: SubgraphDeploymentID - graft: GraftBase | null // Root subgraph does not have a graft base +// Naive lineage information for a subgraph, consisting of a target +// deployment and the series of its graft dependencies, in descending +// order. +export interface SubgraphLineage { + target: SubgraphDeploymentID + // list of descending graft bases (root is the last element). + bases: GraftBase[] } -interface GraftableSubgraphStatus extends GraftableSubgraph { +// Graft base information enriched with indexing status obtained from +// Graph-Node. +interface GraftSubject extends GraftBase { + // No indexing status implies undeployed subgraph. indexingStatus: IndexingStatus | null } -// TODO: use this type instead of a plain list. -// Benefits: No need to check for graft base block on the adjacent sibling. -interface SubgraphGraftLineage { - target: SubgraphDeploymentID - root: GraftBase - // list of descending graft bases, except the root. - bases: GraftBase[] +// Subgraph lineage information enriched with indexing status for each +// graft dependency. +export interface SubgraphLineageWithStatus extends SubgraphLineage { + bases: GraftSubject[] } -// Discovers all graft dependencies for a given subgraph -export async function discoverGraftBases( +// Discovers all graft dependencies for a given subgraph. +export async function discoverLineage( subgraphManifestResolver: SubgraphManifestResolver, targetDeployment: SubgraphDeploymentID, maxIterations: number = 100, -): Promise { - const graftBases: GraftableSubgraph[] = [] +): Promise { + const graftBases: GraftBase[] = [] let iterationCount = 0 - let deployment = targetDeployment + let foundRoot = false + let currentDeployment = targetDeployment while (iterationCount < maxIterations) { - const manifest = await subgraphManifestResolver(deployment) + const manifest = await subgraphManifestResolver(currentDeployment) let graft: GraftBase | null = null if (manifest.features?.includes('grafting') && manifest.graft) { - // Found a graft base + // Found a graft base. const base = new SubgraphDeploymentID(manifest.graft.base) - graft = { block: manifest.graft.block, base } - graftBases.push({ deployment, graft }) - deployment = base + graft = { block: manifest.graft.block, deployment: base } + graftBases.push(graft) + currentDeployment = base } else { - // Reached root subgraph, stop iterating - iterationCount = maxIterations - graftBases.push({ deployment, graft }) + // Reached root subgraph, stop iterating. + foundRoot = true + break } iterationCount++ } - // Check if we have found the graft root - if (graftBases.length > 0 && graftBases[graftBases.length - 1].graft !== null) { - throw new Error( - `Failed to find a graft root for target subgraph deployment (${targetDeployment.ipfsHash}) after ${iterationCount} iterations.`, + // Check if we have found the graft root. + if (!foundRoot) { + throw indexerError( + IndexerErrorCode.IE075, + `Failed to find the graft root for target subgraph deployment (${targetDeployment.ipfsHash}) after ${iterationCount} iterations.`, ) } - return graftBases + return { target: targetDeployment, bases: graftBases } } -export async function getIndexingStatusOfGraftableSubgraph( - subgraph: GraftableSubgraph, +// Adds indexing status to a naive GraftBase. +export async function getIndexingStatus( + graftBase: GraftBase, graphNode: GraphNodeInterface, -): Promise { +): Promise { let response try { - response = await graphNode.indexingStatus([subgraph.deployment]) + response = await graphNode.indexingStatus([graftBase.deployment]) } catch (error) { const message = `Failed to fetch indexing status when resolving subgraph grafts` - // TODO: log this error throw indexerError(IndexerErrorCode.IE075, { message, error }) } let indexingStatus: IndexingStatus | null = null @@ -100,29 +111,26 @@ export async function getIndexingStatusOfGraftableSubgraph( latestBlock, } } - return { ...subgraph, indexingStatus } + return { ...graftBase, indexingStatus } } -export function resolveGraftedSubgraphDeployment( - subgraphLineage: GraftableSubgraphStatus[], +export function determineSubgraphDeploymentDecisions( + subgraphLineage: SubgraphLineageWithStatus, ): SubgraphDeploymentDecision[] { const deploymentDecisions: SubgraphDeploymentDecision[] = [] // Check lineage size before making any assumptions. - if (subgraphLineage.length < 2) { - throw new Error( - `Invalid input: Expected at least two members in graft lineage but got ${subgraphLineage.length}`, + if (!subgraphLineage.bases) { + throw indexerError( + IndexerErrorCode.IE075, + 'Expected target subgraph to have at least one graft base.', ) } - // Check for any unsynced base. - // Iterate backwards while ignoring the target deployment (first element). - for (let i = subgraphLineage.length - 1; i > 1; i--) { - const graft = subgraphLineage[i] - - // Block height is stored in the previous element in the lineage list. - // Since we are skipping the root (last element), the graft info is expected to be present. - const desiredBlockHeight = subgraphLineage[i - 1].graft!.block - + // Check for undeployed and unsynced graft bases. + // Start from the root (iterate backwards). + for (let i = subgraphLineage.bases.length - 1; i >= 0; i--) { + const graft = subgraphLineage.bases[i] + const desiredBlockHeight = graft.block if (!graft.indexingStatus || !graft.indexingStatus.latestBlock) { // Graph Node is not aware of this subgraph deployment. We must deploy it and look no further. deploymentDecisions.push({ @@ -132,7 +140,6 @@ export function resolveGraftedSubgraphDeployment( break } else { // Deployment exists. - // Is it sufficiently synced? if (graft.indexingStatus.latestBlock.number >= desiredBlockHeight) { // If so, we can stop syncing it. @@ -142,10 +149,12 @@ export function resolveGraftedSubgraphDeployment( }) continue } - // Is it healthy? if (graft.indexingStatus.health !== 'healthy') { - throw new Error(`Unhealthy graft base: ${graft.deployment}`) + throw indexerError(IndexerErrorCode.IE075, { + message: `Cannot deploy subgraph due to unhealthy graft base: ${graft.deployment}`, + graftDependencies: subgraphLineage, + }) } } } From 99a0d3c0dde4c881cc47a23e17181f153c7c923a Mon Sep 17 00:00:00 2001 From: tilacog Date: Wed, 25 Oct 2023 14:36:24 -0300 Subject: [PATCH 11/16] common: refactor and test determineSubgraphDeploymentDecisions fn --- .../src/__tests__/grafting.test.ts | 219 +++++++++++++++++- packages/indexer-common/src/grafting.ts | 31 ++- 2 files changed, 235 insertions(+), 15 deletions(-) diff --git a/packages/indexer-common/src/__tests__/grafting.test.ts b/packages/indexer-common/src/__tests__/grafting.test.ts index 3b7062b33..801d19d7e 100644 --- a/packages/indexer-common/src/__tests__/grafting.test.ts +++ b/packages/indexer-common/src/__tests__/grafting.test.ts @@ -1,6 +1,12 @@ -import { discoverLineage, SubgraphLineage } from '../grafting' +import { + determineSubgraphDeploymentDecisions, + discoverLineage, + SubgraphLineage, + SubgraphLineageWithStatus, +} from '../grafting' import { SubgraphDeploymentID } from '@graphprotocol/common-ts' import { indexerError, IndexerErrorCode } from '../errors' +import { SubgraphDeploymentDecisionKind } from '../types' // Create a mock for the fetchSubgraphManifest function const fakeSubgraphManifestResolver = jest.fn() @@ -10,8 +16,9 @@ const target = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrZX' const base1 = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrYj' const base2 = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrYk' const base3 = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrYn' +const base4 = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrZj' -describe('discoverLineage', () => { +describe('discoverLineage function', () => { beforeEach(() => { jest.clearAllMocks() const mockManifests = [ @@ -36,7 +43,7 @@ describe('discoverLineage', () => { .mockImplementationOnce(() => mockManifests[3]) }) - test('should resolve grafting with multiple iterations', async () => { + test('should discover a subgraph grafting lineage', async () => { const targetDeployment = new SubgraphDeploymentID(target) const result: SubgraphLineage = await discoverLineage( @@ -57,7 +64,7 @@ describe('discoverLineage', () => { expect(fakeSubgraphManifestResolver).toHaveBeenCalledTimes(4) }) - test('should resolve grafting when max iterations are reached', async () => { + test('should throw an error after maximum iteration count is reached', async () => { const targetDeployment = new SubgraphDeploymentID(target) expect(() => discoverLineage( @@ -73,3 +80,207 @@ describe('discoverLineage', () => { ) }) }) + +describe('determineSubgraphDeploymentDecisions function', () => { + // beforeEach(() => {}) + + test('should throw an error if bases are not provided', () => { + const lineage: SubgraphLineageWithStatus = { + target: new SubgraphDeploymentID(target), + bases: [], + } + expect(() => determineSubgraphDeploymentDecisions(lineage)).toThrow( + indexerError( + IndexerErrorCode.IE075, + 'Expected target subgraph to have at least one graft base.', + ), + ) + }) + + test('should return DEPLOY subgraph deployment decision if its single base has no indexing status', () => { + const subgraphLineage: SubgraphLineageWithStatus = { + target: new SubgraphDeploymentID(target), + bases: [ + { + block: 1, + deployment: new SubgraphDeploymentID(base1), + indexingStatus: null, + }, + ], + } + const decisions = determineSubgraphDeploymentDecisions(subgraphLineage) + const expected = [ + { + deployment: new SubgraphDeploymentID(base1), + deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, + }, + ] + expect(decisions).toEqual(expected) + }) + + test('should return DEPLOY subgraph deployment decision for the latest undeployed base', () => { + const subgraphLineage: SubgraphLineageWithStatus = { + target: new SubgraphDeploymentID(target), + bases: [ + { + block: 30, + deployment: new SubgraphDeploymentID(base1), + indexingStatus: null, + }, + { + block: 20, + deployment: new SubgraphDeploymentID(base2), + indexingStatus: null, + }, + ], + } + const decisions = determineSubgraphDeploymentDecisions(subgraphLineage) + const expected = [ + { + deployment: new SubgraphDeploymentID(base2), + deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, + }, + ] + expect(decisions).toEqual(expected) + }) + + test('should return REMOVE decision for sufficiently synced bases', () => { + const subgraphLineage: SubgraphLineageWithStatus = { + target: new SubgraphDeploymentID(target), + bases: [ + { + block: 10, + deployment: new SubgraphDeploymentID(base1), + indexingStatus: { + latestBlock: { + number: 10, + hash: 'foo', + }, + health: 'healthy', + }, + }, + ], + } + const decisions = determineSubgraphDeploymentDecisions(subgraphLineage) + const expected = [ + { + deployment: new SubgraphDeploymentID(base1), + deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + }, + ] + expect(decisions).toEqual(expected) + }) + + test('should return DEPLOY for the latest undeployed base and REMOVE for synced bases', () => { + const subgraphLineage: SubgraphLineageWithStatus = { + target: new SubgraphDeploymentID(target), + bases: [ + { + block: 30, + deployment: new SubgraphDeploymentID(base1), + indexingStatus: null, + }, + { + block: 20, + deployment: new SubgraphDeploymentID(base2), + indexingStatus: { + latestBlock: { + number: 20, + hash: 'foo', + }, + health: 'healthy', + }, + }, + { + block: 10, + deployment: new SubgraphDeploymentID(base3), + indexingStatus: { + latestBlock: { + number: 10, + hash: 'bar', + }, + health: 'healthy', + }, + }, + ], + } + const decisions = determineSubgraphDeploymentDecisions(subgraphLineage) + const expected = [ + { + deployment: new SubgraphDeploymentID(base3), + deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + }, + { + deployment: new SubgraphDeploymentID(base2), + deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + }, + { + deployment: new SubgraphDeploymentID(base1), + deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, + }, + ] + expect(decisions).toEqual(expected) + }) + + // This test represents the case when older graft bases are removed after serving as a base. + test('should return DEPLOY for the latest base after the next sufficiently synced base', () => { + const subgraphLineage: SubgraphLineageWithStatus = { + target: new SubgraphDeploymentID(target), + bases: [ + { + block: 30, + deployment: new SubgraphDeploymentID(base1), + indexingStatus: null, + }, + { + block: 20, + deployment: new SubgraphDeploymentID(base2), + indexingStatus: { + latestBlock: { + number: 20, + hash: 'foo', + }, + health: 'healthy', + }, + }, + // Since an earlier synced/healthy graft base exists, this one is not essential + // and should be ignored. No deployment decisions should be made about it. + { + block: 10, + deployment: new SubgraphDeploymentID(base3), + indexingStatus: null, + }, + // Even though this graft base is not essential for deploying the target subgraph, + // it should be removed. + { + block: 5, + deployment: new SubgraphDeploymentID(base4), + indexingStatus: { + latestBlock: { + number: 20, + hash: 'baz', + }, + health: 'healthy', + }, + }, + ], + } + const decisions = determineSubgraphDeploymentDecisions(subgraphLineage) + const expected = [ + { + deployment: new SubgraphDeploymentID(base4), + deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + }, + // Base 3 is intentionally left out of the result. + { + deployment: new SubgraphDeploymentID(base2), + deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + }, + { + deployment: new SubgraphDeploymentID(base1), + deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, + }, + ] + expect(decisions).toEqual(expected) + }) +}) diff --git a/packages/indexer-common/src/grafting.ts b/packages/indexer-common/src/grafting.ts index c4f109dd0..9890de573 100644 --- a/packages/indexer-common/src/grafting.ts +++ b/packages/indexer-common/src/grafting.ts @@ -18,7 +18,6 @@ type SubgraphManifestResolver = ( interface IndexingStatus { latestBlock: BlockPointer | null health: string - synced: boolean } // Naive grafting information: contains only the parent subgraph @@ -107,7 +106,6 @@ export async function getIndexingStatus( } indexingStatus = { health: subgraphIndexingStatus.health, - synced: subgraphIndexingStatus.synced, latestBlock, } } @@ -120,24 +118,35 @@ export function determineSubgraphDeploymentDecisions( const deploymentDecisions: SubgraphDeploymentDecision[] = [] // Check lineage size before making any assumptions. - if (!subgraphLineage.bases) { + if (!subgraphLineage.bases.length) { throw indexerError( IndexerErrorCode.IE075, 'Expected target subgraph to have at least one graft base.', ) } // Check for undeployed and unsynced graft bases. - // Start from the root (iterate backwards). - for (let i = subgraphLineage.bases.length - 1; i >= 0; i--) { + + // Iterate backwards, considering only bases that are essential for subgraph deployment. + let earliestValidBaseIndex = subgraphLineage.bases.findIndex( + (graft) => graft.indexingStatus && graft.indexingStatus.latestBlock, + ) + const lastIndex = subgraphLineage.bases.length - 1 + earliestValidBaseIndex = + earliestValidBaseIndex === -1 ? lastIndex : earliestValidBaseIndex + + for (let i = lastIndex; i >= 0; i--) { const graft = subgraphLineage.bases[i] const desiredBlockHeight = graft.block if (!graft.indexingStatus || !graft.indexingStatus.latestBlock) { - // Graph Node is not aware of this subgraph deployment. We must deploy it and look no further. - deploymentDecisions.push({ - deployment: graft.deployment, - deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, - }) - break + // Ignore undeployed graft bases beyond the earliest valid one. + if (i <= earliestValidBaseIndex) { + // Graph Node is not aware of this subgraph deployment. We must deploy it and look no further. + deploymentDecisions.push({ + deployment: graft.deployment, + deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, + }) + break + } } else { // Deployment exists. // Is it sufficiently synced? From b45097aa61543e49beb999670d2d280c331c2417 Mon Sep 17 00:00:00 2001 From: tilacog Date: Wed, 25 Oct 2023 15:25:20 -0300 Subject: [PATCH 12/16] common: fixed grafting tests --- .../src/__tests__/grafting.test.ts | 93 +++++++++++++++---- packages/indexer-common/src/grafting.ts | 2 +- 2 files changed, 78 insertions(+), 17 deletions(-) diff --git a/packages/indexer-common/src/__tests__/grafting.test.ts b/packages/indexer-common/src/__tests__/grafting.test.ts index 801d19d7e..48d60790e 100644 --- a/packages/indexer-common/src/__tests__/grafting.test.ts +++ b/packages/indexer-common/src/__tests__/grafting.test.ts @@ -66,35 +66,96 @@ describe('discoverLineage function', () => { test('should throw an error after maximum iteration count is reached', async () => { const targetDeployment = new SubgraphDeploymentID(target) - expect(() => - discoverLineage( + let threwError = false + try { + await discoverLineage( fakeSubgraphManifestResolver, targetDeployment, 2, // Set maxIterations to 2 - ), - ).rejects.toThrow( - indexerError( - IndexerErrorCode.IE075, + ) + } catch (err) { + expect(err.code).toStrictEqual(IndexerErrorCode.IE075) + expect(err.cause).toStrictEqual( `Failed to find the graft root for target subgraph deployment (${target}) after 2 iterations.`, - ), - ) + ) + threwError = true + } + expect(threwError).toBeTruthy() }) }) describe('determineSubgraphDeploymentDecisions function', () => { - // beforeEach(() => {}) - test('should throw an error if bases are not provided', () => { - const lineage: SubgraphLineageWithStatus = { + const subgraphLineage: SubgraphLineageWithStatus = { target: new SubgraphDeploymentID(target), bases: [], } - expect(() => determineSubgraphDeploymentDecisions(lineage)).toThrow( - indexerError( - IndexerErrorCode.IE075, + + let threwError = false + try { + determineSubgraphDeploymentDecisions(subgraphLineage) + } catch (err) { + expect(err.code).toStrictEqual(IndexerErrorCode.IE075) + expect(err.cause).toStrictEqual( 'Expected target subgraph to have at least one graft base.', - ), - ) + ) + + threwError = true + } + expect(threwError).toBeTruthy() + }) + + test('should return an empty array if a single base is still syncing and healthy', () => { + const subgraphLineage: SubgraphLineageWithStatus = { + target: new SubgraphDeploymentID(target), + bases: [ + { + block: 10, + deployment: new SubgraphDeploymentID(base1), + indexingStatus: { + latestBlock: { + number: 5, + hash: 'foo', + }, + health: 'healthy', + }, + }, + ], + } + expect(determineSubgraphDeploymentDecisions(subgraphLineage)).toEqual([]) + }) + + test('should throw an error if an unsynced base is unhealthy', () => { + const graftBase = new SubgraphDeploymentID(base1) + const subgraphLineage: SubgraphLineageWithStatus = { + target: new SubgraphDeploymentID(target), + bases: [ + { + block: 10, + deployment: graftBase, + indexingStatus: { + latestBlock: { + number: 5, + hash: 'foo', + }, + health: 'not-healthy', + }, + }, + ], + } + + let threwError = false + try { + determineSubgraphDeploymentDecisions(subgraphLineage) + } catch (err) { + expect(err.code).toStrictEqual(IndexerErrorCode.IE075) + expect(err.cause).toStrictEqual({ + message: `Cannot deploy subgraph due to unhealthy graft base: ${graftBase.ipfsHash}`, + graftDependencies: subgraphLineage, + }) + threwError = true + } + expect(threwError).toBeTruthy() }) test('should return DEPLOY subgraph deployment decision if its single base has no indexing status', () => { diff --git a/packages/indexer-common/src/grafting.ts b/packages/indexer-common/src/grafting.ts index 9890de573..6d0f9b9e5 100644 --- a/packages/indexer-common/src/grafting.ts +++ b/packages/indexer-common/src/grafting.ts @@ -161,7 +161,7 @@ export function determineSubgraphDeploymentDecisions( // Is it healthy? if (graft.indexingStatus.health !== 'healthy') { throw indexerError(IndexerErrorCode.IE075, { - message: `Cannot deploy subgraph due to unhealthy graft base: ${graft.deployment}`, + message: `Cannot deploy subgraph due to unhealthy graft base: ${graft.deployment.ipfsHash}`, graftDependencies: subgraphLineage, }) } From f6856415cbaa4f3732e2ac90b6b41b15ff7e3451 Mon Sep 17 00:00:00 2001 From: tilacog Date: Mon, 30 Oct 2023 09:46:26 -0300 Subject: [PATCH 13/16] common: implement deploySubgraph function and its dependencies --- .../src/__tests__/grafting.test.ts | 29 +- .../src/__tests__/subgraph.test.ts | 2 +- packages/indexer-common/src/grafting.ts | 46 ++- packages/indexer-common/src/graph-node.ts | 13 +- packages/indexer-common/src/index.ts | 1 + .../indexer-common/src/subgraph-deployment.ts | 283 ++++++++++++++++++ packages/indexer-common/src/subgraphs.ts | 14 +- packages/indexer-common/src/types.ts | 17 +- 8 files changed, 375 insertions(+), 30 deletions(-) create mode 100644 packages/indexer-common/src/subgraph-deployment.ts diff --git a/packages/indexer-common/src/__tests__/grafting.test.ts b/packages/indexer-common/src/__tests__/grafting.test.ts index 48d60790e..baa007bb0 100644 --- a/packages/indexer-common/src/__tests__/grafting.test.ts +++ b/packages/indexer-common/src/__tests__/grafting.test.ts @@ -5,7 +5,7 @@ import { SubgraphLineageWithStatus, } from '../grafting' import { SubgraphDeploymentID } from '@graphprotocol/common-ts' -import { indexerError, IndexerErrorCode } from '../errors' +import { IndexerErrorCode } from '../errors' import { SubgraphDeploymentDecisionKind } from '../types' // Create a mock for the fetchSubgraphManifest function @@ -173,7 +173,8 @@ describe('determineSubgraphDeploymentDecisions function', () => { const expected = [ { deployment: new SubgraphDeploymentID(base1), - deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, + kind: SubgraphDeploymentDecisionKind.DEPLOY, + expectedBlockHeight: 1, }, ] expect(decisions).toEqual(expected) @@ -199,7 +200,8 @@ describe('determineSubgraphDeploymentDecisions function', () => { const expected = [ { deployment: new SubgraphDeploymentID(base2), - deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, + kind: SubgraphDeploymentDecisionKind.DEPLOY, + expectedBlockHeight: 20, }, ] expect(decisions).toEqual(expected) @@ -226,7 +228,8 @@ describe('determineSubgraphDeploymentDecisions function', () => { const expected = [ { deployment: new SubgraphDeploymentID(base1), - deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + kind: SubgraphDeploymentDecisionKind.REMOVE, + expectedBlockHeight: 10, }, ] expect(decisions).toEqual(expected) @@ -269,15 +272,18 @@ describe('determineSubgraphDeploymentDecisions function', () => { const expected = [ { deployment: new SubgraphDeploymentID(base3), - deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + kind: SubgraphDeploymentDecisionKind.REMOVE, + expectedBlockHeight: 10, }, { deployment: new SubgraphDeploymentID(base2), - deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + kind: SubgraphDeploymentDecisionKind.REMOVE, + expectedBlockHeight: 20, }, { deployment: new SubgraphDeploymentID(base1), - deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, + kind: SubgraphDeploymentDecisionKind.DEPLOY, + expectedBlockHeight: 30, }, ] expect(decisions).toEqual(expected) @@ -330,16 +336,19 @@ describe('determineSubgraphDeploymentDecisions function', () => { const expected = [ { deployment: new SubgraphDeploymentID(base4), - deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + kind: SubgraphDeploymentDecisionKind.REMOVE, + expectedBlockHeight: 5, }, // Base 3 is intentionally left out of the result. { deployment: new SubgraphDeploymentID(base2), - deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + kind: SubgraphDeploymentDecisionKind.REMOVE, + expectedBlockHeight: 20, }, { deployment: new SubgraphDeploymentID(base1), - deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, + kind: SubgraphDeploymentDecisionKind.DEPLOY, + expectedBlockHeight: 30, }, ] expect(decisions).toEqual(expected) diff --git a/packages/indexer-common/src/__tests__/subgraph.test.ts b/packages/indexer-common/src/__tests__/subgraph.test.ts index 23653713f..4ed780ff4 100644 --- a/packages/indexer-common/src/__tests__/subgraph.test.ts +++ b/packages/indexer-common/src/__tests__/subgraph.test.ts @@ -1,10 +1,10 @@ import { DocumentNode, print } from 'graphql' import { SubgraphFreshnessChecker, - LoggerInterface, ProviderInterface, SubgraphQueryInterface, } from '../subgraphs' +import { LoggerInterface } from '../types' import { QueryResult } from '../network-subgraph' import gql from 'graphql-tag' import { mergeSelectionSets } from '../utils' diff --git a/packages/indexer-common/src/grafting.ts b/packages/indexer-common/src/grafting.ts index 6d0f9b9e5..67e763a6c 100644 --- a/packages/indexer-common/src/grafting.ts +++ b/packages/indexer-common/src/grafting.ts @@ -2,15 +2,17 @@ import { SubgraphDeploymentID } from '@graphprotocol/common-ts' import { GraphNodeInterface } from './graph-node' import { BlockPointer, + LoggerInterface, SubgraphDeploymentDecision, SubgraphDeploymentDecisionKind, SubgraphManifest, } from './types' import { indexerError, IndexerErrorCode } from './errors' +import pMap from 'p-map' // Any type that can return a SubgraphManifest when given a // SubgraphDeploymentID as input. -type SubgraphManifestResolver = ( +export type SubgraphManifestResolver = ( subgraphID: SubgraphDeploymentID, ) => Promise @@ -49,6 +51,10 @@ export interface SubgraphLineageWithStatus extends SubgraphLineage { bases: GraftSubject[] } +export interface GraftBaseDeploymentDecision extends SubgraphDeploymentDecision { + expectedBlockHeight: number +} + // Discovers all graft dependencies for a given subgraph. export async function discoverLineage( subgraphManifestResolver: SubgraphManifestResolver, @@ -114,8 +120,8 @@ export async function getIndexingStatus( export function determineSubgraphDeploymentDecisions( subgraphLineage: SubgraphLineageWithStatus, -): SubgraphDeploymentDecision[] { - const deploymentDecisions: SubgraphDeploymentDecision[] = [] +): GraftBaseDeploymentDecision[] { + const deploymentDecisions: GraftBaseDeploymentDecision[] = [] // Check lineage size before making any assumptions. if (!subgraphLineage.bases.length) { @@ -143,7 +149,8 @@ export function determineSubgraphDeploymentDecisions( // Graph Node is not aware of this subgraph deployment. We must deploy it and look no further. deploymentDecisions.push({ deployment: graft.deployment, - deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, + kind: SubgraphDeploymentDecisionKind.DEPLOY, + expectedBlockHeight: graft.block, }) break } @@ -154,7 +161,8 @@ export function determineSubgraphDeploymentDecisions( // If so, we can stop syncing it. deploymentDecisions.push({ deployment: graft.deployment, - deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + kind: SubgraphDeploymentDecisionKind.REMOVE, + expectedBlockHeight: graft.block, }) continue } @@ -169,3 +177,31 @@ export function determineSubgraphDeploymentDecisions( } return deploymentDecisions } + +// Queries the Graph Node to get the deployment status of each graft base in the +// subgraph lineage. +export async function queryGraftBaseStatuses( + subgraphLineage: SubgraphLineage, + graphNode: GraphNodeInterface, + parentLogger: LoggerInterface, + concurrency: number = 5, +): Promise { + const logger = parentLogger.child({ function: 'queryGraftBaseStatuses' }) + logger.debug('Attempting to resolve graft bases for target subgraph') + + // Fetch deployment details for each graft base + logger.debug('Querying Graph-Node for graft bases deployment status') + const graftBasesDeploymentStatus = await pMap( + subgraphLineage.bases, + async (graftBase: GraftBase) => await getIndexingStatus(graftBase, graphNode), + { + stopOnError: true, + concurrency, + }, + ) + + return { + target: subgraphLineage.target, + bases: graftBasesDeploymentStatus, + } +} diff --git a/packages/indexer-common/src/graph-node.ts b/packages/indexer-common/src/graph-node.ts index e8b895b59..946d6bb72 100644 --- a/packages/indexer-common/src/graph-node.ts +++ b/packages/indexer-common/src/graph-node.ts @@ -62,6 +62,8 @@ export const parseGraphQLBlockPointer = (block: any): BlockPointer | null => export interface GraphNodeInterface { indexingStatus(deployments: SubgraphDeploymentID[]): Promise + ensure(name: string, deployment: SubgraphDeploymentID): Promise + remove(deployment: SubgraphDeploymentID): Promise } export class GraphNode implements GraphNodeInterface { @@ -391,11 +393,16 @@ export class GraphNode implements GraphNodeInterface { } catch (error) { if (!(error instanceof IndexerError)) { const errorCode = IndexerErrorCode.IE020 - this.logger.error(INDEXER_ERROR_MESSAGES[errorCode], { + const unknownIndexerError = indexerError(IndexerErrorCode.IE020, error) + const payload = { name, deployment: deployment.display, - error: indexerError(errorCode, error), - }) + error: unknownIndexerError, + } + this.logger.error(INDEXER_ERROR_MESSAGES[errorCode], payload) + throw unknownIndexerError + } else { + throw error } } } diff --git a/packages/indexer-common/src/index.ts b/packages/indexer-common/src/index.ts index 0090cad0d..8a21d0223 100644 --- a/packages/indexer-common/src/index.ts +++ b/packages/indexer-common/src/index.ts @@ -17,3 +17,4 @@ export * from './utils' export * from './parsers' export * as specification from './network-specification' export * from './multi-networks' +export * from './subgraph-deployment' diff --git a/packages/indexer-common/src/subgraph-deployment.ts b/packages/indexer-common/src/subgraph-deployment.ts new file mode 100644 index 000000000..3b03e4e13 --- /dev/null +++ b/packages/indexer-common/src/subgraph-deployment.ts @@ -0,0 +1,283 @@ +import { SubgraphDeploymentID } from '@graphprotocol/common-ts' +import { LoggerInterface, SubgraphDeploymentDecisionKind } from './types' +import { GraphNodeInterface } from './graph-node' +import { Client, gql } from '@urql/core' +import { + discoverLineage, + determineSubgraphDeploymentDecisions, + SubgraphLineage, + queryGraftBaseStatuses, + GraftBaseDeploymentDecision, +} from './grafting' +import { SubgraphIdentifierType, fetchSubgraphManifest } from './subgraphs' +import { + IndexingDecisionBasis, + IndexingRuleAttributes, + IndexingRuleIdentifier, +} from './indexer-management' +import { IndexerErrorCode, indexerError } from './errors' + +const SET_INDEXING_RULE_MUTATION = gql` + mutation setIndexingRule($rule: IndexingRuleInput!) { + setIndexingRule(rule: $rule) { + identifier + identifierType + custom + decisionBasis + protocolNetwork + } + } +` + +const GET_INDEXING_RULE_QUERY = gql` + query indexingRule($identifier: IndexingRuleIdentifier!) { + indexingRule(identifier: $identifier, merged: false) { + custom + } + } +` +const DELETE_INDEXING_RULE_MUTATION = gql` + mutation deleteIndexingRule($identifier: IndexingRuleIdentifier!) { + deleteIndexingRule(identifier: $identifier) + } +` + +// Deploys a specified subgraph and handles grafting scenarios. +export async function deploySubgraph( + subgraphName: string, + subgraphDeployment: SubgraphDeploymentID, + graphNode: GraphNodeInterface, + ipfsURL: URL, + indexerManagement: Client, + protocolNetwork: string, + parentLogger: LoggerInterface, +): Promise { + // Inspect target subgraph's grafting lineage. + const logger = parentLogger.child({ + function: 'deploySubgraph', + subgraphDeployment, + }) + logger.debug('Attempting to resolve graft bases for target subgraph.') + const subgraphManifestResolver = async (subgraphID: SubgraphDeploymentID) => + await fetchSubgraphManifest(ipfsURL, subgraphID, logger) + const subgraphLineage = await discoverLineage( + subgraphManifestResolver, + subgraphDeployment, + ) + // If there's no graft base, deploy it right away + if (!subgraphLineage.bases.length) { + logger.debug('Subgraph has no graft dependencies.') + return await graphNode.ensure(subgraphName, subgraphDeployment) + } else { + return await deployGraftedSubgraph( + subgraphLineage, + graphNode, + indexerManagement, + protocolNetwork, + logger, + ) + } +} +// Attempts to deploy the first viable base for a grafted subgraph. +// Will create a new indexing rule for the next viable graft base and remove old rules +// for sufficiently synced bases. +async function deployGraftedSubgraph( + subgraphLineage: SubgraphLineage, + graphNode: GraphNodeInterface, + indexerManagement: Client, + protocolNetwork: string, + parentLogger: LoggerInterface, +): Promise { + const logger = parentLogger.child({ + function: 'deployGraftedSubgraph', + targetSubgraph: subgraphLineage.target.ipfsHash, + graftBases: subgraphLineage.bases, + }) + logger.debug('Attempting to deploy first viable base for grafted subgraph.') + + // Fetch the deployment status for all graft bases. + const lineageDeploymentStatus = await queryGraftBaseStatuses( + subgraphLineage, + graphNode, + logger, + ) + + // Inspect if we need to deploy or remove a sufficiently synced graft base. + const deploymentDecisions = determineSubgraphDeploymentDecisions( + lineageDeploymentStatus, + ) + for (const deploymentDecision of deploymentDecisions) { + switch (deploymentDecision.kind) { + case SubgraphDeploymentDecisionKind.DEPLOY: { + // Create an offchain deployment rule for this subgraph. + await createIndexingRuleForGraftBase( + deploymentDecision, + protocolNetwork, + indexerManagement, + logger, + ) + // Deploy the graft base subgraph. + const subgraphName = `indexer-agent/${deploymentDecision.deployment.ipfsHash.slice( + -10, + )}` + logger.info(`Graft Base subgraph deployment`, { + name: subgraphName, + deployment: deploymentDecision.deployment.display, + }) + await graphNode.ensure(subgraphName, deploymentDecision.deployment) + break + } + case SubgraphDeploymentDecisionKind.REMOVE: + await deleteTemporaryIndexingRule( + deploymentDecision, + protocolNetwork, + indexerManagement, + logger, + ) + break + + default: + throw new Error('Unknown deployment decision') + } + } +} + +async function createIndexingRuleForGraftBase( + deploymentDecision: GraftBaseDeploymentDecision, + protocolNetwork: string, + indexerManagement: Client, + logger: LoggerInterface, +): Promise { + const rule: Partial = { + identifier: deploymentDecision.deployment.ipfsHash, + identifierType: SubgraphIdentifierType.DEPLOYMENT, + decisionBasis: IndexingDecisionBasis.OFFCHAIN, + custom: JSON.stringify({ + type: 'graftBase', + targetDeployment: deploymentDecision.deployment.ipfsHash, + block: deploymentDecision.expectedBlockHeight, + }), + protocolNetwork, + } + try { + const result = await indexerManagement + .mutation(SET_INDEXING_RULE_MUTATION, { rule }) + .toPromise() + if (result.error) { + throw result.error + } + logger.debug('Created temporary offchain indexing rule for graft base.', { + deploymentDecision, + }) + } catch (indexerManagementError) { + const error = indexerError(IndexerErrorCode.IE075, indexerManagementError) + logger.warn( + 'Failed to create a temporary offchain indexing rule to support a graft base deployment.', + { error, deploymentDecision }, + ) + throw error + } +} + +// Delete an IndexingRule from the database by querying its ID and checking the 'custom' +// field to ensure it is a temporary rule. +async function deleteTemporaryIndexingRule( + deploymentDecision: GraftBaseDeploymentDecision, + protocolNetwork: string, + indexerManagement: Client, + parentLogger: LoggerInterface, +): Promise { + const identifier: IndexingRuleIdentifier = { + identifier: deploymentDecision.deployment.ipfsHash, + protocolNetwork, + } + const logger = parentLogger.child({ + identifier, + deploymentDecision, + }) + + // Query indexing management client for a indexing rule matching the IPFS hash of this + // subgraph deployment. + const indexingRule = await queryIndexingRule(identifier, indexerManagement, logger) + + if (!indexingRule) { + logger.warn( + 'Failed to find the temporary offchain indexing rule that supported a graft base deployment.', + ) + return + } + + // Check if this is a temporary indexing rule. We should not remove it if there's no + // grafting information stored in its 'custom' field. + const safeToRemove = checkTemporaryIndexingRule( + indexingRule, + deploymentDecision.deployment.ipfsHash, + ) + if (!safeToRemove) { + logger.info( + 'Found indexing rule that was used to support a graft base deployment, ' + + 'but it is not safe to remove it as it might still be in use', + ) + return + } + + // Remove the indexing rule + const deleteResult = await indexerManagement + .mutation(DELETE_INDEXING_RULE_MUTATION, { identifier }) + .toPromise() + if (deleteResult.error) { + throw deleteResult.error + } +} + +type TemporaryIndexingRuleTag = Pick + +async function queryIndexingRule( + identifier: IndexingRuleIdentifier, + indexerManagement: Client, + logger: LoggerInterface, +): Promise { + try { + const result = await indexerManagement + .query(GET_INDEXING_RULE_QUERY, { + identifier, + }) + .toPromise() + if (result.error) { + throw result.error + } + return result.data.indexingRule + } catch (indexerManagementError) { + const error = indexerError(IndexerErrorCode.IE075, indexerManagementError) + logger.warn('Failed to query a temporary offchain indexing rule for its removal.', { + error, + }) + throw error + } +} + +// Returns true if we can identify a tag in this IndexingRule indicating that it is +// temporary, created for the expected graft base deployment. +function checkTemporaryIndexingRule( + rule: TemporaryIndexingRuleTag, + expectedSubgraphDeployment: string, +): boolean { + // Check if we have a string in the 'custom' field. + if (!rule.custom || typeof rule.custom !== 'string') { + return false + } + // Check if that string is a JSON. + let tag + try { + tag = JSON.parse(rule.custom) + } catch (error) { + return false + } + if (!tag || typeof tag !== 'object') { + return false + } + if (tag.type === 'graftBase' && tag.targetDeployment === expectedSubgraphDeployment) { + return true + } + return false +} diff --git a/packages/indexer-common/src/subgraphs.ts b/packages/indexer-common/src/subgraphs.ts index df842ee18..e01e207dd 100644 --- a/packages/indexer-common/src/subgraphs.ts +++ b/packages/indexer-common/src/subgraphs.ts @@ -1,7 +1,12 @@ import { base58 } from 'ethers/lib/utils' import { BigNumber, utils } from 'ethers' import { Logger, SubgraphDeploymentID } from '@graphprotocol/common-ts' -import { SubgraphDeployment, SubgraphManifest, SubgraphManifestSchema } from './types' +import { + LoggerInterface, + SubgraphDeployment, + SubgraphManifest, + SubgraphManifestSchema, +} from './types' import { INDEXING_RULE_GLOBAL, IndexingDecisionBasis, @@ -335,13 +340,6 @@ export interface ProviderInterface { } /* eslint-disable @typescript-eslint/no-explicit-any */ -export interface LoggerInterface { - trace(msg: string, o?: object, ...args: any[]): void - error(msg: string, o?: object, ...args: any[]): void - warn(msg: string, o?: object, ...args: any[]): void - child(bindings: Record): Logger -} - export interface SubgraphQueryInterface { query( query: DocumentNode, diff --git a/packages/indexer-common/src/types.ts b/packages/indexer-common/src/types.ts index d5896f05b..ff2ee5d55 100644 --- a/packages/indexer-common/src/types.ts +++ b/packages/indexer-common/src/types.ts @@ -121,14 +121,25 @@ export const SubgraphManifestSchema = z.object({ export type SubgraphManifest = z.infer +// This enum should aim to match Graph-Node indexer endpoint routes. export enum SubgraphDeploymentDecisionKind { - CREATE = 'create', + // CREATE = 'create', // We don't have any use for the CREATE variant, for now. DEPLOY = 'deploy', REMOVE = 'remove', - // Possible new members: PAUSE, DROP, NOOP } export interface SubgraphDeploymentDecision { deployment: SubgraphDeploymentID - deploymentDecision: SubgraphDeploymentDecisionKind + kind: SubgraphDeploymentDecisionKind } + +/* eslint-disable @typescript-eslint/no-explicit-any */ +export interface LoggerInterface { + trace(msg: string, o?: object, ...args: any[]): void + debug(msg: string, o?: object, ...args: any[]): void + info(msg: string, o?: object, ...args: any[]): void + warn(msg: string, o?: object, ...args: any[]): void + error(msg: string, o?: object, ...args: any[]): void + child(bindings: Record): LoggerInterface +} +/* eslint-disable @typescript-eslint/no-explicit-any */ From 03e8b19227bc6b7993ef425fde0d20a3ded9c791 Mon Sep 17 00:00:00 2001 From: tilacog Date: Tue, 7 Nov 2023 06:44:25 -0300 Subject: [PATCH 14/16] agent: migration for dropping NOT NULL from IndexingRules.protocolnetwork --- ...constraint-from-protocol-network-column.ts | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 packages/indexer-agent/src/db/migrations/12-remove-not-null-constraint-from-protocol-network-column.ts diff --git a/packages/indexer-agent/src/db/migrations/12-remove-not-null-constraint-from-protocol-network-column.ts b/packages/indexer-agent/src/db/migrations/12-remove-not-null-constraint-from-protocol-network-column.ts new file mode 100644 index 000000000..920ed7211 --- /dev/null +++ b/packages/indexer-agent/src/db/migrations/12-remove-not-null-constraint-from-protocol-network-column.ts @@ -0,0 +1,84 @@ +import { Logger } from '@graphprotocol/common-ts' +import { QueryTypes, QueryInterface } from 'sequelize' + +interface MigrationContext { + queryInterface: QueryInterface + logger: Logger +} + +interface Context { + context: MigrationContext +} + +const UNIQUE_INDEX_NAME = 'idx_unique_identifier_and_protocol_network' + +export async function up({ context }: Context): Promise { + const { queryInterface } = context + + const primaryKeyName = await getPrimaryKeyName(queryInterface) + await alterTable(primaryKeyName, queryInterface) +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export async function down({ context }: Context): Promise { + /* TODO + - Drop partial unique index on IndexingRules (identifier) + - Infer the name and drop the unique constraint from (identifier, protocolNetwork) + - Add NOT NULL back to protocolNetwork, possibly removing rows where it's NULL + - Infer the name and drop primary key on id + - Create a primary key on (identifier, protocolNetwork) + */ +} + +async function getPrimaryKeyName( + queryInterface: QueryInterface, +): Promise { + const sql = ` +SELECT + conname +FROM + pg_constraint + INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid + INNER JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace +WHERE + pg_class.relname = 'IndexingRules' + AND pg_namespace.nspname = 'public' + AND pg_constraint.contype = 'p'; +` + const result: null | { conname?: string } = + await queryInterface.sequelize.query(sql, { + type: QueryTypes.SELECT, + raw: true, + plain: true, + }) + + if (!result || !result.conname) { + throw new Error( + `Failed to infer primary key constraint name for the 'IndexingRules' table.`, + ) + } + return result.conname +} + +async function alterTable( + primaryKey: string, + queryInterface: QueryInterface, +): Promise { + const alterTableSql = ` +ALTER TABLE "IndexingRules" + DROP CONSTRAINT "${primaryKey}", + ADD PRIMARY KEY (id), + ALTER COLUMN "protocolNetwork" DROP NOT NULL, + ADD UNIQUE ("identifier", "protocolNetwork"); +` + + // We still need this partial index to enforce "identifier" uniqueness when + // "protocolNetwork" is null. + const createUniqueIndexSql = ` +CREATE UNIQUE INDEX ${UNIQUE_INDEX_NAME} ON "IndexingRules" ("identifier") +WHERE + "protocolNetwork" IS NULL; +` + await queryInterface.sequelize.query(alterTableSql) + await queryInterface.sequelize.query(createUniqueIndexSql) +} From 7631f2f522de38f0d61bf6eeb5fb69718fd3bba2 Mon Sep 17 00:00:00 2001 From: tilacog Date: Thu, 9 Nov 2023 15:23:15 -0300 Subject: [PATCH 15/16] common, agent: logging improvements --- packages/indexer-agent/src/agent.ts | 17 ++++++-- packages/indexer-agent/src/commands/start.ts | 13 +----- .../src/__tests__/grafting.test.ts | 20 +++++---- packages/indexer-common/src/grafting.ts | 43 +++++++++++++++++-- packages/indexer-common/src/graph-node.ts | 9 ++-- .../indexer-common/src/subgraph-deployment.ts | 18 +++++--- packages/indexer-common/src/subgraphs.ts | 2 +- 7 files changed, 83 insertions(+), 39 deletions(-) diff --git a/packages/indexer-agent/src/agent.ts b/packages/indexer-agent/src/agent.ts index b91801786..eec4b6aab 100644 --- a/packages/indexer-agent/src/agent.ts +++ b/packages/indexer-agent/src/agent.ts @@ -36,6 +36,7 @@ import { networkIsL2, networkIsL1, DeploymentManagementMode, + deploySubgraph, } from '@graphprotocol/indexer-common' import PQueue from 'p-queue' @@ -195,6 +196,7 @@ export class Agent { offchainSubgraphs: SubgraphDeploymentID[] autoMigrationSupport: boolean deploymentManagement: DeploymentManagementMode + ipfsURL: URL constructor( logger: Logger, @@ -206,6 +208,7 @@ export class Agent { offchainSubgraphs: SubgraphDeploymentID[], autoMigrationSupport: boolean, deploymentManagement: DeploymentManagementMode, + ipfsURL: URL, ) { this.logger = logger.child({ component: 'Agent' }) this.metrics = metrics @@ -215,6 +218,7 @@ export class Agent { this.offchainSubgraphs = offchainSubgraphs this.autoMigrationSupport = !!autoMigrationSupport this.deploymentManagement = deploymentManagement + this.ipfsURL = ipfsURL } async start(): Promise { @@ -986,10 +990,15 @@ export class Agent { deployment: deployment.display, }) - // Ensure the deployment is deployed to the indexer - // Note: we're not waiting here, as sometimes indexing a subgraph - // will block if the IPFS files cannot be retrieved - await this.graphNode.ensure(name, deployment) + await deploySubgraph( + name, + deployment, + this.graphNode, + this.ipfsURL, + this.indexerManagement, + 'eip155:5', + this.logger, + ) }), ) diff --git a/packages/indexer-agent/src/commands/start.ts b/packages/indexer-agent/src/commands/start.ts index f701a52b8..e47f8d216 100644 --- a/packages/indexer-agent/src/commands/start.ts +++ b/packages/indexer-agent/src/commands/start.ts @@ -226,18 +226,6 @@ export const start = { default: 100, group: 'Query Fees', }) - .option('ipfs-endpoint', { - description: `Endpoint to an ipfs node to quickly query subgraph manifest data`, - type: 'string', - default: 'https://ipfs.network.thegraph.com', - group: 'Indexer Infrastructure', - }) - .option('auto-graft-resolver-limit', { - description: `Maximum depth of grafting dependency to automatically resolve`, - type: 'number', - default: 0, - group: 'Indexer Infrastructure', - }) .option('inject-dai', { description: 'Inject the GRT to DAI/USDC conversion rate into cost model variables', @@ -648,6 +636,7 @@ export async function run( argv.offchainSubgraphs.map((s: string) => new SubgraphDeploymentID(s)), argv.enableAutoMigrationSupport, argv.deploymentManagement, + argv.ipfsEndpoint, ) await agent.start() } diff --git a/packages/indexer-common/src/__tests__/grafting.test.ts b/packages/indexer-common/src/__tests__/grafting.test.ts index baa007bb0..5f226c61a 100644 --- a/packages/indexer-common/src/__tests__/grafting.test.ts +++ b/packages/indexer-common/src/__tests__/grafting.test.ts @@ -6,11 +6,13 @@ import { } from '../grafting' import { SubgraphDeploymentID } from '@graphprotocol/common-ts' import { IndexerErrorCode } from '../errors' -import { SubgraphDeploymentDecisionKind } from '../types' +import { LoggerInterface, SubgraphDeploymentDecisionKind } from '../types' // Create a mock for the fetchSubgraphManifest function const fakeSubgraphManifestResolver = jest.fn() +const fakeLogger = jest.fn() as unknown as LoggerInterface + // Fake IPFS Hashes: const target = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrZX' const base1 = 'QmWaVSK24D1m53Ej2PaddWcb1HZKAV4bjiKkrUwtP3HrYj' @@ -93,7 +95,7 @@ describe('determineSubgraphDeploymentDecisions function', () => { let threwError = false try { - determineSubgraphDeploymentDecisions(subgraphLineage) + determineSubgraphDeploymentDecisions(subgraphLineage, fakeLogger) } catch (err) { expect(err.code).toStrictEqual(IndexerErrorCode.IE075) expect(err.cause).toStrictEqual( @@ -122,7 +124,7 @@ describe('determineSubgraphDeploymentDecisions function', () => { }, ], } - expect(determineSubgraphDeploymentDecisions(subgraphLineage)).toEqual([]) + expect(determineSubgraphDeploymentDecisions(subgraphLineage, fakeLogger)).toEqual([]) }) test('should throw an error if an unsynced base is unhealthy', () => { @@ -146,7 +148,7 @@ describe('determineSubgraphDeploymentDecisions function', () => { let threwError = false try { - determineSubgraphDeploymentDecisions(subgraphLineage) + determineSubgraphDeploymentDecisions(subgraphLineage, fakeLogger) } catch (err) { expect(err.code).toStrictEqual(IndexerErrorCode.IE075) expect(err.cause).toStrictEqual({ @@ -169,7 +171,7 @@ describe('determineSubgraphDeploymentDecisions function', () => { }, ], } - const decisions = determineSubgraphDeploymentDecisions(subgraphLineage) + const decisions = determineSubgraphDeploymentDecisions(subgraphLineage, fakeLogger) const expected = [ { deployment: new SubgraphDeploymentID(base1), @@ -196,7 +198,7 @@ describe('determineSubgraphDeploymentDecisions function', () => { }, ], } - const decisions = determineSubgraphDeploymentDecisions(subgraphLineage) + const decisions = determineSubgraphDeploymentDecisions(subgraphLineage, fakeLogger) const expected = [ { deployment: new SubgraphDeploymentID(base2), @@ -224,7 +226,7 @@ describe('determineSubgraphDeploymentDecisions function', () => { }, ], } - const decisions = determineSubgraphDeploymentDecisions(subgraphLineage) + const decisions = determineSubgraphDeploymentDecisions(subgraphLineage, fakeLogger) const expected = [ { deployment: new SubgraphDeploymentID(base1), @@ -268,7 +270,7 @@ describe('determineSubgraphDeploymentDecisions function', () => { }, ], } - const decisions = determineSubgraphDeploymentDecisions(subgraphLineage) + const decisions = determineSubgraphDeploymentDecisions(subgraphLineage, fakeLogger) const expected = [ { deployment: new SubgraphDeploymentID(base3), @@ -332,7 +334,7 @@ describe('determineSubgraphDeploymentDecisions function', () => { }, ], } - const decisions = determineSubgraphDeploymentDecisions(subgraphLineage) + const decisions = determineSubgraphDeploymentDecisions(subgraphLineage, fakeLogger) const expected = [ { deployment: new SubgraphDeploymentID(base4), diff --git a/packages/indexer-common/src/grafting.ts b/packages/indexer-common/src/grafting.ts index 67e763a6c..50e673cec 100644 --- a/packages/indexer-common/src/grafting.ts +++ b/packages/indexer-common/src/grafting.ts @@ -120,9 +120,15 @@ export async function getIndexingStatus( export function determineSubgraphDeploymentDecisions( subgraphLineage: SubgraphLineageWithStatus, + parentLogger: LoggerInterface, ): GraftBaseDeploymentDecision[] { const deploymentDecisions: GraftBaseDeploymentDecision[] = [] + const logger = parentLogger.child({ + function: 'determineSubgraphDeploymentDecisions', + subgraphLineage: formatLineage(subgraphLineage), + }) + // Check lineage size before making any assumptions. if (!subgraphLineage.bases.length) { throw indexerError( @@ -143,10 +149,17 @@ export function determineSubgraphDeploymentDecisions( for (let i = lastIndex; i >= 0; i--) { const graft = subgraphLineage.bases[i] const desiredBlockHeight = graft.block + const traceLogger = logger.child({ + graft: formatDeploymentContainer(graft), + earliestValidBaseIndex, + graftIndex: i, + }) + traceLogger.trace('Inspecting graft base deployment status') if (!graft.indexingStatus || !graft.indexingStatus.latestBlock) { // Ignore undeployed graft bases beyond the earliest valid one. if (i <= earliestValidBaseIndex) { // Graph Node is not aware of this subgraph deployment. We must deploy it and look no further. + traceLogger.trace('Deploy candidate found') deploymentDecisions.push({ deployment: graft.deployment, kind: SubgraphDeploymentDecisionKind.DEPLOY, @@ -157,7 +170,9 @@ export function determineSubgraphDeploymentDecisions( } else { // Deployment exists. // Is it sufficiently synced? + traceLogger.trace('Deployment exists') if (graft.indexingStatus.latestBlock.number >= desiredBlockHeight) { + traceLogger.trace('Deployment is sufficiently synced') // If so, we can stop syncing it. deploymentDecisions.push({ deployment: graft.deployment, @@ -187,9 +202,6 @@ export async function queryGraftBaseStatuses( concurrency: number = 5, ): Promise { const logger = parentLogger.child({ function: 'queryGraftBaseStatuses' }) - logger.debug('Attempting to resolve graft bases for target subgraph') - - // Fetch deployment details for each graft base logger.debug('Querying Graph-Node for graft bases deployment status') const graftBasesDeploymentStatus = await pMap( subgraphLineage.bases, @@ -199,9 +211,34 @@ export async function queryGraftBaseStatuses( concurrency, }, ) + logger.trace('Got graft bases deployment statuses from Graph-Node', { + graftBasesDeploymentStatus: formatDeploymentContainers(graftBasesDeploymentStatus), + }) return { target: subgraphLineage.target, bases: graftBasesDeploymentStatus, } } + +// Logging utilities + +export function formatDeploymentContainer(base: { deployment: SubgraphDeploymentID }) { + return { + ...base, + deployment: base.deployment.display, + } +} + +export function formatDeploymentContainers( + bases: { deployment: SubgraphDeploymentID }[], +): any { + return bases.map(formatDeploymentContainer) +} + +export function formatLineage(lineage: SubgraphLineageWithStatus): any { + return { + target: lineage.target.display, + bases: formatDeploymentContainers(lineage.bases), + } +} diff --git a/packages/indexer-common/src/graph-node.ts b/packages/indexer-common/src/graph-node.ts index 946d6bb72..29ca2c41d 100644 --- a/packages/indexer-common/src/graph-node.ts +++ b/packages/indexer-common/src/graph-node.ts @@ -189,7 +189,7 @@ export class GraphNode implements GraphNodeInterface { async indexNodes(): Promise { try { - this.logger.trace(`Querying indexing statuses`) + this.logger.trace(`Querying index nodes`) const result = await this.status .query(gql` { @@ -205,7 +205,7 @@ export class GraphNode implements GraphNodeInterface { throw result.error } - this.logger.trace(`Queried indexing statuses`, { + this.logger.trace(`Queried index nodes`, { data: result.data, }) @@ -461,7 +461,6 @@ export class GraphNode implements GraphNodeInterface { } catch (error) { throw indexerError(IndexerErrorCode.IE018, error) } - return ( result.data.indexingStatuses // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -473,7 +472,7 @@ export class GraphNode implements GraphNodeInterface { } try { - return await pRetry(queryIndexingStatuses, { + const indexingStatuses = await pRetry(queryIndexingStatuses, { retries: 5, maxTimeout: 10000, onFailedAttempt: (err) => { @@ -485,6 +484,8 @@ export class GraphNode implements GraphNodeInterface { }) }, } as pRetry.Options) + this.logger.trace('Queried indexing statuses', indexingStatuses) + return indexingStatuses } catch (error) { const err = indexerError(IndexerErrorCode.IE018, error) this.logger.error(`Failed to query indexing status API`, { diff --git a/packages/indexer-common/src/subgraph-deployment.ts b/packages/indexer-common/src/subgraph-deployment.ts index 3b03e4e13..236d3aded 100644 --- a/packages/indexer-common/src/subgraph-deployment.ts +++ b/packages/indexer-common/src/subgraph-deployment.ts @@ -8,6 +8,8 @@ import { SubgraphLineage, queryGraftBaseStatuses, GraftBaseDeploymentDecision, + formatGraftBases, + formatGraftBase, } from './grafting' import { SubgraphIdentifierType, fetchSubgraphManifest } from './subgraphs' import { @@ -55,9 +57,9 @@ export async function deploySubgraph( // Inspect target subgraph's grafting lineage. const logger = parentLogger.child({ function: 'deploySubgraph', - subgraphDeployment, + subgraphDeployment: subgraphDeployment.display, }) - logger.debug('Attempting to resolve graft bases for target subgraph.') + logger.debug('Resolving graft bases for target subgraph deployment.') const subgraphManifestResolver = async (subgraphID: SubgraphDeploymentID) => await fetchSubgraphManifest(ipfsURL, subgraphID, logger) const subgraphLineage = await discoverLineage( @@ -90,10 +92,13 @@ async function deployGraftedSubgraph( ): Promise { const logger = parentLogger.child({ function: 'deployGraftedSubgraph', - targetSubgraph: subgraphLineage.target.ipfsHash, - graftBases: subgraphLineage.bases, + targetSubgraph: subgraphLineage.target.display, + graftBases: formatGraftBases(subgraphLineage.bases), }) - logger.debug('Attempting to deploy first viable base for grafted subgraph.') + logger.debug( + 'Target subgraph deployment has graft bases. ' + + 'Deploying first viable base for grafted subgraph.', + ) // Fetch the deployment status for all graft bases. const lineageDeploymentStatus = await queryGraftBaseStatuses( @@ -105,6 +110,7 @@ async function deployGraftedSubgraph( // Inspect if we need to deploy or remove a sufficiently synced graft base. const deploymentDecisions = determineSubgraphDeploymentDecisions( lineageDeploymentStatus, + logger, ) for (const deploymentDecision of deploymentDecisions) { switch (deploymentDecision.kind) { @@ -193,7 +199,7 @@ async function deleteTemporaryIndexingRule( } const logger = parentLogger.child({ identifier, - deploymentDecision, + deploymentDecision: formatGraftBase(deploymentDecision), }) // Query indexing management client for a indexing rule matching the IPFS hash of this diff --git a/packages/indexer-common/src/subgraphs.ts b/packages/indexer-common/src/subgraphs.ts index e01e207dd..e8420503f 100644 --- a/packages/indexer-common/src/subgraphs.ts +++ b/packages/indexer-common/src/subgraphs.ts @@ -536,7 +536,7 @@ export async function fetchSubgraphManifest( const logger = parentLogger.child({ function: 'fetchSubgraphManifest', - subgraphDeployment: SubgraphDeploymentID, + targetDeployment: targetDeployment.display, subgraphManifestSearchURL, }) From ae4a62d6b5d226928b2ebe350f370bb2399d87c0 Mon Sep 17 00:00:00 2001 From: tilacog Date: Thu, 9 Nov 2023 16:26:16 -0300 Subject: [PATCH 16/16] agent: delete migration This migration works, but a bigger effort to adapt the whole system to this new concept is still required. --- ...constraint-from-protocol-network-column.ts | 84 ------------------- 1 file changed, 84 deletions(-) delete mode 100644 packages/indexer-agent/src/db/migrations/12-remove-not-null-constraint-from-protocol-network-column.ts diff --git a/packages/indexer-agent/src/db/migrations/12-remove-not-null-constraint-from-protocol-network-column.ts b/packages/indexer-agent/src/db/migrations/12-remove-not-null-constraint-from-protocol-network-column.ts deleted file mode 100644 index 920ed7211..000000000 --- a/packages/indexer-agent/src/db/migrations/12-remove-not-null-constraint-from-protocol-network-column.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Logger } from '@graphprotocol/common-ts' -import { QueryTypes, QueryInterface } from 'sequelize' - -interface MigrationContext { - queryInterface: QueryInterface - logger: Logger -} - -interface Context { - context: MigrationContext -} - -const UNIQUE_INDEX_NAME = 'idx_unique_identifier_and_protocol_network' - -export async function up({ context }: Context): Promise { - const { queryInterface } = context - - const primaryKeyName = await getPrimaryKeyName(queryInterface) - await alterTable(primaryKeyName, queryInterface) -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export async function down({ context }: Context): Promise { - /* TODO - - Drop partial unique index on IndexingRules (identifier) - - Infer the name and drop the unique constraint from (identifier, protocolNetwork) - - Add NOT NULL back to protocolNetwork, possibly removing rows where it's NULL - - Infer the name and drop primary key on id - - Create a primary key on (identifier, protocolNetwork) - */ -} - -async function getPrimaryKeyName( - queryInterface: QueryInterface, -): Promise { - const sql = ` -SELECT - conname -FROM - pg_constraint - INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid - INNER JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace -WHERE - pg_class.relname = 'IndexingRules' - AND pg_namespace.nspname = 'public' - AND pg_constraint.contype = 'p'; -` - const result: null | { conname?: string } = - await queryInterface.sequelize.query(sql, { - type: QueryTypes.SELECT, - raw: true, - plain: true, - }) - - if (!result || !result.conname) { - throw new Error( - `Failed to infer primary key constraint name for the 'IndexingRules' table.`, - ) - } - return result.conname -} - -async function alterTable( - primaryKey: string, - queryInterface: QueryInterface, -): Promise { - const alterTableSql = ` -ALTER TABLE "IndexingRules" - DROP CONSTRAINT "${primaryKey}", - ADD PRIMARY KEY (id), - ALTER COLUMN "protocolNetwork" DROP NOT NULL, - ADD UNIQUE ("identifier", "protocolNetwork"); -` - - // We still need this partial index to enforce "identifier" uniqueness when - // "protocolNetwork" is null. - const createUniqueIndexSql = ` -CREATE UNIQUE INDEX ${UNIQUE_INDEX_NAME} ON "IndexingRules" ("identifier") -WHERE - "protocolNetwork" IS NULL; -` - await queryInterface.sequelize.query(alterTableSql) - await queryInterface.sequelize.query(createUniqueIndexSql) -}