From 9d0b66e45b553175c6daa24e8350d849da0fb9f7 Mon Sep 17 00:00:00 2001 From: Nikolay Vlasov Date: Fri, 19 Jan 2024 15:42:17 +1100 Subject: [PATCH 01/22] Website. Bug fixes and addition of gtag manager --- website/docusaurus.config.js | 6 ++++++ website/package-lock.json | 1 + website/package.json | 1 + website/src/components/HomepageFeatures/index.js | 14 +++++++------- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 838c605b..cb4c728c 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -60,6 +60,12 @@ const config = { anonymizeIP: true, }, ], + [ + '@docusaurus/plugin-google-tag-manager', + { + containerId: 'GTM-WQ97QTTR', + }, + ], ], themeConfig: diff --git a/website/package-lock.json b/website/package-lock.json index bfd5f878..e1986ca3 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@docusaurus/core": "^3.1.0", "@docusaurus/plugin-google-gtag": "^3.1.0", + "@docusaurus/plugin-google-tag-manager": "^3.1.0", "@docusaurus/preset-classic": "^3.1.0", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", diff --git a/website/package.json b/website/package.json index f7530b5b..f29ab31d 100644 --- a/website/package.json +++ b/website/package.json @@ -16,6 +16,7 @@ "dependencies": { "@docusaurus/core": "^3.1.0", "@docusaurus/plugin-google-gtag": "^3.1.0", + "@docusaurus/plugin-google-tag-manager": "^3.1.0", "@docusaurus/preset-classic": "^3.1.0", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", diff --git a/website/src/components/HomepageFeatures/index.js b/website/src/components/HomepageFeatures/index.js index dece511b..2ad9f11e 100644 --- a/website/src/components/HomepageFeatures/index.js +++ b/website/src/components/HomepageFeatures/index.js @@ -7,27 +7,27 @@ const FeatureList = [ title: 'Open Source', Icon: "⚀", description: ( - <> - Deployment code is fully open source and available on GitHub. You can see what you are going to deploy before deploying it. - + + Deployment code is fully open source and available on GitHub. You can see what you are going to deploy before deploying it. + ), }, { title: 'Scalable', Icon: "⚁", description: ( -
+ Best Practice Blockchain Nodes Deployment Templates and Examples to run across Regions. -
+ ), }, { title: 'Highly Available', Icon: "⚂", description: ( -
+ Multi-Node Highly Available deployment options to run across Availability Zones. -
+ ), }, ]; From 73241d1c23ff3393afb7a372a678a2f8146c960b Mon Sep 17 00:00:00 2001 From: Nikolay Vlasov Date: Tue, 23 Jan 2024 12:46:17 +1100 Subject: [PATCH 02/22] Ethereum single node deployemnt option for issue #19 --- lib/constructs/single-node.ts | 32 +++- lib/ethereum/README.md | 54 +++++- lib/ethereum/app.ts | 18 +- .../docker-compose-besu-teku.yml | 4 +- .../docker-compose-erigon-lighthouse.yml | 4 +- .../docker-compose-erigon-prysm.yml | 4 +- .../docker-compose-geth-lighthouse.yml | 4 +- .../docker-compose-nethermind-teku.yml | 4 +- lib/ethereum/lib/assets/user-data/node.sh | 14 +- lib/ethereum/lib/common-stack.ts | 3 +- .../lib/config/ethConfig.interface.ts | 2 + lib/ethereum/lib/rpc-nodes-stack.ts | 10 +- ...ync-node-stack.ts => single-node-stack.ts} | 16 +- lib/ethereum/test/rpc-nodes-stack.test.ts | 2 + lib/ethereum/test/solo-node-stack.test.ts | 163 ++++++++++++++++++ lib/ethereum/test/sync-node-stack.test.ts | 8 +- lib/ethereum/tsconfig.json | 31 ++++ package-lock.json | 27 ++- package.json | 2 +- 19 files changed, 341 insertions(+), 61 deletions(-) rename lib/ethereum/lib/{sync-node-stack.ts => single-node-stack.ts} (91%) create mode 100644 lib/ethereum/test/solo-node-stack.test.ts create mode 100644 lib/ethereum/tsconfig.json diff --git a/lib/constructs/single-node.ts b/lib/constructs/single-node.ts index a87369b5..bfb01a80 100644 --- a/lib/constructs/single-node.ts +++ b/lib/constructs/single-node.ts @@ -74,15 +74,29 @@ export class SingleNodeConstruct extends cdkContructs.Construct { throw new Error(`Number of data volumes can't be more than 6, current number: ${dataVolumeIndex}`); } if (dataVolume.type !== constants.InstanceStoreageDeviceVolumeType) { - const newDataVolume = new ec2.Volume(this, `data-volume-${dataVolumeIndex}`, { - availabilityZone: availabilityZone, - size: cdk.Size.gibibytes(dataVolume.sizeGiB), - volumeType: ec2.EbsDeviceVolumeType[dataVolume.type.toUpperCase() as keyof typeof ec2.EbsDeviceVolumeType], - encrypted: true, - iops: dataVolume.iops, - throughput: dataVolume.throughput, - removalPolicy: cdk.RemovalPolicy.DESTROY, - }); + let newDataVolume: ec2.Volume; + + if (dataVolume.type === ec2.EbsDeviceVolumeType.GP3) { + newDataVolume = new ec2.Volume(this, `data-volume-${dataVolumeIndex}`, { + availabilityZone: availabilityZone, + size: cdk.Size.gibibytes(dataVolume.sizeGiB), + volumeType: ec2.EbsDeviceVolumeType[dataVolume.type.toUpperCase() as keyof typeof ec2.EbsDeviceVolumeType], + encrypted: true, + iops: dataVolume.iops, + throughput: dataVolume.throughput, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + } else { + newDataVolume = new ec2.Volume(this, `data-volume-${dataVolumeIndex}`, { + availabilityZone: availabilityZone, + size: cdk.Size.gibibytes(dataVolume.sizeGiB), + volumeType: ec2.EbsDeviceVolumeType[dataVolume.type.toUpperCase() as keyof typeof ec2.EbsDeviceVolumeType], + encrypted: true, + iops: dataVolume.iops, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + } + new ec2.CfnVolumeAttachment(this, `data-volume${dataVolumeIndex}-attachment`, { // Device naming according to https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html diff --git a/lib/ethereum/README.md b/lib/ethereum/README.md index 1cdc4412..2a31911e 100644 --- a/lib/ethereum/README.md +++ b/lib/ethereum/README.md @@ -63,7 +63,7 @@ We will use AWS Cloud9 to execute the subsequent commands. Follow the instructio **NOTE:** In this tutorial we will set all major configuration through environment variables, but you also can modify parameters in `config/config.ts`. -### Deploy Sync Node +### Prepare to deploy nodes 1. Make sure you are in the root directory of the cloned repository @@ -75,6 +75,8 @@ We will use AWS Cloud9 to execute the subsequent commands. Follow the instructio **NOTE:** You may see the following error if the default VPC already exists: `An error occurred (DefaultVpcAlreadyExists) when calling the CreateDefaultVpc operation: A Default VPC already exists for this account in this region.`. That means you can just continue with the following steps. + **NOTE:** The default VPC must have at least two public subnets in different Availability Zones, and public subnet must set `Auto-assign public IPv4 address` to `YES` + 3. Configure your setup Create your own copy of `.env` file and edit it: @@ -96,7 +98,42 @@ Create your own copy of `.env` file and edit it: npx cdk deploy eth-common ``` -5. Deploy Sync Node +### Option 1: Single RPC Node + +1. Deploy Single RPC Node + +```bash + pwd + # Make sure you are in aws-blockchain-node-runners/lib/ethereum + npx cdk deploy eth-single-node --json --outputs-file single-node-deploy.json +``` + **NOTE:** The default VPC must have at least two public subnets in different Availability Zones, and public subnet must set `Auto-assign public IPv4 address` to `YES` + +2. After starting the node you need to wait for the inital syncronization process to finish. It may take from half a day to about 6-10 days depending on the client combination and the state of the network. You can use Amazon CloudWatch to track the progress. There is a script that publishes CloudWatch metrics every 5 minutes, where you can watch `sync distance` for consensus client and `blocks behind` for execution client. When the node is fully synced those two metrics shold show 0. To see them: + + - Navigate to [CloudWatch service](https://console.aws.amazon.com/cloudwatch/) (make sure you are in the region you have specified for `AWS_REGION`) + - Open `Dashboards` and select `eth-sync-node-` from the list of dashboards. + +4. Once the initial synchronization is done, you should be able to access the RPC API of that node from within the same VPC. The RPC port is not exposed to the Internet. Tun the following query against the private IP of the single RPC node you deployed: + +```bash + INSTANCE_ID=$(cat single-node-deploy.json | jq -r '..|.node-instance-id? | select(. != null)') + NODE_INTERNAL_IP=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID --query 'Reservations[*].Instances[*].PrivateIpAddress' --output text) + + # We query token balance of Beacon deposit contract: https://etherscan.io/address/0x00000000219ab540356cbb839cbe05303d7705fa + curl http://$NODE_INTERNAL_IP:8545 -X POST -H "Content-Type: application/json" \ + --data '{"method":"eth_getBalance","params":["0x00000000219ab540356cBB839Cbe05303d7705Fa", "latest"],"id":1,"jsonrpc":"2.0"}' +``` + +The result should be like this (the actual balance might change): + +```javascript + {"jsonrpc":"2.0","id":1,"result":"0xe791d050f91d9949d344d"} +``` + +### Option 2: Deploy the Highly Available RPC Nodes + +1. Deploy Sync Node ```bash pwd @@ -105,7 +142,7 @@ Create your own copy of `.env` file and edit it: ``` **NOTE:** The default VPC must have at least two public subnets in different Availability Zones, and public subnet must set `Auto-assign public IPv4 address` to `YES` -6. After starting the node you need to wait for the inital syncronization process to finish. It may take from half a day to about 6-10 days depending on the client combination and the state of the network. You can use Amazon CloudWatch to track the progress. There is a script that publishes CloudWatch metrics every 5 minutes, where you can watch `sync distance` for consensus client and `blocks behind` for execution client. When the node is fully synced those two metrics shold show 0. To see them: +2. After starting the node you need to wait for the inital syncronization process to finish. It may take from half a day to about 6-10 days depending on the client combination and the state of the network. You can use Amazon CloudWatch to track the progress. There is a script that publishes CloudWatch metrics every 5 minutes, where you can watch `sync distance` for consensus client and `blocks behind` for execution client. When the node is fully synced those two metrics shold show 0. To see them: - Navigate to [CloudWatch service](https://console.aws.amazon.com/cloudwatch/) (make sure you are in the region you have specified for `AWS_REGION`) - Open `Dashboards` and select `eth-sync-node-` from the list of dashboards. @@ -114,9 +151,7 @@ Once synchronization process is over, the script will automatically stop both cl Note: the snapshot backup process will automatically run ever day at midnight time of the time zone were the sync node runs. To change the schedule, modify `crontab` of the root user on the node's EC2 instance. -### Deploy the RPC Nodes - -1. Configure and deploy 2 RPC Nodes +3. Configure and deploy 2 RPC Nodes ```bash pwd @@ -124,7 +159,7 @@ Note: the snapshot backup process will automatically run ever day at midnight ti npx cdk deploy eth-rpc-nodes --json --outputs-file rpc-node-deploy.json ``` -2. Give the new RPC nodes about 30 minutes (up to 2 hours for Erigon) to initialize and then run the following query against the load balancer behind the RPC node created +4. Give the new RPC nodes about 30 minutes (up to 2 hours for Erigon) to initialize and then run the following query against the load balancer behind the RPC node created ```bash export ETH_RPC_ABL_URL=$(cat rpc-node-deploy.json | jq -r '..|.alburl? | select(. != null)') @@ -151,7 +186,7 @@ The result should be like this (the actual balance might change): ``` -**NOTE:** By default and for security reasons the load balancer is available only from wihtin the default VPC in the region where it is deployed. It is not available from the Internet and is not open for external connections. Before opening it up please make sure you protect your RPC APIs. +**NOTE:** By default and for security reasons the load balancer is available only from within the default VPC in the region where it is deployed. It is not available from the Internet and is not open for external connections. Before opening it up please make sure you protect your RPC APIs. ### Clearing up and undeploying everything @@ -165,6 +200,9 @@ The result should be like this (the actual balance might change): pwd # Make sure you are in aws-blockchain-node-runners/lib/ethereum + # Undeploy Single RPC Node + cdk destroy eth-single-node + # Undeploy RPC Nodes cdk destroy eth-rpc-nodes diff --git a/lib/ethereum/app.ts b/lib/ethereum/app.ts index 3ac03fff..1197d359 100644 --- a/lib/ethereum/app.ts +++ b/lib/ethereum/app.ts @@ -4,8 +4,9 @@ import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import * as nag from "cdk-nag"; import * as config from "./lib/config/ethConfig"; +import { EthNodeRole } from "./lib/config/ethConfig.interface"; -import { EthSyncNodeStack } from "./lib/sync-node-stack"; +import { EthSingleNodeStack } from "./lib/single-node-stack"; import { EthCommonStack } from "./lib/common-stack"; import { EthRpcNodesStack } from "./lib/rpc-nodes-stack"; @@ -17,11 +18,23 @@ new EthCommonStack(app, "eth-common", { stackName: `eth-nodes-common`, }); -new EthSyncNodeStack(app, "eth-sync-node", { +new EthSingleNodeStack(app, "eth-sync-node", { stackName: `eth-sync-node-${config.baseConfig.clientCombination}`, env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, ethClientCombination: config.baseConfig.clientCombination, + nodeRole: "sync-node", + instanceType: config.syncNodeConfig.instanceType, + instanceCpuType: config.syncNodeConfig.instanceCpuType, + dataVolumes: config.syncNodeConfig.dataVolumes, +}); + +new EthSingleNodeStack(app, "eth-single-node", { + stackName: `eth-single-node-${config.baseConfig.clientCombination}`, + + env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, + ethClientCombination: config.baseConfig.clientCombination, + nodeRole: "single-node", instanceType: config.syncNodeConfig.instanceType, instanceCpuType: config.syncNodeConfig.instanceCpuType, dataVolumes: config.syncNodeConfig.dataVolumes, @@ -32,6 +45,7 @@ new EthRpcNodesStack(app, "eth-rpc-nodes", { env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, ethClientCombination: config.baseConfig.clientCombination, + nodeRole: "rpc-node", instanceType: config.rpcNodeConfig.instanceType, instanceCpuType: config.rpcNodeConfig.instanceCpuType, numberOfNodes: config.rpcNodeConfig.numberOfNodes, diff --git a/lib/ethereum/lib/assets/docker-compose/docker-compose-besu-teku.yml b/lib/ethereum/lib/assets/docker-compose/docker-compose-besu-teku.yml index aa32274f..3eddedd6 100644 --- a/lib/ethereum/lib/assets/docker-compose/docker-compose-besu-teku.yml +++ b/lib/ethereum/lib/assets/docker-compose/docker-compose-besu-teku.yml @@ -4,7 +4,7 @@ services: besu_node: environment: - "JAVA_OPTS=-Xmx8g" - image: hyperledger/besu:23.4.5-SNAPSHOT + image: hyperledger/besu:24.1.2-SNAPSHOT container_name: execution restart: always command: @@ -43,7 +43,7 @@ services: environment: - "JAVA_OPTS=-Xmx4g" - "TEKU_OPTS=-XX:-HeapDumpOnOutOfMemoryError" - image: consensys/teku:23.6.1-jdk16 + image: consensys/teku:24.1.0-jdk17 container_name: consensus restart: always command: diff --git a/lib/ethereum/lib/assets/docker-compose/docker-compose-erigon-lighthouse.yml b/lib/ethereum/lib/assets/docker-compose/docker-compose-erigon-lighthouse.yml index 476860df..2878c6be 100644 --- a/lib/ethereum/lib/assets/docker-compose/docker-compose-erigon-lighthouse.yml +++ b/lib/ethereum/lib/assets/docker-compose/docker-compose-erigon-lighthouse.yml @@ -2,7 +2,7 @@ version: "3" services: erigon_node: - image: thorax/erigon:2.48.1-__IMAGE_ARCH__ + image: thorax/erigon:2.57.0-__IMAGE_ARCH__ container_name: execution restart: always command: @@ -43,7 +43,7 @@ services: - "30303:30303/udp" lighthouse_node: - image: sigp/lighthouse:v4.3.0 + image: sigp/lighthouse:v4.5.0 container_name: consensus restart: always command: diff --git a/lib/ethereum/lib/assets/docker-compose/docker-compose-erigon-prysm.yml b/lib/ethereum/lib/assets/docker-compose/docker-compose-erigon-prysm.yml index bcee3833..0409e766 100644 --- a/lib/ethereum/lib/assets/docker-compose/docker-compose-erigon-prysm.yml +++ b/lib/ethereum/lib/assets/docker-compose/docker-compose-erigon-prysm.yml @@ -2,7 +2,7 @@ version: "3" services: erigon_node: - image: thorax/erigon:2.48.1-__IMAGE_ARCH__ + image: thorax/erigon:2.57.0-__IMAGE_ARCH__ container_name: execution restart: always command: @@ -43,7 +43,7 @@ services: - "30303:30303/udp" prysm_node: - image: rocketpool/prysm:v4.0.6 + image: rocketpool/prysm:v4.2.0 container_name: consensus restart: always command: diff --git a/lib/ethereum/lib/assets/docker-compose/docker-compose-geth-lighthouse.yml b/lib/ethereum/lib/assets/docker-compose/docker-compose-geth-lighthouse.yml index 5eedfa4b..6a1ffc53 100644 --- a/lib/ethereum/lib/assets/docker-compose/docker-compose-geth-lighthouse.yml +++ b/lib/ethereum/lib/assets/docker-compose/docker-compose-geth-lighthouse.yml @@ -2,7 +2,7 @@ version: "3" services: geth_node: - image: ethereum/client-go:v1.11.6 + image: ethereum/client-go:v1.13.10 container_name: execution restart: always command: @@ -38,7 +38,7 @@ services: - "30303:30303/udp" lighthouse_node: - image: sigp/lighthouse:v4.3.0 + image: sigp/lighthouse:v4.5.0 container_name: consensus restart: always command: diff --git a/lib/ethereum/lib/assets/docker-compose/docker-compose-nethermind-teku.yml b/lib/ethereum/lib/assets/docker-compose/docker-compose-nethermind-teku.yml index 0adf6151..a7bbc6eb 100644 --- a/lib/ethereum/lib/assets/docker-compose/docker-compose-nethermind-teku.yml +++ b/lib/ethereum/lib/assets/docker-compose/docker-compose-nethermind-teku.yml @@ -4,7 +4,7 @@ services: nethermind_node: environment: - "DOTNET_BUNDLE_EXTRACT_BASE_DIR=/var/lib/nethermind/data" - image: nethermind/nethermind:1.19.3 + image: nethermind/nethermind:1.25.2 container_name: execution restart: always command: [ @@ -54,7 +54,7 @@ services: environment: - "JAVA_OPTS=-Xmx4g" - "TEKU_OPTS=-XX:-HeapDumpOnOutOfMemoryError" - image: consensys/teku:23.6.1-jdk16 + image: consensys/teku:24.1.0-jdk17 container_name: consensus restart: always command: diff --git a/lib/ethereum/lib/assets/user-data/node.sh b/lib/ethereum/lib/assets/user-data/node.sh index 6b653371..d41dc133 100644 --- a/lib/ethereum/lib/assets/user-data/node.sh +++ b/lib/ethereum/lib/assets/user-data/node.sh @@ -193,18 +193,18 @@ if [ "$NODE_ROLE" == "sync-node" ]; then chmod 766 /opt/copy-data-to-s3.sh fi -if [ "$NODE_ROLE" == "sync-node" ]; then - echo "Sync node. Signaling completion to CloudFormation" +if [ "$NODE_ROLE" == "sync-node" ] || [ "$NODE_ROLE" == "single-node" ]; then + echo "Single node. Signaling completion to CloudFormation" /opt/aws/bin/cfn-signal --stack $STACK_NAME --resource $RESOURCE_ID --region $REGION fi echo "Preparing data volume" -if [ "$NODE_ROLE" == "sync-node" ]; then +if [ "$NODE_ROLE" == "sync-node" ] || [ "$NODE_ROLE" == "single-node" ]; then # echo "Sync node. Wait for the volume to be attached" # aws ec2 wait volume-available --volume-ids $DATA_VOLUME_ID --region $REGION - echo "Sync node. Wait for one minute for the volume to be available" + echo "Single node. Wait for one minute for the volume to be available" sleep 60 fi @@ -242,11 +242,11 @@ mkdir -p /data/consensus/data chown -R ethereum:ethereum /data chmod -R 755 /data -if [ "$NODE_ROLE" == "sync-node" ]; then +if [ "$NODE_ROLE" == "sync-node" ] || [ "$NODE_ROLE" == "single-node" ]; then if [ "$AUTOSTART_CONTAINER" == "false" ]; then - echo "Sync node. Autostart disabled. Start docker-compose manually!" + echo "Single node. Autostart disabled. Start docker-compose manually!" else - echo "Sync node. Autostart enabled. Starting docker-compose in 3 min." + echo "Single node. Autostart enabled. Starting docker-compose in 3 min." echo "sudo su ethereum && /usr/local/bin/docker-compose -f /home/ethereum/docker-compose.yml up -d" | at now +3 minutes fi fi diff --git a/lib/ethereum/lib/common-stack.ts b/lib/ethereum/lib/common-stack.ts index cc4c5ca0..4116d524 100644 --- a/lib/ethereum/lib/common-stack.ts +++ b/lib/ethereum/lib/common-stack.ts @@ -12,6 +12,7 @@ export interface EthCommonStackProps extends cdk.StackProps {} export class EthCommonStack extends cdk.Stack { AWS_STACKNAME = cdk.Stack.of(this).stackName; AWS_ACCOUNT_ID = cdk.Stack.of(this).account; + AWS_REGION = cdk.Stack.of(this).region constructor(scope: cdkConstructs.Construct, id: string, props: EthCommonStackProps) { super(scope, id, props); @@ -23,7 +24,7 @@ export class EthCommonStack extends cdk.Stack { }); const snapshotsBucket = new SnapshotsS3BucketConstruct(this, `snapshots-s3-bucket`, { - bucketName: `eth-snapshots-${this.AWS_ACCOUNT_ID}-${this.AWS_STACKNAME}`, + bucketName: `eth-snapshots-${this.AWS_STACKNAME}-${this.AWS_ACCOUNT_ID}-${this.AWS_REGION}`, }); const s3VPCEndpoint = vpc.addGatewayEndpoint("s3-vpc-endpoint", { diff --git a/lib/ethereum/lib/config/ethConfig.interface.ts b/lib/ethereum/lib/config/ethConfig.interface.ts index aaa396da..6d2df4e0 100644 --- a/lib/ethereum/lib/config/ethConfig.interface.ts +++ b/lib/ethereum/lib/config/ethConfig.interface.ts @@ -2,6 +2,8 @@ import * as configTypes from "../../../constructs/config.interface"; export type EthClientCombination = "besu-teku" | "geth-lighthouse" | "erigon-lighthouse" | "erigon-prysm" | "nethermind-teku"; +export type EthNodeRole = "sync-node" | "rpc-node" | "single-node"; + export interface EthDataVolumeConfig extends configTypes.DataVolumeConfig { } diff --git a/lib/ethereum/lib/rpc-nodes-stack.ts b/lib/ethereum/lib/rpc-nodes-stack.ts index 4aca8c78..9b355f23 100644 --- a/lib/ethereum/lib/rpc-nodes-stack.ts +++ b/lib/ethereum/lib/rpc-nodes-stack.ts @@ -6,15 +6,16 @@ import * as s3Assets from "aws-cdk-lib/aws-s3-assets"; import * as nag from "cdk-nag"; import * as path from "path"; import * as fs from "fs"; -import * as config from "./config/ethConfig.interface"; +import * as configTypes from "./config/ethConfig.interface"; import { EthNodeSecurityGroupConstruct } from "./constructs/eth-node-security-group" import { HANodesConstruct } from "../../constructs/ha-rpc-nodes-with-alb" export interface EthRpcNodesStackProps extends cdk.StackProps { - ethClientCombination: config.EthClientCombination; + ethClientCombination: configTypes.EthClientCombination; + nodeRole: configTypes.EthNodeRole; instanceType: ec2.InstanceType; instanceCpuType: ec2.AmazonLinuxCpuType; - dataVolumes: config.EthDataVolumeConfig[], + dataVolumes: configTypes.EthDataVolumeConfig[], numberOfNodes: number; albHealthCheckGracePeriodMin: number; heartBeatDelayMin: number; @@ -34,6 +35,7 @@ export class EthRpcNodesStack extends cdk.Stack { const { instanceType, ethClientCombination, + nodeRole, instanceCpuType, dataVolumes, albHealthCheckGracePeriodMin, @@ -71,7 +73,7 @@ export class EthRpcNodesStack extends cdk.Stack { _ETH_CLIENT_COMBINATION_: ethClientCombination, _STACK_NAME_: STACK_NAME, _FORMAT_DISK_: "true", - _NODE_ROLE_:"rpc-node", + _NODE_ROLE_: nodeRole, _AUTOSTART_CONTAINER_: "true", _NODE_CF_LOGICAL_ID_: "", _LIFECYCLE_HOOK_NAME_: lifecycleHookName, diff --git a/lib/ethereum/lib/sync-node-stack.ts b/lib/ethereum/lib/single-node-stack.ts similarity index 91% rename from lib/ethereum/lib/sync-node-stack.ts rename to lib/ethereum/lib/single-node-stack.ts index 667a19ef..b2a49a8b 100644 --- a/lib/ethereum/lib/sync-node-stack.ts +++ b/lib/ethereum/lib/single-node-stack.ts @@ -12,15 +12,16 @@ import * as configTypes from "./config/ethConfig.interface"; import { EthNodeSecurityGroupConstruct } from "./constructs/eth-node-security-group" import * as nag from "cdk-nag"; -export interface EthSyncNodeStackProps extends cdk.StackProps { +export interface EthSingleNodeStackProps extends cdk.StackProps { ethClientCombination: configTypes.EthClientCombination; + nodeRole: configTypes.EthNodeRole; instanceType: ec2.InstanceType; instanceCpuType: ec2.AmazonLinuxCpuType; dataVolumes: configTypes.EthDataVolumeConfig[]; } -export class EthSyncNodeStack extends cdk.Stack { - constructor(scope: cdkConstructs.Construct, id: string, props: EthSyncNodeStackProps) { +export class EthSingleNodeStack extends cdk.Stack { + constructor(scope: cdkConstructs.Construct, id: string, props: EthSingleNodeStackProps) { super(scope, id, props); // Setting up necessary environment variables @@ -33,6 +34,7 @@ export class EthSyncNodeStack extends cdk.Stack { const { instanceType, ethClientCombination, + nodeRole, instanceCpuType, dataVolumes, } = props; @@ -61,7 +63,7 @@ export class EthSyncNodeStack extends cdk.Stack { asset.bucket.grantRead(instanceRole); // Setting up the node using generic Single Node constract - const syncNode = new SingleNodeConstruct(this, "sync-node", { + const syncNode = new SingleNodeConstruct(this, "single-node", { instanceName: STACK_NAME, instanceType, dataVolumes, @@ -89,7 +91,7 @@ export class EthSyncNodeStack extends cdk.Stack { _STACK_NAME_: STACK_NAME, _AUTOSTART_CONTAINER_: "true", _FORMAT_DISK_: "true", - _NODE_ROLE_:"sync-node", + _NODE_ROLE_:nodeRole, _NODE_CF_LOGICAL_ID_: syncNode.nodeCFLogicalId, _LIFECYCLE_HOOK_NAME_: "", _AUTOSCALING_GROUP_NAME_: "", @@ -105,12 +107,12 @@ export class EthSyncNodeStack extends cdk.Stack { REGION: REGION, }) - new cw.CfnDashboard(this, 'sync-cw-dashboard', { + new cw.CfnDashboard(this, 'single-cw-dashboard', { dashboardName: STACK_NAME, dashboardBody: dashboardString, }); - new cdk.CfnOutput(this, "sync-instance-id", { + new cdk.CfnOutput(this, "single-instance-id", { value: syncNode.instanceId, }); diff --git a/lib/ethereum/test/rpc-nodes-stack.test.ts b/lib/ethereum/test/rpc-nodes-stack.test.ts index 05555a85..a1680046 100644 --- a/lib/ethereum/test/rpc-nodes-stack.test.ts +++ b/lib/ethereum/test/rpc-nodes-stack.test.ts @@ -4,6 +4,7 @@ import * as dotenv from 'dotenv'; dotenv.config({ path: './test/.env-test' }); import * as config from "../lib/config/ethConfig"; import { EthRpcNodesStack } from "../lib/rpc-nodes-stack"; +import { EthNodeRole } from "../lib/config/ethConfig.interface"; describe("EthRpcNodesStack", () => { test("synthesizes the way we expect", () => { @@ -15,6 +16,7 @@ describe("EthRpcNodesStack", () => { env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, ethClientCombination: config.baseConfig.clientCombination, + nodeRole: "single-node", instanceType: config.rpcNodeConfig.instanceType, instanceCpuType: config.rpcNodeConfig.instanceCpuType, numberOfNodes: config.rpcNodeConfig.numberOfNodes, diff --git a/lib/ethereum/test/solo-node-stack.test.ts b/lib/ethereum/test/solo-node-stack.test.ts new file mode 100644 index 00000000..18d471e4 --- /dev/null +++ b/lib/ethereum/test/solo-node-stack.test.ts @@ -0,0 +1,163 @@ +import { Match, Template } from "aws-cdk-lib/assertions"; +import * as cdk from "aws-cdk-lib"; +import * as dotenv from 'dotenv'; +dotenv.config({ path: './test/.env-test' }); +import * as config from "../lib/config/ethConfig"; +import { EthSingleNodeStack } from "../lib/single-node-stack"; +import { EthNodeRole } from "../lib/config/ethConfig.interface"; + +describe("EthSyncNodeStack", () => { + test("synthesizes the way we expect", () => { + const app = new cdk.App(); + + // Create the EthSingleNodeStack. + const ethSyncNodeStack = new EthSingleNodeStack(app, "eth-single-node", { + stackName: `eth-single-node-${config.baseConfig.clientCombination}`, + + env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, + ethClientCombination: config.baseConfig.clientCombination, + nodeRole: "single-node", + instanceType: config.syncNodeConfig.instanceType, + instanceCpuType: config.syncNodeConfig.instanceCpuType, + dataVolumes: config.syncNodeConfig.dataVolumes, + }); + + // Prepare the stack for assertions. + const template = Template.fromStack(ethSyncNodeStack); + + // Has EC2 instance security group. + template.hasResourceProperties("AWS::EC2::SecurityGroup", { + GroupDescription: Match.anyValue(), + VpcId: Match.anyValue(), + SecurityGroupEgress: [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + SecurityGroupIngress: [ + { + "CidrIp": "0.0.0.0/0", + "Description": "P2P", + "FromPort": 30303, + "IpProtocol": "tcp", + "ToPort": 30303 + }, + { + "CidrIp": "0.0.0.0/0", + "Description": "P2P", + "FromPort": 30303, + "IpProtocol": "udp", + "ToPort": 30303 + }, + { + "CidrIp": "0.0.0.0/0", + "Description": "P2P", + "FromPort": 30304, + "IpProtocol": "tcp", + "ToPort": 30304 + }, + { + "CidrIp": "0.0.0.0/0", + "Description": "P2P", + "FromPort": 30304, + "IpProtocol": "udp", + "ToPort": 30304 + }, + { + "CidrIp": "0.0.0.0/0", + "Description": "CL Client P2P", + "FromPort": 9000, + "IpProtocol": "tcp", + "ToPort": 9000 + }, + { + "CidrIp": "0.0.0.0/0", + "Description": "CL Client P2P", + "FromPort": 9000, + "IpProtocol": "udp", + "ToPort": 9000 + }, + { + "CidrIp": "1.2.3.4/5", + "Description": "CL Client API", + "FromPort": 5051, + "IpProtocol": "tcp", + "ToPort": 5051 + }, + { + "CidrIp": "1.2.3.4/5", + "Description": "CL Client API", + "FromPort": 5052, + "IpProtocol": "tcp", + "ToPort": 5052 + }, + { + "CidrIp": "1.2.3.4/5", + "Description": "EL Client RPC (Auth)", + "FromPort": 8551, + "IpProtocol": "tcp", + "ToPort": 8551 + }, + { + "CidrIp": "1.2.3.4/5", + "Description": "EL Client RPC", + "FromPort": 8545, + "IpProtocol": "tcp", + "ToPort": 8545 + } + ] + }) + + // Has EC2 instance with node configuration + template.hasResourceProperties("AWS::EC2::Instance", { + AvailabilityZone: Match.anyValue(), + UserData: Match.anyValue(), + BlockDeviceMappings: [ + { + DeviceName: "/dev/xvda", + Ebs: { + DeleteOnTermination: true, + Encrypted: true, + Iops: 3000, + VolumeSize: 46, + VolumeType: "gp3" + } + } + ], + IamInstanceProfile: Match.anyValue(), + ImageId: Match.anyValue(), + InstanceType: "m6g.2xlarge", + Monitoring: true, + PropagateTagsToVolumeOnCreation: true, + SecurityGroupIds: Match.anyValue(), + SubnetId: Match.anyValue(), + }) + + // Has EBS data volume. + template.hasResourceProperties("AWS::EC2::Volume", { + AvailabilityZone: Match.anyValue(), + Encrypted: true, + Iops: 6000, + MultiAttachEnabled: false, + Size: 3072, + Throughput: 400, + VolumeType: "gp3" + }) + + // Has EBS data volume attachment. + template.hasResourceProperties("AWS::EC2::VolumeAttachment", { + Device: "/dev/sdf", + InstanceId: Match.anyValue(), + VolumeId: Match.anyValue(), + }) + + // Has CloudWatch dashboard. + template.hasResourceProperties("AWS::CloudWatch::Dashboard", { + DashboardBody: Match.anyValue(), + DashboardName: `eth-single-node-${config.baseConfig.clientCombination}` + }) + + }); +}); diff --git a/lib/ethereum/test/sync-node-stack.test.ts b/lib/ethereum/test/sync-node-stack.test.ts index ffdb310e..a7996176 100644 --- a/lib/ethereum/test/sync-node-stack.test.ts +++ b/lib/ethereum/test/sync-node-stack.test.ts @@ -3,18 +3,20 @@ import * as cdk from "aws-cdk-lib"; import * as dotenv from 'dotenv'; dotenv.config({ path: './test/.env-test' }); import * as config from "../lib/config/ethConfig"; -import { EthSyncNodeStack } from "../lib/sync-node-stack"; +import { EthSingleNodeStack } from "../lib/single-node-stack"; +import { EthNodeRole } from "../lib/config/ethConfig.interface"; describe("EthSyncNodeStack", () => { test("synthesizes the way we expect", () => { const app = new cdk.App(); - // Create the EthSyncNodeStack. - const ethSyncNodeStack = new EthSyncNodeStack(app, "eth-sync-node", { + // Create the EthSingleNodeStack. + const ethSyncNodeStack = new EthSingleNodeStack(app, "eth-sync-node", { stackName: `eth-sync-node-${config.baseConfig.clientCombination}`, env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, ethClientCombination: config.baseConfig.clientCombination, + nodeRole: "sync-node", instanceType: config.syncNodeConfig.instanceType, instanceCpuType: config.syncNodeConfig.instanceCpuType, dataVolumes: config.syncNodeConfig.dataVolumes, diff --git a/lib/ethereum/tsconfig.json b/lib/ethereum/tsconfig.json new file mode 100644 index 00000000..8e1979f3 --- /dev/null +++ b/lib/ethereum/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020", + "dom" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "../../node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} diff --git a/package-lock.json b/package-lock.json index 22a0cc46..d4a1045c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,8 +14,8 @@ "source-map-support": "^0.5.21" }, "devDependencies": { - "@types/jest": "^29.5.0", - "@types/node": "^20.5.0", + "@types/jest": "^29.5.11", + "@types/node": "^20.10.0", "aws-cdk": "^2.84.0", "cdk-nag": "^2.27.43", "jest": "^29.5.0", @@ -1176,9 +1176,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.4", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.4.tgz", - "integrity": "sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A==", + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -1186,10 +1186,13 @@ } }, "node_modules/@types/node": { - "version": "20.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.0.tgz", - "integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==", - "dev": true + "version": "20.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", + "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/stack-utils": { "version": "2.0.1", @@ -4104,6 +4107,12 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", diff --git a/package.json b/package.json index b44da3a0..c98c4710 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "run-pre-commit": "pre-commit run --all-files" }, "devDependencies": { - "@types/jest": "^29.5.0", + "@types/jest": "^29.5.11", "@types/node": "^20.10.0", "aws-cdk": "^2.84.0", "cdk-nag": "^2.27.43", From f435bfad0de2c47906534149dc687ea5d32e0b18 Mon Sep 17 00:00:00 2001 From: Nikolay Vlasov Date: Tue, 23 Jan 2024 13:01:20 +1100 Subject: [PATCH 03/22] Single node Ethereum setup - add diagram to the Readme file --- lib/ethereum/README.md | 10 +++++++++- lib/ethereum/doc/assets/Architecture-PoC.png | Bin 0 -> 108880 bytes 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 lib/ethereum/doc/assets/Architecture-PoC.png diff --git a/lib/ethereum/README.md b/lib/ethereum/README.md index 2a31911e..212048a0 100644 --- a/lib/ethereum/README.md +++ b/lib/ethereum/README.md @@ -2,6 +2,14 @@ ## Architecture Overview +This blueprint has two options for running nodes. You can set up a single JSON RPC node or multiple nodes in highly-available setup. The details are below. + +### Single RPC node setup +![SingleNodeSetup](./doc/assets/Architecture-PoC.png) + +This setup is for small scale PoC or development environments. It deploys a single EC2 instance with both consensus and execution clients. The RPC port is exposed only to internal IP range of the VPC, while P2P ports allow external access to keep the clients synced. + +### Highly available setup ![Architecture](./doc/assets/Architecture.png) 1. An ongoing data synchronization process is configured with nodes in the Ethereum network with a sync node and RPC nodes. @@ -131,7 +139,7 @@ The result should be like this (the actual balance might change): {"jsonrpc":"2.0","id":1,"result":"0xe791d050f91d9949d344d"} ``` -### Option 2: Deploy the Highly Available RPC Nodes +### Option 2: Highly Available RPC Nodes 1. Deploy Sync Node diff --git a/lib/ethereum/doc/assets/Architecture-PoC.png b/lib/ethereum/doc/assets/Architecture-PoC.png new file mode 100644 index 0000000000000000000000000000000000000000..6cdac6fd22644d4a4691921cf6645b7e74f844c5 GIT binary patch literal 108880 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

+JvUX`T07z20sT+UBS@_jOYnx+@EzzyGaMiKttM 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 e2Ogmnen9z2b+c-TyXqqh{OO-HI#a5Rjrboa6 Date: Thu, 18 Jan 2024 16:44:51 +0900 Subject: [PATCH 04/22] add Indy Node sample update documents (English and Japanese README) --- lib/indy/README.md | 175 +++++++++++++++++ lib/indy/README_ja.md | 176 ++++++++++++++++++ lib/indy/app.ts | 7 + lib/indy/cdk.json | 44 +++++ lib/indy/doc/assets/Architecture.drawio | 87 +++++++++ lib/indy/doc/assets/Architecture.png | Bin 0 -> 52745 bytes lib/indy/jest.config.js | 8 + lib/indy/lib/assets/user-data/steward.sh | 102 ++++++++++ lib/indy/lib/assets/user-data/trustee.sh | 34 ++++ lib/indy/lib/constructs/indy-node-instance.ts | 63 +++++++ lib/indy/lib/indy-node-stack.ts | 66 +++++++ lib/indy/package-lock.json | 20 ++ lib/indy/package.json | 14 ++ 13 files changed, 796 insertions(+) create mode 100644 lib/indy/README.md create mode 100644 lib/indy/README_ja.md create mode 100644 lib/indy/app.ts create mode 100644 lib/indy/cdk.json create mode 100644 lib/indy/doc/assets/Architecture.drawio create mode 100644 lib/indy/doc/assets/Architecture.png create mode 100644 lib/indy/jest.config.js create mode 100644 lib/indy/lib/assets/user-data/steward.sh create mode 100644 lib/indy/lib/assets/user-data/trustee.sh create mode 100644 lib/indy/lib/constructs/indy-node-instance.ts create mode 100644 lib/indy/lib/indy-node-stack.ts create mode 100644 lib/indy/package-lock.json create mode 100644 lib/indy/package.json diff --git a/lib/indy/README.md b/lib/indy/README.md new file mode 100644 index 00000000..c676ce47 --- /dev/null +++ b/lib/indy/README.md @@ -0,0 +1,175 @@ +# Sample AWS Blockchain Node Runner app for Hyperledger Indy + +[View this page in Japanese (日本語)](./README_ja.md) + +## Architecture Overview + +![Architecture](./doc/assets/Architecture.png) + +This is a sample of building a Hyperledger Indy network on AWS. +The overall architecture is shown below, processing itself is performed by 4 Stewards (Validator Nodes), and network management is performed with Trustee. It consists of 4 EC2 instances for Steward and 1 EC2 instance for Trustee. + +## Solution Walkthrough + +### Setup Cloud9 + +We will use AWS Cloud9 to execute the subsequent commands. Follow the instructions in [Cloud9 Setup](../../docs/setup-cloud9.md) + +### Clone this repository and install dependencies + +```bash +git clone https://github.com/aws-samples/aws-blockchain-node-runners.git +cd aws-blockchain-node-runners +npm install +``` + +**NOTE:** In this tutorial we will set all major configuration through environment variables, but you also can modify parameters in `config/config.ts`. + +### Deploy Indy Nodes + +Indy Network is built using 4 EC2 instances for Steward and 1 EC2 instance for Trustee. Various information such as DID is acquired in the following procedure, copied by referring to [this community spreadsheet](https://docs.google.com/spreadsheets/d/1LDduIeZp7pansd9deXeVSqGgdf0VdAHNMc7xYli3QAY/edit#gid=0). + +#### Building resources + +1. Install npm dependency packages + +```bash +cd lib/indy +pwd +# Make sure you are in aws-blockchain-node-runners/lib/indy +npm install +``` + +2. Setting up initial AWS Cloud Development Kit (CDK) + +The following command is executed only when using AWS CDK for the first time in the region where the deployment will be carried out. + +```bash +npx cdk bootstrap +``` + +3. Deploying resources with CDK + +```bash +npx cdk deploy + +Outputs: +IndyNodeStack.Node1InstanceId = i-xxxxxxxxxxxxxxxxx +IndyNodeStack.Node2InstanceId = i-xxxxxxxxxxxxxxxxx +IndyNodeStack.Node3InstanceId = i-xxxxxxxxxxxxxxxxx +IndyNodeStack.Node4InstanceId = i-xxxxxxxxxxxxxxxxx +IndyNodeStack.TrusteeInstanceId = i-xxxxxxxxxxxxxxxxx +``` + +**NOTE:** User data for the Steward instance is created by referring to [the Community Docs](https://github.com/hyperledger/indy-node/blob/main/docs/source/install-docs/AWS-NodeInstall-20.04.md). + +#### Setting up Trustee + +Log in to the Trustee instance via Session Manager from the EC2 (or Systems Manager) console and generate Trustee/Steward DIDs. +​ + +```bash +cd / +./indy-cli-rs +​ +# Perform the following commands 3 times for Trustee and 4 times for Steward +wallet create key= +wallet open key= +did new seed= +wallet close +``` + +#### Setting up Steward + +Log in to the Steward instance via Session Manager from the EC2 (or Systems Manager) console and generate Validator verkey, BLS key, and BLS POP. + +```bash +sudo init_indy_node 9701 9702 +``` + +**NOTE:** Here, Steward represents Validator Node ([reference information](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md#32-validator-node-installation)). + +#### Generating Genesis files + +1. Download each sheet (Stewards, Trustees) containing the information generated in the steps so far + - File → Download → .csv +2. Save `trustees.csv` and `stewards.csv` to Trustee instance + +**NOTE:** To transfer a locally downloaded CSV file via Session Manager, use the Session Manager Plugin in addition to the AWS CLI to transfer it with the following command ([reference information](https://dev.classmethod.jp/articles/ssm-session-manager-support-for-tunneling-ssh-scp-on-windows10/)). + +```bash +scp -i ec2-user@:~/ +``` + +​ +3. Generate Genesis files + +Using the above two CSV files, generate Genesis files (`pool_transactions_genesis`, `domain_transactions_genesis`) with `genesis_from_files.py` + +```bash +cd ~/ +wget -nc https://raw.githubusercontent.com/sovrin-foundation/steward-tools/master/create_genesis/genesis_from_files.py +​ +chmod +x genesis_from_files.py +./genesis_from_files.py --stewards stewards.csv --trustees trustees.csv + +DEBUG:root:new line check for file: ./pool_transactions_genesis +INFO:root:Starting ledger... +INFO:root:Recovering tree from transaction log +INFO:root:Recovered tree in 0.00010979999979099375 seconds +DEBUG:root:new line check for file: ./domain_transactions_genesis +INFO:root:Starting ledger... +INFO:root:Recovering tree from transaction log +INFO:root:Recovered tree in 8.670999977766769e-05 seconds +``` + +#### Setting up Nodes + +Start up each Validator Node (Steward) + +1. Download Genesis files and set permissions + +Download or copy Genesis files to Node instance. Then, set the permissions for all files under `/var/lib/indy/` to `indy` ([reference information](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md#iv-create-and-distribute-genesis-transaction-files)​). + +```bash +cd /var/lib/indy/sample-network + +# Save domain_transactions_genesis and pool_transactions_genesis +# sudo curl -o domain_transactions_genesis +# sudo curl -o pool_transactions_genesis + +sudo chown -R indy:indy ../ +``` + +**NOTE:** The directory name of `/var/lib/indy/sample-network` is `NETWORK_NAME` set in `lib/indy/lib/assets/user-data/steward.sh`. + +2. Start indy-node and check status + +```bash +sudo systemctl start indy-node +sudo systemctl status indy-node +sudo systemctl enable indy-node +​ +sudo validator-info +``` + +**NOTE:** [reference information](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md#35-add-node-to-a-pool) + +#### reference information + +- [Buidling Indy Network](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md) +- [Setting up EC2 instances for Indy Node](https://github.com/hyperledger/indy-node/blob/main/docs/source/install-docs/AWS-NodeInstall-20.04.md) +- [Setting up Indy Node](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md) +​ + +### Considerations + +Matters to be examined in additional development etc. when using this sample are described. + +- Change the instance type to M + - Currently, it is a T instance, but in production environments, it is recommended to change to M +- Fix the security group for Node NICs attached to Steward (Validator Node) + - Limit source IPs to node IPs of other nodes (currently open within VPC and can also be accessed by clients) + - Fix Node's private IP +- If necessary, change the subnet to which the node belongs to a public subnet +- Make Steward and Node separate instances diff --git a/lib/indy/README_ja.md b/lib/indy/README_ja.md new file mode 100644 index 00000000..8cf47753 --- /dev/null +++ b/lib/indy/README_ja.md @@ -0,0 +1,176 @@ +# Sample AWS Blockchain Node Runner app for Hyperledger Indy + +[English](./README.md) + +## Architecture Overview + +![Architecture](./doc/assets/Architecture.png) + +Hyperledger Indy のネットワークを AWS 上に構築するサンプルである。 +全体像は下図の通り、処理自体は 4 つの Steward (Validator Node) で行われ、ネットワークの管理は Trustee で行われる。実体は Steward 用の 4 つの EC2 インスタンスと、Trustee 用の 1 つの EC2 インスタンスである。 + +## Solution Walkthrough + +### Setup Cloud9 + +We will use AWS Cloud9 to execute the subsequent commands. Follow the instructions in [Cloud9 Setup](../../docs/setup-cloud9.md) + +### Clone this repository and install dependencies + +```bash +git clone https://github.com/aws-samples/aws-blockchain-node-runners.git +cd aws-blockchain-node-runners +npm install +``` + +**NOTE:** In this tutorial we will set all major configuration through environment variables, but you also can modify parameters in `config/config.ts`. + +### Deploy Indy Nodes + +Indy Network を Steward 用の4つの EC2 インスタンスと、Trustee 用の1つの EC2 インスタンスを用いて構築する。下記手順の中で DID など各種情報を取得し、それらを[こちらの Community のスプレッドシート](https://docs.google.com/spreadsheets/d/1LDduIeZp7pansd9deXeVSqGgdf0VdAHNMc7xYli3QAY/edit#gid=0)を参考にコピーしてまとめる。 + +#### リソースの構築 + +1. npm の依存パッケージをインストール + +```bash +cd lib/indy +pwd +# Make sure you are in aws-blockchain-node-runners/lib/indy +npm install +``` + +2. AWS Cloud Development Kit (CDK) の初期設定 + +下記のコマンドはデプロイを実施するリージョンで AWS CDK を使用していない場合のみ実施する + +```bash +npx cdk bootstrap +``` + +3. CDK でリソースの構築 + +```bash +npx cdk deploy + +Outputs: +IndyNodeStack.Node1InstanceId = i-xxxxxxxxxxxxxxxxx +IndyNodeStack.Node2InstanceId = i-xxxxxxxxxxxxxxxxx +IndyNodeStack.Node3InstanceId = i-xxxxxxxxxxxxxxxxx +IndyNodeStack.Node4InstanceId = i-xxxxxxxxxxxxxxxxx +IndyNodeStack.TrusteeInstanceId = i-xxxxxxxxxxxxxxxxx +``` + +**NOTE:** Steward インスタンスのユーザーデータは [Community の Doc](https://github.com/hyperledger/indy-node/blob/main/docs/source/install-docs/AWS-NodeInstall-20.04.md) を参考に作成している。 + +#### Trustee の設定 + +EC2 (もしくは Systems Manager) のコンソールから Session Manager 経由で Trustee インスタンスにログインし、Trustee/Steward の DID を生成する。 +​ + +```bash +cd / +./indy-cli-rs +​ +# 下記の操作を Trustee 用に 3回、Steward 用に 4回の計7回実施 +wallet create key= +wallet open key= +did new seed= +wallet close +``` + +#### Steward の設定​ + +EC2 (もしくは Systems Manager) のコンソールから Session Manager 経由で Steward インスタンスにログインして、Validator verkey, BLS key, BLS POP を生成する。 + +```bash +sudo init_indy_node 9701 9702 +``` + +**NOTE:** ここでは Steward は Validator Node のことを表す ([参考情報](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md#32-validator-node-installation))。 + +#### Genesis Files の生成 + +1. これまでの手順で生成した情報を記載したスプレッドシートの各シート (Stewards / Trustees) をダウンロード + - File → Download → .csv +2. `trustees.csv` / `stewards.csv` を Trustee インスタンスに保存 + +**NOTE:** ローカルにダウンロードした CSV ファイルを Session Manager 経由で転送するには AWS CLI に加えて Session Manager Plugin を用いて下記コマンドで転送する ([参考情報](https://dev.classmethod.jp/articles/ssm-session-manager-support-for-tunneling-ssh-scp-on-windows10/))。 + +```bash +scp -i ec2-user@:~/ +``` + +​ +3. Genesis Files 生成 + +上記 2 つの CSV ファイルを用いて、`genesis_from_files.py` によって Genesis files (`pool_transactions_genesis`, `domain_transactions_genesis`) を生成する + +```bash +cd ~/ +wget -nc https://raw.githubusercontent.com/sovrin-foundation/steward-tools/master/create_genesis/genesis_from_files.py +​ +chmod +x genesis_from_files.py +./genesis_from_files.py --stewards stewards.csv --trustees trustees.csv + +DEBUG:root:new line check for file: ./pool_transactions_genesis +INFO:root:Starting ledger... +INFO:root:Recovering tree from transaction log +INFO:root:Recovered tree in 0.00010979999979099375 seconds +DEBUG:root:new line check for file: ./domain_transactions_genesis +INFO:root:Starting ledger... +INFO:root:Recovering tree from transaction log +INFO:root:Recovered tree in 8.670999977766769e-05 seconds +``` + +#### Node の設定 + +各 Validator Node (Steward) の立ち上げを行う + +1. Genesis Files のダウンロードと各種ファイルの権限設定 + +Genesis Files を Node インスタンスにダウンロードもしくはコピーする。そして、`/var/lib/indy/` 配下の全ファイルの権限を indy に設定する ([参考情報](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md#iv-create-and-distribute-genesis-transaction-files)​)。 + +```bash +cd /var/lib/indy/sample-network + +# domain_transactions_genesis と pool_transactions_genesis を保存 +# sudo curl -o domain_transactions_genesis +# sudo curl -o pool_transactions_genesis + +sudo chown -R indy:indy ../ +``` + +**NOTE:** `/var/lib/indy/sample-network` のディレクトリ名 は `lib/indy/lib/assets/user-data/steward.sh` で設定している `NETWORK_NAME` である。 + +2. indy-node の起動と動作確認 + +```bash +sudo systemctl start indy-node +sudo systemctl status indy-node +sudo systemctl enable indy-node +​ +sudo validator-info +``` + +**NOTE:** [ドキュメント](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md#35-add-node-to-a-pool)の 3.5.2 以降を実施している +​ + +#### 参考情報 + +- [Indy Network の構築](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md) +- [Indy Node のための EC2 セットアップ](https://github.com/hyperledger/indy-node/blob/main/docs/source/install-docs/AWS-NodeInstall-20.04.md) +- [Indy Node のセットアップ](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md) +​ + +### 考慮事項 + +本サンプルを利用するにあたり追加開発などで検討する事項を記載する。 + +- インスタンスタイプを M 系に変更 + - 現状は T 系インスタンスであるが本番環境では M 系などへの変更を推奨 +- Steward (Validator Node) にアタッチされている Node NIC の Security Group を修正 + - Source IP を他ノードの Node IP に制限する (現在は VPC 内にオープンになっており、Client からもアクセスできる) + - Node の Private IP を固定 +- 必要に応じて Node の属するサブネットを Public Subnet にする +- Steward と Node を別インスタンスにする diff --git a/lib/indy/app.ts b/lib/indy/app.ts new file mode 100644 index 00000000..399ab153 --- /dev/null +++ b/lib/indy/app.ts @@ -0,0 +1,7 @@ +#!/usr/bin/env node +import 'source-map-support/register'; +import * as cdk from 'aws-cdk-lib'; +import { IndyNodeStack } from './lib/indy-node-stack'; + +const app = new cdk.App(); +new IndyNodeStack(app, 'IndyNodeStack', {}); \ No newline at end of file diff --git a/lib/indy/cdk.json b/lib/indy/cdk.json new file mode 100644 index 00000000..d374005c --- /dev/null +++ b/lib/indy/cdk.json @@ -0,0 +1,44 @@ +{ + "app": "npx ts-node --prefer-ts-exts app.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false + } +} diff --git a/lib/indy/doc/assets/Architecture.drawio b/lib/indy/doc/assets/Architecture.drawio new file mode 100644 index 00000000..20cb9a08 --- /dev/null +++ b/lib/indy/doc/assets/Architecture.drawio @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/indy/doc/assets/Architecture.png b/lib/indy/doc/assets/Architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..af1432cfc9811f4af61fe7920dc13c8fc38d2d4e GIT binary patch literal 52745 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 literal 0 HcmV?d00001 diff --git a/lib/indy/jest.config.js b/lib/indy/jest.config.js new file mode 100644 index 00000000..0bc77ac0 --- /dev/null +++ b/lib/indy/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + }, +}; diff --git a/lib/indy/lib/assets/user-data/steward.sh b/lib/indy/lib/assets/user-data/steward.sh new file mode 100644 index 00000000..1202f7ac --- /dev/null +++ b/lib/indy/lib/assets/user-data/steward.sh @@ -0,0 +1,102 @@ +#!/bin/bash +NETWORK_NAME=sample-network + +echo 'network: {config: disabled}' > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg +echo 800 800 >> /etc/iproute2/rt_tables +echo 801 801 >> /etc/iproute2/rt_tables + +gateway_ip=$(ip route | grep -m1 "default via " | cut -d\ -f 3) + +client_ip_cidr=$(ip -f inet -o addr show ens5|cut -d\ -f 7) +client_ip=$(ip -f inet -o addr show ens5|cut -d\ -f 7 | cut -d/ -f 1) +client_mac=$(cat `find /sys/devices/ -name ens5`/address) + +node_ip_cidr=$(ip -f inet -o addr show ens6|cut -d\ -f 7) +node_ip=$(ip -f inet -o addr show ens6|cut -d\ -f 7 | cut -d/ -f 1) +node_mac=$(cat `find /sys/devices/ -name ens6`/address) + +echo " +network: + ethernets: + ens5: + addresses: + - ${client_ip_cidr} + gateway4: ${gateway_ip} + match: + macaddress: ${client_mac} + mtu: 1500 + set-name: ens5 + routes: + - to: 0.0.0.0/0 + via: ${gateway_ip} + table: 800 + routing-policy: + - from: ${client_ip} + table: 800 + priority: 300 + nameservers: + addresses: + - 8.8.8.8 + - 8.8.4.4 + - 1.1.1.1 + ens6: + addresses: + - ${node_ip_cidr} + match: + macaddress: ${node_mac} + mtu: 1500 + set-name: ens6 + routes: + - to: 0.0.0.0/0 + via: ${gateway_ip} + table: 801 + routing-policy: + - from: ${node_ip} + table: 801 + priority: 300 + nameservers: + addresses: + - 8.8.8.8 + - 8.8.4.4 + - 1.1.1.1 + version: 2 +" > /etc/netplan/50-cloud-init.yaml + +netplan generate +netplan apply + +apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 9692C00E657DDE61 +apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 +apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3BC8C2DD662F1C45 +add-apt-repository "deb https://hyperledger.jfrog.io/artifactory/indy focal rc" +add-apt-repository "deb http://security.ubuntu.com/ubuntu bionic-security main" +add-apt-repository "deb https://repo.sovrin.org/deb bionic master" +add-apt-repository "deb https://sovrin.jfrog.io/artifactory/deb focal rc" +apt-get update -y +apt-get upgrade -y +apt-get install -y \ + rocksdb=5.8.8 \ + libgflags-dev \ + libsnappy-dev \ + zlib1g-dev \ + libbz2-dev \ + liblz4-dev \ + libgflags-dev \ + python3-libnacl=1.6.1 \ + python3-sortedcontainers=1.5.7 \ + python3-ujson=1.33 \ + python3-pyzmq=22.3.0 \ + indy-plenum=1.13.1~rc3 \ + indy-node=1.13.2~rc5 \ + sovtoken=1.1.0~rc0 \ + sovtokenfees=1.1.0~rc0 \ + sovrin=1.2.0~rc1 \ + libssl1.0.0 \ + ursa=0.3.2-1 + +ln -sv /usr/lib/ursa/* /usr/lib + +sed -i "s/NETWORK_NAME = None/NETWORK_NAME = '${NETWORK_NAME}'/" /etc/indy/indy_config.py +sed -i -re "s/(NETWORK_NAME = ')\w+/\1${NETWORK_NAME}/" /etc/indy/indy_config.py + +reboot \ No newline at end of file diff --git a/lib/indy/lib/assets/user-data/trustee.sh b/lib/indy/lib/assets/user-data/trustee.sh new file mode 100644 index 00000000..be333d83 --- /dev/null +++ b/lib/indy/lib/assets/user-data/trustee.sh @@ -0,0 +1,34 @@ +#!/bin/bash +apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 9692C00E657DDE61 +apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 +apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3BC8C2DD662F1C45 +add-apt-repository "deb https://hyperledger.jfrog.io/artifactory/indy focal rc" +add-apt-repository "deb http://security.ubuntu.com/ubuntu bionic-security main" +add-apt-repository "deb https://repo.sovrin.org/deb bionic master" +add-apt-repository "deb https://sovrin.jfrog.io/artifactory/deb focal rc" +apt-get update -y +apt-get upgrade -y +apt-get install -y \ + rocksdb=5.8.8 \ + libgflags-dev \ + libsnappy-dev \ + zlib1g-dev \ + libbz2-dev \ + liblz4-dev \ + libgflags-dev \ + python3-libnacl=1.6.1 \ + python3-sortedcontainers=1.5.7 \ + python3-ujson=1.33 \ + python3-pyzmq=22.3.0 \ + indy-plenum=1.13.1~rc3 \ + indy-node=1.13.2~rc5 \ + sovtoken=1.1.0~rc0 \ + sovtokenfees=1.1.0~rc0 \ + sovrin=1.2.0~rc1 \ + libssl1.0.0 \ + ursa=0.3.2-1 + +ln -sv /usr/lib/ursa/* /usr/lib + +wget https://github.com/hyperledger/indy-cli-rs/releases/download/v0.1.0/indy-cli-rs-0.1.0-linux-x86_64.tar.gz +tar -xf indy-cli-rs-0.1.0-linux-x86_64.tar.gz \ No newline at end of file diff --git a/lib/indy/lib/constructs/indy-node-instance.ts b/lib/indy/lib/constructs/indy-node-instance.ts new file mode 100644 index 00000000..a0bdf532 --- /dev/null +++ b/lib/indy/lib/constructs/indy-node-instance.ts @@ -0,0 +1,63 @@ +import * as cdk from 'aws-cdk-lib'; +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import { Construct } from 'constructs'; + +import { readFileSync } from "fs"; + +export interface IndyNodeInstanceProps { + readonly vpc: ec2.IVpc + readonly clientSG: ec2.ISecurityGroup + readonly nodeSG: ec2.ISecurityGroup +} + +export class IndyNodeInstance extends Construct { + public readonly instance: ec2.Instance; + + constructor(scope: Construct, id: string, props: IndyNodeInstanceProps) { + super(scope, id); + + const { vpc, clientSG, nodeSG } = props; + + const clientNic: ec2.CfnInstance.NetworkInterfaceProperty = { + deviceIndex: "0", + groupSet: [clientSG.securityGroupId], + subnetId: vpc.privateSubnets[0].subnetId, + description: 'Client NIC', + }; + + const nodeNic: ec2.CfnInstance.NetworkInterfaceProperty = { + deviceIndex: "1", + groupSet: [nodeSG.securityGroupId], + subnetId: vpc.privateSubnets[0].subnetId, + description: 'Node NIC', + }; + + const instance = new ec2.Instance(this, 'Instance', { + vpc: vpc, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.LARGE), + machineImage: ec2.MachineImage.fromSsmParameter( + '/aws/service/canonical/ubuntu/server/focal/stable/current/amd64/hvm/ebs-gp2/ami-id' + ), + ssmSessionPermissions: true, + userData: ec2.UserData.custom(readFileSync("./lib/assets/user-data/steward.sh", "base64")), + blockDevices: [{ + deviceName: "/dev/sda1", + volume: ec2.BlockDeviceVolume.ebs(250, { + volumeType: ec2.EbsDeviceVolumeType.STANDARD, + encrypted: true + }) + }], + }); + + const cfnInstance = instance.node.defaultChild as ec2.CfnInstance; + cfnInstance.addPropertyDeletionOverride('SubnetId'); + cfnInstance.addPropertyDeletionOverride('SecurityGroupIds'); + cfnInstance.networkInterfaces = [ + clientNic, nodeNic + ] + + instance.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN); + + this.instance = instance; + } +} diff --git a/lib/indy/lib/indy-node-stack.ts b/lib/indy/lib/indy-node-stack.ts new file mode 100644 index 00000000..2f32877e --- /dev/null +++ b/lib/indy/lib/indy-node-stack.ts @@ -0,0 +1,66 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as ec2 from "aws-cdk-lib/aws-ec2"; + +import { IndyNodeInstance } from './constructs/indy-node-instance'; + +import { readFileSync } from "fs"; + +export class IndyNodeStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const vpc = new ec2.Vpc(this, "IndyVpc", { + ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'), + }); + + // SecurityGroup of Nodes for Clients + const clientSG = new ec2.SecurityGroup(this, 'ClientSG', {vpc}); + clientSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(9702), 'Allow 9702 from anywhere'); + + // SecurityGroup of Nodes for Other Nodes + const nodeSG = new ec2.SecurityGroup(this, 'NodeSG', {vpc}); + nodeSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(9701), 'Allow 9701 from anywhere'); + + const node1 = new IndyNodeInstance(this, "Node1",{vpc, clientSG, nodeSG}); + const node2 = new IndyNodeInstance(this, "Node2",{vpc, clientSG, nodeSG}); + const node3 = new IndyNodeInstance(this, "Node3",{vpc, clientSG, nodeSG}); + const node4 = new IndyNodeInstance(this, "Node4",{vpc, clientSG, nodeSG}); + + const trustee = new ec2.Instance(this, 'TrusteeInstance', { + vpc: vpc, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM), + machineImage: ec2.MachineImage.fromSsmParameter( + '/aws/service/canonical/ubuntu/server/focal/stable/current/amd64/hvm/ebs-gp2/ami-id' + ), + ssmSessionPermissions: true, + userData: ec2.UserData.custom(readFileSync("./lib/assets/user-data/trustee.sh", "base64")), + }); + trustee.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN); + + new cdk.CfnOutput(this, 'Node1InstanceId', { + value: node1.instance.instanceId, + exportName: 'Node1InstanceId', + }) + + new cdk.CfnOutput(this, 'Node2InstanceId', { + value: node2.instance.instanceId, + exportName: 'Node2InstanceId', + }) + + new cdk.CfnOutput(this, 'Node3InstanceId', { + value: node3.instance.instanceId, + exportName: 'Node3InstanceId', + }) + + new cdk.CfnOutput(this, 'Node4InstanceId', { + value: node4.instance.instanceId, + exportName: 'Node4InstanceId', + }) + + new cdk.CfnOutput(this, 'TrusteeInstanceId', { + value: trustee.instanceId, + exportName: 'TrusteeInstanceId', + }) + } +} diff --git a/lib/indy/package-lock.json b/lib/indy/package-lock.json new file mode 100644 index 00000000..0c769d99 --- /dev/null +++ b/lib/indy/package-lock.json @@ -0,0 +1,20 @@ +{ + "name": "aws-blockchain-node-runners-indy", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "aws-blockchain-node-runners-indy", + "version": "0.1.0", + "dependencies": { + "fs": "^0.0.1-security" + } + }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + } + } +} diff --git a/lib/indy/package.json b/lib/indy/package.json new file mode 100644 index 00000000..4dc47052 --- /dev/null +++ b/lib/indy/package.json @@ -0,0 +1,14 @@ +{ + "name": "aws-blockchain-node-runners-indy", + "version": "0.1.0", + "scripts": { + "build": "npx tsc", + "watch": "npx tsc -w", + "test": "npx jest", + "cdk": "npx cdk", + "scan-cdk": "npx cdk synth" + }, + "dependencies": { + "fs": "^0.0.1-security" + } +} From 88621a9fb16be15d75eb533f2e1bb4fa98913b51 Mon Sep 17 00:00:00 2001 From: Satsuki Fukazu Date: Mon, 18 Mar 2024 13:15:06 +0900 Subject: [PATCH 05/22] Change indy's build procedure to ansible --- .gitignore | 2 + lib/constructs/single-node.ts | 2 +- lib/indy/README.md | 208 +++++++++------- lib/indy/README_ja.md | 232 ++++++++++-------- lib/indy/ansible/ansible.cfg | 9 + lib/indy/ansible/inventory/group_vars/all.yml | 1 + lib/indy/ansible/inventory/inventory.yml | 33 +++ .../playbook/000_install_dependencies.yml | 6 + .../ansible/playbook/001_create_wallet.yml | 5 + .../ansible/playbook/002_validator_init.yml | 5 + .../playbook/003_create_genesis_files.yml | 5 + .../playbook/004_fetch_genesis_files.yml | 5 + lib/indy/ansible/playbook/005_run_indy.yml | 6 + .../playbook/997_trustee_instance_start.yml | 12 + .../playbook/998_trustee_instance_stop.yml | 12 + lib/indy/ansible/playbook/999_cleanup.yml | 54 ++++ lib/indy/ansible/playbook/site.yml | 9 + lib/indy/ansible/requirements.txt | 4 + .../create_genesis_files/defaults/main.yml | 2 + .../create_genesis_files/handlers/main.yml | 2 + .../tasks/create_genesis_file.yml | 81 ++++++ .../roles/create_genesis_files/tasks/main.yml | 1 + .../templates/stewards.csv.j2 | 5 + .../templates/trustee.csv.j2 | 4 + .../roles/create_genesis_files/vars/main.yml | 2 + .../roles/create_wallet/defaults/main.yml | 3 + .../roles/create_wallet/handlers/main.yml | 2 + .../create_wallet/tasks/install_indy_cli.yml | 16 ++ .../roles/create_wallet/tasks/main.yml | 2 + .../create_wallet/tasks/wallet_create.yml | 55 +++++ .../templates/walletCreate.batch.j2 | 6 + .../templates/walletOpen.batch.j2 | 4 + .../ansible/roles/create_wallet/vars/main.yml | 2 + .../fetch_genesis_files/defaults/main.yml | 2 + .../fetch_genesis_files/handlers/main.yml | 2 + .../roles/fetch_genesis_files/tasks/main.yml | 41 ++++ .../roles/fetch_genesis_files/vars/main.yml | 2 + .../install_dependencies/defaults/main.yml | 2 + .../install_dependencies/handlers/main.yml | 2 + .../roles/install_dependencies/tasks/main.yml | 66 +++++ .../templates/indy_config.py.j2 | 39 +++ .../roles/install_dependencies/vars/main.yml | 2 + .../ansible/roles/run_indy/defaults/main.yml | 2 + .../ansible/roles/run_indy/handlers/main.yml | 2 + .../ansible/roles/run_indy/tasks/main.yml | 7 + lib/indy/ansible/roles/run_indy/vars/main.yml | 2 + .../roles/validator_init/defaults/main.yml | 3 + .../roles/validator_init/handlers/main.yml | 2 + .../roles/validator_init/tasks/init.yml | 29 +++ .../roles/validator_init/tasks/main.yml | 1 + .../roles/validator_init/vars/main.yml | 2 + lib/indy/app.ts | 8 +- lib/indy/cdk.json | 79 +++--- lib/indy/doc/assets/Architecture.drawio | 2 +- lib/indy/lib/assets/user-data/steward.sh | 37 +-- lib/indy/lib/assets/user-data/trustee.sh | 34 --- lib/indy/lib/constructs/indy-node-instance.ts | 63 ----- .../constructs/indy-steward-node-instance.ts | 83 +++++++ .../constructs/indy-trustee-node-instance.ts | 46 ++++ lib/indy/lib/indy-node-stack.ts | 113 ++++----- lib/solana/README.md | 2 +- website/src/theme/Root.js | 6 +- 62 files changed, 1034 insertions(+), 444 deletions(-) create mode 100644 lib/indy/ansible/ansible.cfg create mode 100644 lib/indy/ansible/inventory/group_vars/all.yml create mode 100644 lib/indy/ansible/inventory/inventory.yml create mode 100644 lib/indy/ansible/playbook/000_install_dependencies.yml create mode 100644 lib/indy/ansible/playbook/001_create_wallet.yml create mode 100644 lib/indy/ansible/playbook/002_validator_init.yml create mode 100644 lib/indy/ansible/playbook/003_create_genesis_files.yml create mode 100644 lib/indy/ansible/playbook/004_fetch_genesis_files.yml create mode 100644 lib/indy/ansible/playbook/005_run_indy.yml create mode 100644 lib/indy/ansible/playbook/997_trustee_instance_start.yml create mode 100644 lib/indy/ansible/playbook/998_trustee_instance_stop.yml create mode 100644 lib/indy/ansible/playbook/999_cleanup.yml create mode 100644 lib/indy/ansible/playbook/site.yml create mode 100644 lib/indy/ansible/requirements.txt create mode 100644 lib/indy/ansible/roles/create_genesis_files/defaults/main.yml create mode 100644 lib/indy/ansible/roles/create_genesis_files/handlers/main.yml create mode 100644 lib/indy/ansible/roles/create_genesis_files/tasks/create_genesis_file.yml create mode 100644 lib/indy/ansible/roles/create_genesis_files/tasks/main.yml create mode 100644 lib/indy/ansible/roles/create_genesis_files/templates/stewards.csv.j2 create mode 100644 lib/indy/ansible/roles/create_genesis_files/templates/trustee.csv.j2 create mode 100644 lib/indy/ansible/roles/create_genesis_files/vars/main.yml create mode 100644 lib/indy/ansible/roles/create_wallet/defaults/main.yml create mode 100644 lib/indy/ansible/roles/create_wallet/handlers/main.yml create mode 100644 lib/indy/ansible/roles/create_wallet/tasks/install_indy_cli.yml create mode 100644 lib/indy/ansible/roles/create_wallet/tasks/main.yml create mode 100644 lib/indy/ansible/roles/create_wallet/tasks/wallet_create.yml create mode 100644 lib/indy/ansible/roles/create_wallet/templates/walletCreate.batch.j2 create mode 100644 lib/indy/ansible/roles/create_wallet/templates/walletOpen.batch.j2 create mode 100644 lib/indy/ansible/roles/create_wallet/vars/main.yml create mode 100644 lib/indy/ansible/roles/fetch_genesis_files/defaults/main.yml create mode 100644 lib/indy/ansible/roles/fetch_genesis_files/handlers/main.yml create mode 100644 lib/indy/ansible/roles/fetch_genesis_files/tasks/main.yml create mode 100644 lib/indy/ansible/roles/fetch_genesis_files/vars/main.yml create mode 100644 lib/indy/ansible/roles/install_dependencies/defaults/main.yml create mode 100644 lib/indy/ansible/roles/install_dependencies/handlers/main.yml create mode 100644 lib/indy/ansible/roles/install_dependencies/tasks/main.yml create mode 100644 lib/indy/ansible/roles/install_dependencies/templates/indy_config.py.j2 create mode 100644 lib/indy/ansible/roles/install_dependencies/vars/main.yml create mode 100644 lib/indy/ansible/roles/run_indy/defaults/main.yml create mode 100644 lib/indy/ansible/roles/run_indy/handlers/main.yml create mode 100644 lib/indy/ansible/roles/run_indy/tasks/main.yml create mode 100644 lib/indy/ansible/roles/run_indy/vars/main.yml create mode 100644 lib/indy/ansible/roles/validator_init/defaults/main.yml create mode 100644 lib/indy/ansible/roles/validator_init/handlers/main.yml create mode 100644 lib/indy/ansible/roles/validator_init/tasks/init.yml create mode 100644 lib/indy/ansible/roles/validator_init/tasks/main.yml create mode 100644 lib/indy/ansible/roles/validator_init/vars/main.yml delete mode 100644 lib/indy/lib/assets/user-data/trustee.sh delete mode 100644 lib/indy/lib/constructs/indy-node-instance.ts create mode 100644 lib/indy/lib/constructs/indy-steward-node-instance.ts create mode 100644 lib/indy/lib/constructs/indy-trustee-node-instance.ts diff --git a/.gitignore b/.gitignore index dd881760..063a4dcb 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ ha-nodes-deploy*.json *.OLD .env .idea + +lib/indy/ansible/.venv diff --git a/lib/constructs/single-node.ts b/lib/constructs/single-node.ts index bfb01a80..a0b8f1a7 100644 --- a/lib/constructs/single-node.ts +++ b/lib/constructs/single-node.ts @@ -96,7 +96,7 @@ export class SingleNodeConstruct extends cdkContructs.Construct { removalPolicy: cdk.RemovalPolicy.DESTROY, }); } - + new ec2.CfnVolumeAttachment(this, `data-volume${dataVolumeIndex}-attachment`, { // Device naming according to https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html diff --git a/lib/indy/README.md b/lib/indy/README.md index c676ce47..39cbf0d9 100644 --- a/lib/indy/README.md +++ b/lib/indy/README.md @@ -7,7 +7,7 @@ ![Architecture](./doc/assets/Architecture.png) This is a sample of building a Hyperledger Indy network on AWS. -The overall architecture is shown below, processing itself is performed by 4 Stewards (Validator Nodes), and network management is performed with Trustee. It consists of 4 EC2 instances for Steward and 1 EC2 instance for Trustee. +The overall architecture is shown below, processing itself is performed by 4 Stewards (Validator Nodes), and network management is performed with Trustee. It consists of 4 EC2 instances for Steward and 3 EC2 instances for Trustee. ## Solution Walkthrough @@ -27,8 +27,6 @@ npm install ### Deploy Indy Nodes -Indy Network is built using 4 EC2 instances for Steward and 1 EC2 instance for Trustee. Various information such as DID is acquired in the following procedure, copied by referring to [this community spreadsheet](https://docs.google.com/spreadsheets/d/1LDduIeZp7pansd9deXeVSqGgdf0VdAHNMc7xYli3QAY/edit#gid=0). - #### Building resources 1. Install npm dependency packages @@ -54,122 +52,150 @@ npx cdk bootstrap npx cdk deploy Outputs: -IndyNodeStack.Node1InstanceId = i-xxxxxxxxxxxxxxxxx -IndyNodeStack.Node2InstanceId = i-xxxxxxxxxxxxxxxxx -IndyNodeStack.Node3InstanceId = i-xxxxxxxxxxxxxxxxx -IndyNodeStack.Node4InstanceId = i-xxxxxxxxxxxxxxxxx -IndyNodeStack.TrusteeInstanceId = i-xxxxxxxxxxxxxxxxx +IndyNetworkStack.AnsibleFileTransferBucketName = 111122223333-ansible-file-transfer-bucket +IndyNetworkStack.steward1steward1InstanceId2F9F8910 = i-1234567890abcdef1 +IndyNetworkStack.steward2steward2InstanceId995438F2 = i-1234567890abcdef2 +IndyNetworkStack.steward3steward3InstanceIdB5D10BBE = i-1234567890abcdef3 +IndyNetworkStack.steward4steward4InstanceIdB3DD7753 = i-1234567890abcdef4 +IndyNetworkStack.trustee1trustee1InstanceId8FDDE052 = i-1234567890abcdef5 +IndyNetworkStack.trustee2trustee2InstanceIdE12079EA = i-1234567890abcdef6 +IndyNetworkStack.trustee3trustee3InstanceId508C4E4C = i-1234567890abcdef7 ``` **NOTE:** User data for the Steward instance is created by referring to [the Community Docs](https://github.com/hyperledger/indy-node/blob/main/docs/source/install-docs/AWS-NodeInstall-20.04.md). -#### Setting up Trustee +#### Building an environment using Ansible -Log in to the Trustee instance via Session Manager from the EC2 (or Systems Manager) console and generate Trustee/Steward DIDs. -​ +When running on a Mac, set the following environment variables. -```bash -cd / -./indy-cli-rs -​ -# Perform the following commands 3 times for Trustee and 4 times for Steward -wallet create key= -wallet open key= -did new seed= -wallet close -``` +> export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES -#### Setting up Steward -Log in to the Steward instance via Session Manager from the EC2 (or Systems Manager) console and generate Validator verkey, BLS key, and BLS POP. - -```bash -sudo init_indy_node 9701 9702 -``` +##### Preparing for Ansible -**NOTE:** Here, Steward represents Validator Node ([reference information](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md#32-validator-node-installation)). +- Create a Python virtual environment and install ansible + ``` + $cd ansible + $ Python3 -m venv.venv + $source.venv/bin/activate + ``` -#### Generating Genesis files + ``` + $ pip install -r requirements.txt + ``` -1. Download each sheet (Stewards, Trustees) containing the information generated in the steps so far - - File → Download → .csv -2. Save `trustees.csv` and `stewards.csv` to Trustee instance +##### Ansible and Session Manager -**NOTE:** To transfer a locally downloaded CSV file via Session Manager, use the Session Manager Plugin in addition to the AWS CLI to transfer it with the following command ([reference information](https://dev.classmethod.jp/articles/ssm-session-manager-support-for-tunneling-ssh-scp-on-windows10/)). +- In order to achieve SSH access to the EC2 instance using Session Manager, refer to [Install the Session Manager plugin for the AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) and install the Session Manager Plugin. By using the Session Manager, deployment by Ansible to an EC2 instance of a private subnet that cannot be accessed from the internet is possible without setting a security group. -```bash -scp -i ec2-user@:~/ -``` +- Installs a ansible plug-in for SSH access to EC2 using the AWS Systems Manager Session Manager. + ``` + $ ansible-galaxy collection install community.aws + ``` -​ -3. Generate Genesis files +##### Describe instance information to be built in inventory.yml -Using the above two CSV files, generate Genesis files (`pool_transactions_genesis`, `domain_transactions_genesis`) with `genesis_from_files.py` +- Create an indentory file containing information on the EC2 instance that will build the environment. Enter the instance ID described in the CDK output results in the settings column for each node. The value of `indyNetworkStack.ansibleFileTransferBucketName` described in CDK output results is inputted to `ansible_aws_ssm_bucket_name`. When Ansible transfers files to the target host, the Amazon Simple Storage Service (Amazon S3) bucket specified here is used. -```bash -cd ~/ -wget -nc https://raw.githubusercontent.com/sovrin-foundation/steward-tools/master/create_genesis/genesis_from_files.py -​ -chmod +x genesis_from_files.py -./genesis_from_files.py --stewards stewards.csv --trustees trustees.csv - -DEBUG:root:new line check for file: ./pool_transactions_genesis -INFO:root:Starting ledger... -INFO:root:Recovering tree from transaction log -INFO:root:Recovered tree in 0.00010979999979099375 seconds -DEBUG:root:new line check for file: ./domain_transactions_genesis -INFO:root:Starting ledger... -INFO:root:Recovering tree from transaction log -INFO:root:Recovered tree in 8.670999977766769e-05 seconds -``` + ``` + $ vi inventory/inventory.yml + all: + hosts: + steward1: + ansible_aws_ssm_instance_id: i-1234567890abcdef1 + steward2: + ansible_aws_ssm_instance_id: i-1234567890abcdef2 + steward3: + ansible_aws_ssm_instance_id: i-1234567890abcdef3 + steward4: + ansible_aws_ssm_instance_id: i-1234567890abcdef4 + trustee1: + ansible_aws_ssm_instance_id: i-1234567890abcdef5 + trustee2: + ansible_aws_ssm_instance_id: i-1234567890abcdef6 + trustee3: + ansible_aws_ssm_instance_id: i-1234567890abcdef7 + children: + steward: + hosts: + steward[1:4]: + trustee: + hosts: + trustee1 -#### Setting up Nodes + vars: + ansible_connection: aws_ssm + ansible_aws_ssm_region: aa-example-1 + ansible_aws_ssm_s3_addressing_style: virtual + ansible_aws_ssm_bucket_name: 111122223333-ansible-file-transfer-bucket + ``` -Start up each Validator Node (Steward) -1. Download Genesis files and set permissions +##### Ansible parameter settings +Define the parameters referred to by Ansible in the configuration file. Set Indy's network name -Download or copy Genesis files to Node instance. Then, set the permissions for all files under `/var/lib/indy/` to `indy` ([reference information](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md#iv-create-and-distribute-genesis-transaction-files)​). - -```bash -cd /var/lib/indy/sample-network - -# Save domain_transactions_genesis and pool_transactions_genesis -# sudo curl -o domain_transactions_genesis -# sudo curl -o pool_transactions_genesis - -sudo chown -R indy:indy ../ ``` - -**NOTE:** The directory name of `/var/lib/indy/sample-network` is `NETWORK_NAME` set in `lib/indy/lib/assets/user-data/steward.sh`. - -2. Start indy-node and check status - -```bash -sudo systemctl start indy-node -sudo systemctl status indy-node -sudo systemctl enable indy-node -​ -sudo validator-info +$ vi inventory/group_vars/all.yml +INDY_NETEORK_NAME: sample-network ``` -**NOTE:** [reference information](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md#35-add-node-to-a-pool) +##### Execute environment construction with Ansible + +- Use ansible's `ping` module to confirm that ansible can connect to the instance set in inventory/inventory.yml + + ``` + $ ansible -m ping all -i inventory/inventory.yml + steward2 | SUCCESS => { + "changed": false, + "ping": "pong" + } + steward3 | SUCCESS => { + "changed": false, + "ping": "pong" + } + trustee1 | SUCCESS => { + "changed": false, + "ping": "pong" + } + steward4 | SUCCESS => { + "changed": false, + "ping": "pong" + } + trustee2 | SUCCESS => { + "changed": false, + "ping": "pong" + } + trustee3 | SUCCESS => { + "changed": false, + "ping": "pong" + } + steward1 | SUCCESS => { + "changed": false, + "ping": "pong" + } + ``` + +- Execute Hyperledger Indy environment construction for target EC2 instances defined in `inventory/inventory.yml` in ansible + ``` + $ ansible-playbook playbook/site.yml + ``` + #### reference information -- [Buidling Indy Network](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md) -- [Setting up EC2 instances for Indy Node](https://github.com/hyperledger/indy-node/blob/main/docs/source/install-docs/AWS-NodeInstall-20.04.md) -- [Setting up Indy Node](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md) -​ +- [Buidling Indy Network](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md) +- [Setting up EC2 instances for Indy Node](https://github.com/hyperledger/indy-node/blob/main/docs/source/install-docs/AWS-NodeInstall-20.04.md) +- [Setting up Indy Node](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md) + ​ ### Considerations Matters to be examined in additional development etc. when using this sample are described. -- Change the instance type to M - - Currently, it is a T instance, but in production environments, it is recommended to change to M -- Fix the security group for Node NICs attached to Steward (Validator Node) - - Limit source IPs to node IPs of other nodes (currently open within VPC and can also be accessed by clients) - - Fix Node's private IP -- If necessary, change the subnet to which the node belongs to a public subnet -- Make Steward and Node separate instances +- Change the instance type to M + - Currently, it is a T instance, but in production environments, it is recommended to change to M +- Fix the security group for Node NICs attached to Steward (Validator Node) + - Limit source IPs to node IPs of other nodes (currently open within VPC and can also be accessed by clients) + - Fix Node's private IP +- If necessary, change the subnet to which the node belongs to a public subnet +- Make Steward and Node separate instances diff --git a/lib/indy/README_ja.md b/lib/indy/README_ja.md index 8cf47753..226188da 100644 --- a/lib/indy/README_ja.md +++ b/lib/indy/README_ja.md @@ -7,7 +7,7 @@ ![Architecture](./doc/assets/Architecture.png) Hyperledger Indy のネットワークを AWS 上に構築するサンプルである。 -全体像は下図の通り、処理自体は 4 つの Steward (Validator Node) で行われ、ネットワークの管理は Trustee で行われる。実体は Steward 用の 4 つの EC2 インスタンスと、Trustee 用の 1 つの EC2 インスタンスである。 +全体像は下図の通り、処理自体は 4 つの Steward (Validator Node) で行われ、ネットワークの管理は Trustee で行われる。実体は Steward 用の 4 つの EC2 インスタンスと、Trustee 用の 3 つの EC2 インスタンスである。 ## Solution Walkthrough @@ -27,8 +27,6 @@ npm install ### Deploy Indy Nodes -Indy Network を Steward 用の4つの EC2 インスタンスと、Trustee 用の1つの EC2 インスタンスを用いて構築する。下記手順の中で DID など各種情報を取得し、それらを[こちらの Community のスプレッドシート](https://docs.google.com/spreadsheets/d/1LDduIeZp7pansd9deXeVSqGgdf0VdAHNMc7xYli3QAY/edit#gid=0)を参考にコピーしてまとめる。 - #### リソースの構築 1. npm の依存パッケージをインストール @@ -54,123 +52,143 @@ npx cdk bootstrap npx cdk deploy Outputs: -IndyNodeStack.Node1InstanceId = i-xxxxxxxxxxxxxxxxx -IndyNodeStack.Node2InstanceId = i-xxxxxxxxxxxxxxxxx -IndyNodeStack.Node3InstanceId = i-xxxxxxxxxxxxxxxxx -IndyNodeStack.Node4InstanceId = i-xxxxxxxxxxxxxxxxx -IndyNodeStack.TrusteeInstanceId = i-xxxxxxxxxxxxxxxxx +IndyNetworkStack.AnsibleFileTransferBucketName = 111122223333-ansible-file-transfer-bucket +IndyNetworkStack.steward1steward1InstanceId2F9F8910 = i-1234567890abcdef1 +IndyNetworkStack.steward2steward2InstanceId995438F2 = i-1234567890abcdef2 +IndyNetworkStack.steward3steward3InstanceIdB5D10BBE = i-1234567890abcdef3 +IndyNetworkStack.steward4steward4InstanceIdB3DD7753 = i-1234567890abcdef4 +IndyNetworkStack.trustee1trustee1InstanceId8FDDE052 = i-1234567890abcdef5 +IndyNetworkStack.trustee2trustee2InstanceIdE12079EA = i-1234567890abcdef6 +IndyNetworkStack.trustee3trustee3InstanceId508C4E4C = i-1234567890abcdef7 ``` **NOTE:** Steward インスタンスのユーザーデータは [Community の Doc](https://github.com/hyperledger/indy-node/blob/main/docs/source/install-docs/AWS-NodeInstall-20.04.md) を参考に作成している。 -#### Trustee の設定 - -EC2 (もしくは Systems Manager) のコンソールから Session Manager 経由で Trustee インスタンスにログインし、Trustee/Steward の DID を生成する。 -​ - -```bash -cd / -./indy-cli-rs -​ -# 下記の操作を Trustee 用に 3回、Steward 用に 4回の計7回実施 -wallet create key= -wallet open key= -did new seed= -wallet close -``` - -#### Steward の設定​ - -EC2 (もしくは Systems Manager) のコンソールから Session Manager 経由で Steward インスタンスにログインして、Validator verkey, BLS key, BLS POP を生成する。 - -```bash -sudo init_indy_node 9701 9702 -``` - -**NOTE:** ここでは Steward は Validator Node のことを表す ([参考情報](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md#32-validator-node-installation))。 - -#### Genesis Files の生成 - -1. これまでの手順で生成した情報を記載したスプレッドシートの各シート (Stewards / Trustees) をダウンロード - - File → Download → .csv -2. `trustees.csv` / `stewards.csv` を Trustee インスタンスに保存 - -**NOTE:** ローカルにダウンロードした CSV ファイルを Session Manager 経由で転送するには AWS CLI に加えて Session Manager Plugin を用いて下記コマンドで転送する ([参考情報](https://dev.classmethod.jp/articles/ssm-session-manager-support-for-tunneling-ssh-scp-on-windows10/))。 - -```bash -scp -i ec2-user@:~/ -``` - -​ -3. Genesis Files 生成 - -上記 2 つの CSV ファイルを用いて、`genesis_from_files.py` によって Genesis files (`pool_transactions_genesis`, `domain_transactions_genesis`) を生成する +# Ansibleを使用した環境構築 + +Macで実行する場合は次の環境変数を設定する。 + +> export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES + +## Ansibleの事前準備 + +- pythonの仮想環境を作成しansibleを導入する + ``` + $ cd ansible + $ python3 -m venv .venv + $ source .venv/bin/activate + ``` + + ``` + $ pip install -r requirements.txt + ``` + +## AnsibleとSession Manager + +- EC2 Instanceに対してSession Managerを使用したSSHアクセスを実現するために、 [Install the Session Manager plugin for the AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) を参照して、Session Manager Pluginをインストールする。Session Managerを使用することでセキュリティグループの設定をすることなく、インターネットからアクセスできないPrivate SubnetのEC2 Instanceに対してAnsibleによるデプロイが可能となる。 + +- AnsibleがAWS Systems Manager Session Managerを使用してEC2にSSHログインするためのプラグインをインストールする。 + ``` + $ ansible-galaxy collection install community.aws + ``` + +## 構築対象のインスタンス情報をInventory.ymlに記載する +- 環境構築を行うEC2インスタンスの情報を記載したIndentoryファイルを作成する。CDKの出力結果に記載されているインスタンスのIDをそれぞれのノードの設定欄に記入する。 `ansible_aws_ssm_bucket_name` には CDKの出力結果に記載されている `IndyNetworkStack.AnsibleFileTransferBucketName` の値を入力する。Ansibleが対象のホストに対してファイルを転送する時に、ここで指定したAmazon Simple Storage Service(Amazon S3) Bucketを使用する。 + ``` + $ vi inventory/inventory.yml + all: + hosts: + steward1: + ansible_aws_ssm_instance_id: i-1234567890abcdef1 + steward2: + ansible_aws_ssm_instance_id: i-1234567890abcdef2 + steward3: + ansible_aws_ssm_instance_id: i-1234567890abcdef3 + steward4: + ansible_aws_ssm_instance_id: i-1234567890abcdef4 + trustee1: + ansible_aws_ssm_instance_id: i-1234567890abcdef5 + trustee2: + ansible_aws_ssm_instance_id: i-1234567890abcdef6 + trustee3: + ansible_aws_ssm_instance_id: i-1234567890abcdef7 + children: + steward: + hosts: + steward[1:4]: + trustee: + hosts: + trustee1 + + vars: + ansible_connection: aws_ssm + ansible_aws_ssm_region: aa-example-1 + ansible_aws_ssm_s3_addressing_style: virtual + ansible_aws_ssm_bucket_name: 111122223333-ansible-file-transfer-bucket + ``` + +## Ansibleの設定 +Ansibleが参照するパラメータを設定ファイルで定義する。 -```bash -cd ~/ -wget -nc https://raw.githubusercontent.com/sovrin-foundation/steward-tools/master/create_genesis/genesis_from_files.py -​ -chmod +x genesis_from_files.py -./genesis_from_files.py --stewards stewards.csv --trustees trustees.csv - -DEBUG:root:new line check for file: ./pool_transactions_genesis -INFO:root:Starting ledger... -INFO:root:Recovering tree from transaction log -INFO:root:Recovered tree in 0.00010979999979099375 seconds -DEBUG:root:new line check for file: ./domain_transactions_genesis -INFO:root:Starting ledger... -INFO:root:Recovering tree from transaction log -INFO:root:Recovered tree in 8.670999977766769e-05 seconds ``` - -#### Node の設定 - -各 Validator Node (Steward) の立ち上げを行う - -1. Genesis Files のダウンロードと各種ファイルの権限設定 - -Genesis Files を Node インスタンスにダウンロードもしくはコピーする。そして、`/var/lib/indy/` 配下の全ファイルの権限を indy に設定する ([参考情報](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md#iv-create-and-distribute-genesis-transaction-files)​)。 - -```bash -cd /var/lib/indy/sample-network - -# domain_transactions_genesis と pool_transactions_genesis を保存 -# sudo curl -o domain_transactions_genesis -# sudo curl -o pool_transactions_genesis - -sudo chown -R indy:indy ../ +$ vi inventory/group_vars/all.yml +INDY_NETEORK_NAME: sample-network ``` - -**NOTE:** `/var/lib/indy/sample-network` のディレクトリ名 は `lib/indy/lib/assets/user-data/steward.sh` で設定している `NETWORK_NAME` である。 - -2. indy-node の起動と動作確認 - -```bash -sudo systemctl start indy-node -sudo systemctl status indy-node -sudo systemctl enable indy-node -​ -sudo validator-info -``` - -**NOTE:** [ドキュメント](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md#35-add-node-to-a-pool)の 3.5.2 以降を実施している ​ - -#### 参考情報 - +## Ansibleによる環境構築の実行 + +- inventory/inventory.ymlで設定したインスタンスにansibleが接続できることをansibleの `ping` モジュールを使用して確認する + ``` + $ ansible -m ping all -i inventory/inventory.yml + steward2 | SUCCESS => { + "changed": false, + "ping": "pong" + } + steward3 | SUCCESS => { + "changed": false, + "ping": "pong" + } + trustee1 | SUCCESS => { + "changed": false, + "ping": "pong" + } + steward4 | SUCCESS => { + "changed": false, + "ping": "pong" + } + trustee2 | SUCCESS => { + "changed": false, + "ping": "pong" + } + trustee3 | SUCCESS => { + "changed": false, + "ping": "pong" + } + steward1 | SUCCESS => { + "changed": false, + "ping": "pong" + } + ``` + +- ansibleで `inventory/inventory.yml` で定義した対象のEC2インスタンスに対してHyperledger Indyの環境構築を実行する + ``` + $ ansible-playbook playbook/site.yml + ``` + + +## 参考情報 - [Indy Network の構築](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md) - [Indy Node のための EC2 セットアップ](https://github.com/hyperledger/indy-node/blob/main/docs/source/install-docs/AWS-NodeInstall-20.04.md) - [Indy Node のセットアップ](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md) -​ ### 考慮事項 本サンプルを利用するにあたり追加開発などで検討する事項を記載する。 -- インスタンスタイプを M 系に変更 - - 現状は T 系インスタンスであるが本番環境では M 系などへの変更を推奨 -- Steward (Validator Node) にアタッチされている Node NIC の Security Group を修正 - - Source IP を他ノードの Node IP に制限する (現在は VPC 内にオープンになっており、Client からもアクセスできる) - - Node の Private IP を固定 -- 必要に応じて Node の属するサブネットを Public Subnet にする -- Steward と Node を別インスタンスにする +- インスタンスタイプを M 系に変更 + - 現状は T 系インスタンスであるが本番環境では M 系などへの変更を推奨 +- Steward (Validator Node) にアタッチされている Node NIC の Security Group を修正 + - Source IP を他ノードの Node IP に制限する (現在は VPC 内にオープンになっており、Client からもアクセスできる) + - Node の Private IP を固定 +- 必要に応じて Node の属するサブネットを Public Subnet にする +- Steward と Node を別インスタンスにする diff --git a/lib/indy/ansible/ansible.cfg b/lib/indy/ansible/ansible.cfg new file mode 100644 index 00000000..db0c46b9 --- /dev/null +++ b/lib/indy/ansible/ansible.cfg @@ -0,0 +1,9 @@ +# config file for ansible -- https://ansible.com/ +# =============================================== + +[defaults] +inventory = ./inventory/inventory.yml +host_key_checking = False +deprecation_warnings=False +roles_path: ./roles +interpreter_python: /usr/bin/python3 diff --git a/lib/indy/ansible/inventory/group_vars/all.yml b/lib/indy/ansible/inventory/group_vars/all.yml new file mode 100644 index 00000000..4af85a59 --- /dev/null +++ b/lib/indy/ansible/inventory/group_vars/all.yml @@ -0,0 +1 @@ +INDY_NETEORK_NAME: sample-network diff --git a/lib/indy/ansible/inventory/inventory.yml b/lib/indy/ansible/inventory/inventory.yml new file mode 100644 index 00000000..e90b7a8d --- /dev/null +++ b/lib/indy/ansible/inventory/inventory.yml @@ -0,0 +1,33 @@ +all: + hosts: + steward1: + ansible_aws_ssm_instance_id: i-1234567890abcdef1 + steward2: + ansible_aws_ssm_instance_id: i-1234567890abcdef2 + steward3: + ansible_aws_ssm_instance_id: i-1234567890abcdef3 + steward4: + ansible_aws_ssm_instance_id: i-1234567890abcdef4 + trustee1: + ansible_aws_ssm_instance_id: i-1234567890abcdef5 + trustee2: + ansible_aws_ssm_instance_id: i-1234567890abcdef6 + trustee3: + ansible_aws_ssm_instance_id: i-1234567890abcdef7 + children: + steward: + hosts: + steward[1:4]: + trustee: + hosts: + trustee[1:3]: + indy: + children: + steward: + trustee: + + vars: + ansible_connection: aws_ssm + ansible_aws_ssm_region: ap-northeast-1 + ansible_aws_ssm_s3_addressing_style: virtual + ansible_aws_ssm_bucket_name: 111122223333-ansible-file-transfer-bucket diff --git a/lib/indy/ansible/playbook/000_install_dependencies.yml b/lib/indy/ansible/playbook/000_install_dependencies.yml new file mode 100644 index 00000000..d1be0225 --- /dev/null +++ b/lib/indy/ansible/playbook/000_install_dependencies.yml @@ -0,0 +1,6 @@ +- name: install dependencies + hosts: + - indy + become: yes + roles: + - install_dependencies diff --git a/lib/indy/ansible/playbook/001_create_wallet.yml b/lib/indy/ansible/playbook/001_create_wallet.yml new file mode 100644 index 00000000..3d37d110 --- /dev/null +++ b/lib/indy/ansible/playbook/001_create_wallet.yml @@ -0,0 +1,5 @@ +- name: Create Wallet + hosts: + - indy + roles: + - create_wallet diff --git a/lib/indy/ansible/playbook/002_validator_init.yml b/lib/indy/ansible/playbook/002_validator_init.yml new file mode 100644 index 00000000..457515ff --- /dev/null +++ b/lib/indy/ansible/playbook/002_validator_init.yml @@ -0,0 +1,5 @@ +- name: Validator Init + hosts: + - steward + roles: + - validator_init diff --git a/lib/indy/ansible/playbook/003_create_genesis_files.yml b/lib/indy/ansible/playbook/003_create_genesis_files.yml new file mode 100644 index 00000000..0a0a87d9 --- /dev/null +++ b/lib/indy/ansible/playbook/003_create_genesis_files.yml @@ -0,0 +1,5 @@ +- name: Create Genesis FIles + hosts: + - trustee1 # Run on a representative device + roles: + - create_genesis_files diff --git a/lib/indy/ansible/playbook/004_fetch_genesis_files.yml b/lib/indy/ansible/playbook/004_fetch_genesis_files.yml new file mode 100644 index 00000000..11218f1d --- /dev/null +++ b/lib/indy/ansible/playbook/004_fetch_genesis_files.yml @@ -0,0 +1,5 @@ +- name: Fetch Genesis FIles + hosts: + - steward + roles: + - fetch_genesis_files diff --git a/lib/indy/ansible/playbook/005_run_indy.yml b/lib/indy/ansible/playbook/005_run_indy.yml new file mode 100644 index 00000000..3bbdd97e --- /dev/null +++ b/lib/indy/ansible/playbook/005_run_indy.yml @@ -0,0 +1,6 @@ +- name: Start indy service + become: yes + hosts: + - steward + roles: + - run_indy diff --git a/lib/indy/ansible/playbook/997_trustee_instance_start.yml b/lib/indy/ansible/playbook/997_trustee_instance_start.yml new file mode 100644 index 00000000..d425620b --- /dev/null +++ b/lib/indy/ansible/playbook/997_trustee_instance_start.yml @@ -0,0 +1,12 @@ +- name: Start the trustee instances + hosts: + - localhost + gather_facts: false + tasks: + - name: Start the trustee instances + amazon.aws.ec2_instance: + instance_ids: "{{ hostvars[item]['ansible_aws_ssm_instance_id'] }}" + # region: '{{ region }}' + state: running + wait: True + loop: "{{ groups['trustee'] }}" diff --git a/lib/indy/ansible/playbook/998_trustee_instance_stop.yml b/lib/indy/ansible/playbook/998_trustee_instance_stop.yml new file mode 100644 index 00000000..e9d63f8c --- /dev/null +++ b/lib/indy/ansible/playbook/998_trustee_instance_stop.yml @@ -0,0 +1,12 @@ +- name: Stop the trustee instances + hosts: + - localhost + gather_facts: false + tasks: + - name: Stop the trustee instances + amazon.aws.ec2_instance: + instance_ids: "{{ hostvars[item]['ansible_aws_ssm_instance_id'] }}" + # region: '{{ region }}' + state: stopped + wait: True + loop: "{{ groups['trustee'] }}" diff --git a/lib/indy/ansible/playbook/999_cleanup.yml b/lib/indy/ansible/playbook/999_cleanup.yml new file mode 100644 index 00000000..680b19a6 --- /dev/null +++ b/lib/indy/ansible/playbook/999_cleanup.yml @@ -0,0 +1,54 @@ +- name: Remove Secrets Manager + hosts: + - localhost + tasks: + - name: remove indy-seed from secrets manager + community.aws.secretsmanager_secret: + name: "{{ nodename }}-indy-seed" + state: absent + secret_type: string + secret: "{{ lookup('community.general.random_string', length=32, special=false) }}" + overwrite: false + with_items: + - steward1 + - steward2 + - steward3 + - steward4 + - trustee1 + - trustee2 + - trustee3 + loop_control: + loop_var: nodename + + - name: remove indy-did from secrets manager + community.aws.secretsmanager_secret: + name: "{{ nodename }}-indy-did" + state: absent + secret_type: string + secret: "{{ lookup('community.general.random_string', length=32, special=false) }}" + overwrite: false + with_items: + - steward1 + - steward2 + - steward3 + - steward4 + - trustee1 + - trustee2 + - trustee3 + loop_control: + loop_var: nodename + + - name: remove indy-nodeInfo from secrets manager + community.aws.secretsmanager_secret: + name: "{{ nodename }}-indy-nodeInfo" + state: absent + secret_type: string + secret: "{{ lookup('community.general.random_string', length=32, special=false) }}" + overwrite: false + with_items: + - steward1 + - steward2 + - steward3 + - steward4 + loop_control: + loop_var: nodename diff --git a/lib/indy/ansible/playbook/site.yml b/lib/indy/ansible/playbook/site.yml new file mode 100644 index 00000000..69f68c39 --- /dev/null +++ b/lib/indy/ansible/playbook/site.yml @@ -0,0 +1,9 @@ +--- +- import_playbook: 997_trustee_instance_start.yml +- import_playbook: 000_install_dependencies.yml +- import_playbook: 001_create_wallet.yml +- import_playbook: 002_validator_init.yml +- import_playbook: 003_create_genesis_files.yml +- import_playbook: 004_fetch_genesis_files.yml +- import_playbook: 005_run_indy.yml +- import_playbook: 998_trustee_instance_stop.yml diff --git a/lib/indy/ansible/requirements.txt b/lib/indy/ansible/requirements.txt new file mode 100644 index 00000000..174acaf3 --- /dev/null +++ b/lib/indy/ansible/requirements.txt @@ -0,0 +1,4 @@ +ansible==8.7.0 +ansible-core==2.15.9 +boto3==1.34.49 +botocore==1.34.49 diff --git a/lib/indy/ansible/roles/create_genesis_files/defaults/main.yml b/lib/indy/ansible/roles/create_genesis_files/defaults/main.yml new file mode 100644 index 00000000..5723c3b9 --- /dev/null +++ b/lib/indy/ansible/roles/create_genesis_files/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for create_genesis_files diff --git a/lib/indy/ansible/roles/create_genesis_files/handlers/main.yml b/lib/indy/ansible/roles/create_genesis_files/handlers/main.yml new file mode 100644 index 00000000..8a9ffa42 --- /dev/null +++ b/lib/indy/ansible/roles/create_genesis_files/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for create_genesis_files diff --git a/lib/indy/ansible/roles/create_genesis_files/tasks/create_genesis_file.yml b/lib/indy/ansible/roles/create_genesis_files/tasks/create_genesis_file.yml new file mode 100644 index 00000000..4d13e0ae --- /dev/null +++ b/lib/indy/ansible/roles/create_genesis_files/tasks/create_genesis_file.yml @@ -0,0 +1,81 @@ +--- +- name: remove old genesis files + file: + path: "{{ item }}" + state: absent + with_items: + - /tmp/pool_transactions_genesis + - /tmp/domain_transactions_genesis + +# tasks file for create_genesis_file +- name: get nodeinfo from secrets manager + set_fact: + stewardNodeInfo1: "{{ lookup('amazon.aws.aws_secret', 'steward1-indy-nodeInfo', on_denied='warn')}}" + stewardNodeInfo2: "{{ lookup('amazon.aws.aws_secret', 'steward2-indy-nodeInfo', on_denied='warn')}}" + stewardNodeInfo3: "{{ lookup('amazon.aws.aws_secret', 'steward3-indy-nodeInfo', on_denied='warn')}}" + stewardNodeInfo4: "{{ lookup('amazon.aws.aws_secret', 'steward4-indy-nodeInfo', on_denied='warn')}}" + +- name: get didinfo from secrets manager + set_fact: + stewardDidInfo1: "{{ lookup('amazon.aws.aws_secret', 'steward1-indy-did', on_denied='warn')}}" + stewardDidInfo2: "{{ lookup('amazon.aws.aws_secret', 'steward2-indy-did', on_denied='warn')}}" + stewardDidInfo3: "{{ lookup('amazon.aws.aws_secret', 'steward3-indy-did', on_denied='warn')}}" + stewardDidInfo4: "{{ lookup('amazon.aws.aws_secret', 'steward4-indy-did', on_denied='warn')}}" + +- name: get trustee info from secrets manager + set_fact: + trusteeDidInfo1: "{{ lookup('amazon.aws.aws_secret', 'trustee1-indy-did', on_denied='warn')}}" + trusteeDidInfo2: "{{ lookup('amazon.aws.aws_secret', 'trustee2-indy-did', on_denied='warn')}}" + trusteeDidInfo3: "{{ lookup('amazon.aws.aws_secret', 'trustee3-indy-did', on_denied='warn')}}" + +- name: make stewards.csv + template: + src: stewards.csv.j2 + dest: /tmp/stewards.csv + +- name: make trustee.csv + template: + src: trustee.csv.j2 + dest: /tmp/trustee.csv + +- name: wget genesis_from_files.py + get_url: + url: https://raw.githubusercontent.com/sovrin-foundation/steward-tools/master/create_genesis/genesis_from_files.py + dest: /tmp/genesis_from_files.py + mode: 0755 + +- name: execute genesis_from_files.py + shell: /tmp/genesis_from_files.py --stewards /tmp/stewards.csv --trustees /tmp/trustee.csv + args: + chdir: /tmp + register: genesis_result + +- name: fetch pool_transactions_genesis + ansible.builtin.fetch: + src: /tmp/pool_transactions_genesis + dest: /tmp/pool_transactions_genesis + flat: yes + +- name: fetch domain_transactions_genesis + ansible.builtin.fetch: + src: /tmp/domain_transactions_genesis + dest: /tmp/domain_transactions_genesis + flat: yes + +- amazon.aws.aws_caller_info: + register: aws_caller_info + +- name: join s3 bucket name + ansible.builtin.set_fact: + S3_BUCKET_NAME: "{{ aws_caller_info.account }}-ansible-file-transfer-bucket" + +- name: domain_transactions_genesis into s3 bucket + amazon.aws.s3_object: + bucket: "{{ S3_BUCKET_NAME }}" + object: "/genesis/{{ item }}" + src: "/tmp/{{ item }}" + mode: put + delegate_to: localhost + with_items: + - pool_transactions_genesis + - domain_transactions_genesis diff --git a/lib/indy/ansible/roles/create_genesis_files/tasks/main.yml b/lib/indy/ansible/roles/create_genesis_files/tasks/main.yml new file mode 100644 index 00000000..152403b1 --- /dev/null +++ b/lib/indy/ansible/roles/create_genesis_files/tasks/main.yml @@ -0,0 +1 @@ +- ansible.builtin.import_tasks: create_genesis_file.yml diff --git a/lib/indy/ansible/roles/create_genesis_files/templates/stewards.csv.j2 b/lib/indy/ansible/roles/create_genesis_files/templates/stewards.csv.j2 new file mode 100644 index 00000000..c5065cc4 --- /dev/null +++ b/lib/indy/ansible/roles/create_genesis_files/templates/stewards.csv.j2 @@ -0,0 +1,5 @@ +Steward name,Validator alias,Node IP address,Node port,Client IP address,Client port,Validator verkey,Validator BLS key,Validator BLS POP,Steward DID,Steward verkey +StewardWallet1,{{ stewardNodeInfo1.nodeStackName }},{{ stewardNodeInfo1.nodeIpAddress }},9701,0.0.0.0,9702,{{ stewardNodeInfo1.verificationKeyForClientStack }},{{ stewardNodeInfo1.blsPublicKey }},{{ stewardNodeInfo1.proofOfPossessionForBlsKey }},{{ stewardDidInfo1.did }},{{ stewardDidInfo1.verkey}} +StewardWallet2,{{ stewardNodeInfo2.nodeStackName }},{{ stewardNodeInfo2.nodeIpAddress }},9701,0.0.0.0,9702,{{ stewardNodeInfo2.verificationKeyForClientStack }},{{ stewardNodeInfo2.blsPublicKey }},{{ stewardNodeInfo2.proofOfPossessionForBlsKey }},{{ stewardDidInfo2.did }},{{ stewardDidInfo2.verkey}} +StewardWallet3,{{ stewardNodeInfo3.nodeStackName }},{{ stewardNodeInfo3.nodeIpAddress }},9701,0.0.0.0,9702,{{ stewardNodeInfo3.verificationKeyForClientStack }},{{ stewardNodeInfo3.blsPublicKey }},{{ stewardNodeInfo3.proofOfPossessionForBlsKey }},{{ stewardDidInfo3.did }},{{ stewardDidInfo3.verkey}} +StewardWallet4,{{ stewardNodeInfo4.nodeStackName }},{{ stewardNodeInfo4.nodeIpAddress }},9701,0.0.0.0,9702,{{ stewardNodeInfo4.verificationKeyForClientStack }},{{ stewardNodeInfo4.blsPublicKey }},{{ stewardNodeInfo4.proofOfPossessionForBlsKey }},{{ stewardDidInfo4.did }},{{ stewardDidInfo4.verkey}} diff --git a/lib/indy/ansible/roles/create_genesis_files/templates/trustee.csv.j2 b/lib/indy/ansible/roles/create_genesis_files/templates/trustee.csv.j2 new file mode 100644 index 00000000..898ddea0 --- /dev/null +++ b/lib/indy/ansible/roles/create_genesis_files/templates/trustee.csv.j2 @@ -0,0 +1,4 @@ +Trustee name,Trustee DID,Trustee verkey +TrusteeWallet1,{{ trusteeDidInfo1.did }},{{ trusteeDidInfo1.verkey }} +TrusteeWallet2,{{ trusteeDidInfo2.did }},{{ trusteeDidInfo2.verkey }} +TrusteeWallet3,{{ trusteeDidInfo3.did }},{{ trusteeDidInfo3.verkey }} diff --git a/lib/indy/ansible/roles/create_genesis_files/vars/main.yml b/lib/indy/ansible/roles/create_genesis_files/vars/main.yml new file mode 100644 index 00000000..fc00801e --- /dev/null +++ b/lib/indy/ansible/roles/create_genesis_files/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for create_genesis_files diff --git a/lib/indy/ansible/roles/create_wallet/defaults/main.yml b/lib/indy/ansible/roles/create_wallet/defaults/main.yml new file mode 100644 index 00000000..6bbfee3f --- /dev/null +++ b/lib/indy/ansible/roles/create_wallet/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# defaults file for create_wallet +indy_cli_rs_path: "/" diff --git a/lib/indy/ansible/roles/create_wallet/handlers/main.yml b/lib/indy/ansible/roles/create_wallet/handlers/main.yml new file mode 100644 index 00000000..0a336006 --- /dev/null +++ b/lib/indy/ansible/roles/create_wallet/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for create_wallet diff --git a/lib/indy/ansible/roles/create_wallet/tasks/install_indy_cli.yml b/lib/indy/ansible/roles/create_wallet/tasks/install_indy_cli.yml new file mode 100644 index 00000000..d2a576cc --- /dev/null +++ b/lib/indy/ansible/roles/create_wallet/tasks/install_indy_cli.yml @@ -0,0 +1,16 @@ +--- +# wget https://github.com/hyperledger/indy-cli-rs/releases/download/v0.1.0/indy-cli-rs-0.1.0-linux-x86_64.tar.gz + +- name: download indy cli + get_url: + url: https://github.com/hyperledger/indy-cli-rs/releases/download/v0.1.0/indy-cli-rs-0.1.0-linux-x86_64.tar.gz + dest: /tmp/indy-cli-rs-0.1.0-linux-x86_64.tar.gz + mode: '0755' + +# tar -xf indy-cli-rs-0.1.0-linux-x86_64.tar.gz +- name: extract indy cli + become: yes + unarchive: + src: /tmp/indy-cli-rs-0.1.0-linux-x86_64.tar.gz + dest: /usr/local/bin + remote_src: yes diff --git a/lib/indy/ansible/roles/create_wallet/tasks/main.yml b/lib/indy/ansible/roles/create_wallet/tasks/main.yml new file mode 100644 index 00000000..c9301b96 --- /dev/null +++ b/lib/indy/ansible/roles/create_wallet/tasks/main.yml @@ -0,0 +1,2 @@ +- ansible.builtin.import_tasks: install_indy_cli.yml +- ansible.builtin.import_tasks: wallet_create.yml diff --git a/lib/indy/ansible/roles/create_wallet/tasks/wallet_create.yml b/lib/indy/ansible/roles/create_wallet/tasks/wallet_create.yml new file mode 100644 index 00000000..965fdeb1 --- /dev/null +++ b/lib/indy/ansible/roles/create_wallet/tasks/wallet_create.yml @@ -0,0 +1,55 @@ +--- +- name: Check Instance Name + ansible.builtin.debug: + msg: "{{ inventory_hostname }}" + +- name: create did seed into secrets manager + community.aws.secretsmanager_secret: + name: "{{ inventory_hostname }}-indy-seed" + state: present + secret_type: string + secret: "{{ lookup('community.general.random_string', length=32, special=false) }}" + overwrite: false + delegate_to: localhost + +- name: make a batch file for create indy wallet + ansible.builtin.template: + src: "{{ item }}.batch.j2" + dest: "/tmp/{{ item }}.batch" + mode: '0644' + with_items: + - walletCreate + - walletOpen + +- name: create indy wallet + ansible.builtin.shell: indy-cli-rs /tmp/walletCreate.batch + args: + chdir: "{{ indy_cli_rs_path }}" + ignore_errors: yes + +- name: get DID and Verkey + ansible.builtin.shell: indy-cli-rs /tmp/walletOpen.batch + args: + chdir: "{{ indy_cli_rs_path }}" + register: create_wallet_result + +# debug create_wallet_result +- name: debug create_wallet_result + ansible.builtin.debug: + var: create_wallet_result.stdout_lines[7] + +- name: Extract DID and Verkey + set_fact: + did: "{{ create_wallet_result.stdout_lines[7][2:24] }}" + verkey: "{{ create_wallet_result.stdout_lines[7][27:51] }}" + +- name: Store the DID and Verkey into secrets manager + community.aws.secretsmanager_secret: + name: "{{ inventory_hostname }}-indy-did" + state: present + secret_type: string + json_secret: + did: "{{ did }}" + verkey: "{{ verkey }}" + overwrite: true + delegate_to: localhost diff --git a/lib/indy/ansible/roles/create_wallet/templates/walletCreate.batch.j2 b/lib/indy/ansible/roles/create_wallet/templates/walletCreate.batch.j2 new file mode 100644 index 00000000..4fd9d361 --- /dev/null +++ b/lib/indy/ansible/roles/create_wallet/templates/walletCreate.batch.j2 @@ -0,0 +1,6 @@ +wallet create {{ inventory_hostname }} key={{ inventory_hostname }} +wallet open {{ inventory_hostname }} key={{ inventory_hostname }} +did new seed={{ lookup('amazon.aws.aws_secret', '{{ inventory_hostname }}-indy-seed', on_denied='warn')}} + +wallet close +exit diff --git a/lib/indy/ansible/roles/create_wallet/templates/walletOpen.batch.j2 b/lib/indy/ansible/roles/create_wallet/templates/walletOpen.batch.j2 new file mode 100644 index 00000000..fa9cde93 --- /dev/null +++ b/lib/indy/ansible/roles/create_wallet/templates/walletOpen.batch.j2 @@ -0,0 +1,4 @@ +wallet open {{ inventory_hostname }} key={{ inventory_hostname }} +did list +wallet close +exit diff --git a/lib/indy/ansible/roles/create_wallet/vars/main.yml b/lib/indy/ansible/roles/create_wallet/vars/main.yml new file mode 100644 index 00000000..8382e2e4 --- /dev/null +++ b/lib/indy/ansible/roles/create_wallet/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for create_wallet diff --git a/lib/indy/ansible/roles/fetch_genesis_files/defaults/main.yml b/lib/indy/ansible/roles/fetch_genesis_files/defaults/main.yml new file mode 100644 index 00000000..1e4e538f --- /dev/null +++ b/lib/indy/ansible/roles/fetch_genesis_files/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for fetch_genesis_files diff --git a/lib/indy/ansible/roles/fetch_genesis_files/handlers/main.yml b/lib/indy/ansible/roles/fetch_genesis_files/handlers/main.yml new file mode 100644 index 00000000..f64da84a --- /dev/null +++ b/lib/indy/ansible/roles/fetch_genesis_files/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for fetch_genesis_files diff --git a/lib/indy/ansible/roles/fetch_genesis_files/tasks/main.yml b/lib/indy/ansible/roles/fetch_genesis_files/tasks/main.yml new file mode 100644 index 00000000..8279a4e3 --- /dev/null +++ b/lib/indy/ansible/roles/fetch_genesis_files/tasks/main.yml @@ -0,0 +1,41 @@ +--- +- name: make indy directory + become: yes + ansible.builtin.file: + path: "/var/lib/indy/{{ INDY_NETEORK_NAME }}" + state: directory + group: indy + owner: indy + mode: '0755' + recurse: yes + +- amazon.aws.aws_caller_info: + register: aws_caller_info + +- name: join s3 bucket name + ansible.builtin.set_fact: + S3_BUCKET_NAME: "{{ aws_caller_info.account }}-ansible-file-transfer-bucket" + +# tasks file for fetch_genesis_files +- name: fetch genesis files from s3 bucket + become: yes + amazon.aws.s3_object: + bucket: "{{ S3_BUCKET_NAME }}" + object: "/genesis/{{ item }}" + dest: "/var/lib/indy/{{ INDY_NETEORK_NAME }}/{{ item }}" + mode: get + with_items: + - pool_transactions_genesis + - domain_transactions_genesis + +- name: change owner and group of genesis files + become: yes + ansible.builtin.file: + path: "/var/lib/indy/{{ INDY_NETEORK_NAME }}/{{ item }}" + state: file + group: indy + owner: indy + mode: '0644' + with_items: + - pool_transactions_genesis + - domain_transactions_genesis diff --git a/lib/indy/ansible/roles/fetch_genesis_files/vars/main.yml b/lib/indy/ansible/roles/fetch_genesis_files/vars/main.yml new file mode 100644 index 00000000..d69b9abb --- /dev/null +++ b/lib/indy/ansible/roles/fetch_genesis_files/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for fetch_genesis_files diff --git a/lib/indy/ansible/roles/install_dependencies/defaults/main.yml b/lib/indy/ansible/roles/install_dependencies/defaults/main.yml new file mode 100644 index 00000000..93a5b2e3 --- /dev/null +++ b/lib/indy/ansible/roles/install_dependencies/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for install_dependencies diff --git a/lib/indy/ansible/roles/install_dependencies/handlers/main.yml b/lib/indy/ansible/roles/install_dependencies/handlers/main.yml new file mode 100644 index 00000000..75631557 --- /dev/null +++ b/lib/indy/ansible/roles/install_dependencies/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for install_dependencies diff --git a/lib/indy/ansible/roles/install_dependencies/tasks/main.yml b/lib/indy/ansible/roles/install_dependencies/tasks/main.yml new file mode 100644 index 00000000..3ea55dba --- /dev/null +++ b/lib/indy/ansible/roles/install_dependencies/tasks/main.yml @@ -0,0 +1,66 @@ +--- +# tasks file for install_dependencies +- name: Add an apt key by id from a keyserver + ansible.builtin.apt_key: + keyserver: "{{ item.keyserver }}" + id: "{{ item.key }}" + with_items: + - { keyserver: keyserver.ubuntu.com, key: 9692C00E657DDE61 } + - { keyserver: keyserver.ubuntu.com, key: CE7709D068DB5E88 } + - { keyserver: keyserver.ubuntu.com, key: 3BC8C2DD662F1C45 } + +- name: Add specified repository into sources list + ansible.builtin.apt_repository: + repo: "{{ item }}" + state: present + with_items: + - "deb https://hyperledger.jfrog.io/artifactory/indy focal rc" + - "deb http://security.ubuntu.com/ubuntu bionic-security main" + - "deb https://repo.sovrin.org/deb bionic master" + - "deb https://sovrin.jfrog.io/artifactory/deb focal rc" + +- name: apt install + ansible.builtin.apt: + update_cache: true + pkg: + - python3-pip + - rocksdb=5.8.8 + - libgflags-dev + - libsnappy-dev + - zlib1g-dev + - libbz2-dev + - liblz4-dev + - libgflags-dev + - python3-libnacl=1.6.1 + - python3-sortedcontainers=1.5.7 + - python3-ujson=1.33 + - python3-pyzmq=22.3.0 + - indy-plenum=1.13.1~rc3 + - indy-node=1.13.2~rc5 + - sovtoken=1.1.0~rc0 + - sovtokenfees=1.1.0~rc0 + - sovrin=1.2.0~rc1 + - libssl1.0.0 + - ursa=0.3.2-1 + +- name: pip install boto3 + ansible.builtin.pip: + name: boto3 + state: latest + +- name: symbolic link for urusa + ansible.builtin.file: + src: "/usr/lib/ursa/{{ item }}" + dest: "/usr/lib/{{ item }}" + state: link + with_items: + - libursa.a + - libursa.so + +- name: replace for indy_config.py + ansible.builtin.template: + src: indy_config.py.j2 + dest: /etc/indy/indy_config.py + owner: indy + group: indy + mode: '0774' diff --git a/lib/indy/ansible/roles/install_dependencies/templates/indy_config.py.j2 b/lib/indy/ansible/roles/install_dependencies/templates/indy_config.py.j2 new file mode 100644 index 00000000..ee539ec9 --- /dev/null +++ b/lib/indy/ansible/roles/install_dependencies/templates/indy_config.py.j2 @@ -0,0 +1,39 @@ +# Current network +NETWORK_NAME = '{{ INDY_NETEORK_NAME }}' + +# Disable stdout logging +enableStdOutLogging = False + +# Directory to store ledger. +LEDGER_DIR = '/var/lib/indy' + +# Directory to store logs. +LOG_DIR = '/var/log/indy' + +# Directory to store keys. +KEYS_DIR = '/var/lib/indy' + +# Directory to store genesis transactions files. +GENESIS_DIR = '/var/lib/indy' + +# Directory to store backups. +BACKUP_DIR = '/var/lib/indy/backup' + +# Directory to store plugins. +PLUGINS_DIR = '/var/lib/indy/plugins' + +# Directory to store node info. +NODE_INFO_DIR = '/var/lib/indy' + + +ENABLED_PLUGINS=[] + +ENABLED_PLUGINS.append('sovtoken') + +ENABLED_PLUGINS.append('sovtokenfees') + +ANYONE_CAN_WRITE = False + +UPGRADE_ENTRY = 'sovrin' + +INDY_VERSION_MATCHING = {"1.1.52": "1.9.1", "1.1.24": "0.9.3", "1.1.35": "0.9.3", "1.1.50": "1.0.0"} diff --git a/lib/indy/ansible/roles/install_dependencies/vars/main.yml b/lib/indy/ansible/roles/install_dependencies/vars/main.yml new file mode 100644 index 00000000..e7cbed3a --- /dev/null +++ b/lib/indy/ansible/roles/install_dependencies/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for install_dependencies diff --git a/lib/indy/ansible/roles/run_indy/defaults/main.yml b/lib/indy/ansible/roles/run_indy/defaults/main.yml new file mode 100644 index 00000000..3f93d4a5 --- /dev/null +++ b/lib/indy/ansible/roles/run_indy/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for run_indy diff --git a/lib/indy/ansible/roles/run_indy/handlers/main.yml b/lib/indy/ansible/roles/run_indy/handlers/main.yml new file mode 100644 index 00000000..b2643c36 --- /dev/null +++ b/lib/indy/ansible/roles/run_indy/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for run_indy diff --git a/lib/indy/ansible/roles/run_indy/tasks/main.yml b/lib/indy/ansible/roles/run_indy/tasks/main.yml new file mode 100644 index 00000000..d962c36b --- /dev/null +++ b/lib/indy/ansible/roles/run_indy/tasks/main.yml @@ -0,0 +1,7 @@ +--- +# tasks file for run_indy +- name: run indy-node + ansible.builtin.systemd: + name: indy-node + state: restarted + enabled: yes diff --git a/lib/indy/ansible/roles/run_indy/vars/main.yml b/lib/indy/ansible/roles/run_indy/vars/main.yml new file mode 100644 index 00000000..bbda1abc --- /dev/null +++ b/lib/indy/ansible/roles/run_indy/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for run_indy diff --git a/lib/indy/ansible/roles/validator_init/defaults/main.yml b/lib/indy/ansible/roles/validator_init/defaults/main.yml new file mode 100644 index 00000000..e23edebc --- /dev/null +++ b/lib/indy/ansible/roles/validator_init/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# defaults file for validator_init +INDY_CLIENT_IP_ADDRESS: 0.0.0.0 diff --git a/lib/indy/ansible/roles/validator_init/handlers/main.yml b/lib/indy/ansible/roles/validator_init/handlers/main.yml new file mode 100644 index 00000000..f3520df6 --- /dev/null +++ b/lib/indy/ansible/roles/validator_init/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for validator_init diff --git a/lib/indy/ansible/roles/validator_init/tasks/init.yml b/lib/indy/ansible/roles/validator_init/tasks/init.yml new file mode 100644 index 00000000..5f7b0297 --- /dev/null +++ b/lib/indy/ansible/roles/validator_init/tasks/init.yml @@ -0,0 +1,29 @@ +--- +# tasks file for validator_init +- name: validator initialize + ansible.builtin.shell: init_indy_node {{ inventory_hostname }} {{ ansible_facts['ens6']['ipv4']['address'] }} 9701 {{ INDY_CLIENT_IP_ADDRESS }} 9702 {{ lookup('amazon.aws.aws_secret', '{{ inventory_hostname }}-indy-seed', on_denied='warn')}} + become: yes + register: init_result + +- name: print init result + ansible.builtin.debug: + msg: "{{ init_result.stdout_lines }}" + +- name: Store the DID and Verkey into secrets manager + community.aws.secretsmanager_secret: + name: "{{ inventory_hostname }}-indy-nodeInfo" + state: present + secret_type: string + json_secret: + nodeStackName: "{{ init_result.stdout_lines[0] | regex_findall('Node-stack name is (.*)') | first }}" + clientStackName: "{{ init_result.stdout_lines[1] | regex_findall('Client-stack name is (.*)') | first }}" + generatingkeysForProvidedSeed: "{{ init_result.stdout_lines[2] | regex_findall('Generating keys for provided seed (.*)') | first }}" + publicKeyForClientStack: "{{ init_result.stdout_lines[4] | regex_findall('Public key is (.*)')| first }}" + verificationKeyForClientStack: "{{ init_result.stdout_lines[5] | regex_findall('Verification key is (.*)')| first }}" + publicKeyForNodeStack: "{{ init_result.stdout_lines[7] | regex_findall('Public key is (.*)') | last }}" + verificationKeyForNodeStack: "{{ init_result.stdout_lines[8] | regex_findall('Verification key is (.*)') | last }}" + blsPublicKey: "{{ init_result.stdout_lines[9] | regex_findall('BLS Public key is (.*)') | first }}" + proofOfPossessionForBlsKey: "{{ init_result.stdout_lines[10] | regex_findall('Proof of possession for BLS key is (.*)') | first }}" + nodeIpAddress: "{{ ansible_facts['ens6']['ipv4']['address'] }}" + overwrite: true + delegate_to: localhost diff --git a/lib/indy/ansible/roles/validator_init/tasks/main.yml b/lib/indy/ansible/roles/validator_init/tasks/main.yml new file mode 100644 index 00000000..4565a97d --- /dev/null +++ b/lib/indy/ansible/roles/validator_init/tasks/main.yml @@ -0,0 +1 @@ +- ansible.builtin.import_tasks: init.yml diff --git a/lib/indy/ansible/roles/validator_init/vars/main.yml b/lib/indy/ansible/roles/validator_init/vars/main.yml new file mode 100644 index 00000000..f6d71ada --- /dev/null +++ b/lib/indy/ansible/roles/validator_init/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for validator_init diff --git a/lib/indy/app.ts b/lib/indy/app.ts index 399ab153..c1f174ef 100644 --- a/lib/indy/app.ts +++ b/lib/indy/app.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node -import 'source-map-support/register'; -import * as cdk from 'aws-cdk-lib'; -import { IndyNodeStack } from './lib/indy-node-stack'; +import "source-map-support/register"; +import * as cdk from "aws-cdk-lib"; +import { IndyNodeStack } from "./lib/indy-node-stack"; const app = new cdk.App(); -new IndyNodeStack(app, 'IndyNodeStack', {}); \ No newline at end of file +new IndyNodeStack(app, "IndyNodeStack", {}); diff --git a/lib/indy/cdk.json b/lib/indy/cdk.json index d374005c..1658b3d7 100644 --- a/lib/indy/cdk.json +++ b/lib/indy/cdk.json @@ -1,44 +1,39 @@ { - "app": "npx ts-node --prefer-ts-exts app.ts", - "watch": { - "include": [ - "**" - ], - "exclude": [ - "README.md", - "cdk*.json", - "**/*.d.ts", - "**/*.js", - "tsconfig.json", - "package*.json", - "yarn.lock", - "node_modules", - "test" - ] - }, - "context": { - "@aws-cdk/aws-lambda:recognizeLayerVersion": true, - "@aws-cdk/core:checkSecretUsage": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ], - "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, - "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, - "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, - "@aws-cdk/aws-iam:minimizePolicies": true, - "@aws-cdk/core:validateSnapshotRemovalPolicy": true, - "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, - "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, - "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, - "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, - "@aws-cdk/core:enablePartitionLiterals": true, - "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, - "@aws-cdk/aws-iam:standardizedServicePrincipals": true, - "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, - "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, - "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, - "@aws-cdk/aws-route53-patters:useCertificate": true, - "@aws-cdk/customresources:installLatestAwsSdkDefault": false - } + "app": "npx ts-node --prefer-ts-exts app.ts", + "watch": { + "include": ["**"], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": ["aws", "aws-cn"], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false + } } diff --git a/lib/indy/doc/assets/Architecture.drawio b/lib/indy/doc/assets/Architecture.drawio index 20cb9a08..47546cfa 100644 --- a/lib/indy/doc/assets/Architecture.drawio +++ b/lib/indy/doc/assets/Architecture.drawio @@ -84,4 +84,4 @@ - \ No newline at end of file + diff --git a/lib/indy/lib/assets/user-data/steward.sh b/lib/indy/lib/assets/user-data/steward.sh index 1202f7ac..3559fdf5 100644 --- a/lib/indy/lib/assets/user-data/steward.sh +++ b/lib/indy/lib/assets/user-data/steward.sh @@ -1,5 +1,4 @@ #!/bin/bash -NETWORK_NAME=sample-network echo 'network: {config: disabled}' > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg echo 800 800 >> /etc/iproute2/rt_tables @@ -65,38 +64,4 @@ network: netplan generate netplan apply -apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 9692C00E657DDE61 -apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 -apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3BC8C2DD662F1C45 -add-apt-repository "deb https://hyperledger.jfrog.io/artifactory/indy focal rc" -add-apt-repository "deb http://security.ubuntu.com/ubuntu bionic-security main" -add-apt-repository "deb https://repo.sovrin.org/deb bionic master" -add-apt-repository "deb https://sovrin.jfrog.io/artifactory/deb focal rc" -apt-get update -y -apt-get upgrade -y -apt-get install -y \ - rocksdb=5.8.8 \ - libgflags-dev \ - libsnappy-dev \ - zlib1g-dev \ - libbz2-dev \ - liblz4-dev \ - libgflags-dev \ - python3-libnacl=1.6.1 \ - python3-sortedcontainers=1.5.7 \ - python3-ujson=1.33 \ - python3-pyzmq=22.3.0 \ - indy-plenum=1.13.1~rc3 \ - indy-node=1.13.2~rc5 \ - sovtoken=1.1.0~rc0 \ - sovtokenfees=1.1.0~rc0 \ - sovrin=1.2.0~rc1 \ - libssl1.0.0 \ - ursa=0.3.2-1 - -ln -sv /usr/lib/ursa/* /usr/lib - -sed -i "s/NETWORK_NAME = None/NETWORK_NAME = '${NETWORK_NAME}'/" /etc/indy/indy_config.py -sed -i -re "s/(NETWORK_NAME = ')\w+/\1${NETWORK_NAME}/" /etc/indy/indy_config.py - -reboot \ No newline at end of file +reboot diff --git a/lib/indy/lib/assets/user-data/trustee.sh b/lib/indy/lib/assets/user-data/trustee.sh deleted file mode 100644 index be333d83..00000000 --- a/lib/indy/lib/assets/user-data/trustee.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 9692C00E657DDE61 -apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 -apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3BC8C2DD662F1C45 -add-apt-repository "deb https://hyperledger.jfrog.io/artifactory/indy focal rc" -add-apt-repository "deb http://security.ubuntu.com/ubuntu bionic-security main" -add-apt-repository "deb https://repo.sovrin.org/deb bionic master" -add-apt-repository "deb https://sovrin.jfrog.io/artifactory/deb focal rc" -apt-get update -y -apt-get upgrade -y -apt-get install -y \ - rocksdb=5.8.8 \ - libgflags-dev \ - libsnappy-dev \ - zlib1g-dev \ - libbz2-dev \ - liblz4-dev \ - libgflags-dev \ - python3-libnacl=1.6.1 \ - python3-sortedcontainers=1.5.7 \ - python3-ujson=1.33 \ - python3-pyzmq=22.3.0 \ - indy-plenum=1.13.1~rc3 \ - indy-node=1.13.2~rc5 \ - sovtoken=1.1.0~rc0 \ - sovtokenfees=1.1.0~rc0 \ - sovrin=1.2.0~rc1 \ - libssl1.0.0 \ - ursa=0.3.2-1 - -ln -sv /usr/lib/ursa/* /usr/lib - -wget https://github.com/hyperledger/indy-cli-rs/releases/download/v0.1.0/indy-cli-rs-0.1.0-linux-x86_64.tar.gz -tar -xf indy-cli-rs-0.1.0-linux-x86_64.tar.gz \ No newline at end of file diff --git a/lib/indy/lib/constructs/indy-node-instance.ts b/lib/indy/lib/constructs/indy-node-instance.ts deleted file mode 100644 index a0bdf532..00000000 --- a/lib/indy/lib/constructs/indy-node-instance.ts +++ /dev/null @@ -1,63 +0,0 @@ -import * as cdk from 'aws-cdk-lib'; -import * as ec2 from 'aws-cdk-lib/aws-ec2'; -import { Construct } from 'constructs'; - -import { readFileSync } from "fs"; - -export interface IndyNodeInstanceProps { - readonly vpc: ec2.IVpc - readonly clientSG: ec2.ISecurityGroup - readonly nodeSG: ec2.ISecurityGroup -} - -export class IndyNodeInstance extends Construct { - public readonly instance: ec2.Instance; - - constructor(scope: Construct, id: string, props: IndyNodeInstanceProps) { - super(scope, id); - - const { vpc, clientSG, nodeSG } = props; - - const clientNic: ec2.CfnInstance.NetworkInterfaceProperty = { - deviceIndex: "0", - groupSet: [clientSG.securityGroupId], - subnetId: vpc.privateSubnets[0].subnetId, - description: 'Client NIC', - }; - - const nodeNic: ec2.CfnInstance.NetworkInterfaceProperty = { - deviceIndex: "1", - groupSet: [nodeSG.securityGroupId], - subnetId: vpc.privateSubnets[0].subnetId, - description: 'Node NIC', - }; - - const instance = new ec2.Instance(this, 'Instance', { - vpc: vpc, - instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.LARGE), - machineImage: ec2.MachineImage.fromSsmParameter( - '/aws/service/canonical/ubuntu/server/focal/stable/current/amd64/hvm/ebs-gp2/ami-id' - ), - ssmSessionPermissions: true, - userData: ec2.UserData.custom(readFileSync("./lib/assets/user-data/steward.sh", "base64")), - blockDevices: [{ - deviceName: "/dev/sda1", - volume: ec2.BlockDeviceVolume.ebs(250, { - volumeType: ec2.EbsDeviceVolumeType.STANDARD, - encrypted: true - }) - }], - }); - - const cfnInstance = instance.node.defaultChild as ec2.CfnInstance; - cfnInstance.addPropertyDeletionOverride('SubnetId'); - cfnInstance.addPropertyDeletionOverride('SecurityGroupIds'); - cfnInstance.networkInterfaces = [ - clientNic, nodeNic - ] - - instance.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN); - - this.instance = instance; - } -} diff --git a/lib/indy/lib/constructs/indy-steward-node-instance.ts b/lib/indy/lib/constructs/indy-steward-node-instance.ts new file mode 100644 index 00000000..4a864de7 --- /dev/null +++ b/lib/indy/lib/constructs/indy-steward-node-instance.ts @@ -0,0 +1,83 @@ +import * as cdk from "aws-cdk-lib"; +import * as ec2 from "aws-cdk-lib/aws-ec2"; +import * as S3 from "aws-cdk-lib/aws-s3"; +import { Construct } from "constructs"; + +import { readFileSync } from "fs"; + +export interface IndyNodeInstanceProps { + readonly vpc: ec2.IVpc; + readonly clientSG: ec2.ISecurityGroup; + readonly nodeSG: ec2.ISecurityGroup; + readonly ansibleBucket: S3.Bucket; +} + +export class IndyStewardNodeInstance extends Construct { + public readonly instance: ec2.Instance; + + constructor(scope: Construct, id: string, props: IndyNodeInstanceProps) { + super(scope, id); + + const { vpc, clientSG, nodeSG } = props; + + const clientNic: ec2.CfnInstance.NetworkInterfaceProperty = { + deviceIndex: "0", + groupSet: [clientSG.securityGroupId], + subnetId: vpc.privateSubnets[0].subnetId, + description: "Client NIC", + }; + + const nodeNic: ec2.CfnInstance.NetworkInterfaceProperty = { + deviceIndex: "1", + groupSet: [nodeSG.securityGroupId], + subnetId: vpc.privateSubnets[0].subnetId, + description: "Node NIC", + }; + + const instance = new ec2.Instance(this, "Instance", { + vpc: vpc, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.LARGE), + machineImage: ec2.MachineImage.fromSsmParameter( + "/aws/service/canonical/ubuntu/server/focal/stable/current/amd64/hvm/ebs-gp2/ami-id", + ), + ssmSessionPermissions: true, + userData: ec2.UserData.custom(readFileSync("./lib/assets/user-data/steward.sh", "base64")), + blockDevices: [ + { + deviceName: "/dev/sda1", + volume: ec2.BlockDeviceVolume.ebs(200, { + volumeType: ec2.EbsDeviceVolumeType.GP3, + encrypted: true, + deleteOnTermination: true, + }), + }, + ], + }); + + cdk.Tags.of(instance).add("Name", id); + + instance.addToRolePolicy( + new cdk.aws_iam.PolicyStatement({ + effect: cdk.aws_iam.Effect.ALLOW, + actions: ["secretsmanager:GetSecretValue"], + resources: [`arn:aws:secretsmanager:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:secret:${id}-*`], + }), + ); + + props.ansibleBucket.grantRead(instance); + + const cfnInstance = instance.node.defaultChild as ec2.CfnInstance; + cfnInstance.addPropertyDeletionOverride("SubnetId"); + cfnInstance.addPropertyDeletionOverride("SecurityGroupIds"); + cfnInstance.networkInterfaces = [clientNic, nodeNic]; + + instance.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY); + + new cdk.CfnOutput(this, `${id}InstanceId`, { + value: instance.instanceId, + exportName: `${id}InstanceId`, + }); + + this.instance = instance; + } +} diff --git a/lib/indy/lib/constructs/indy-trustee-node-instance.ts b/lib/indy/lib/constructs/indy-trustee-node-instance.ts new file mode 100644 index 00000000..e65f2dc4 --- /dev/null +++ b/lib/indy/lib/constructs/indy-trustee-node-instance.ts @@ -0,0 +1,46 @@ +import * as cdk from "aws-cdk-lib"; +import * as ec2 from "aws-cdk-lib/aws-ec2"; +import { Construct } from "constructs"; + +export interface IndyNodeInstanceProps { + readonly vpc: ec2.IVpc; + readonly nodeSG: ec2.ISecurityGroup; +} + +export class IndyTrusteeNodeInstance extends Construct { + public readonly instance: ec2.Instance; + + constructor(scope: Construct, id: string, props: IndyNodeInstanceProps) { + super(scope, id); + + const { vpc } = props; + + const instance = new ec2.Instance(this, "Instance", { + vpc: vpc, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM), + machineImage: ec2.MachineImage.fromSsmParameter( + "/aws/service/canonical/ubuntu/server/focal/stable/current/amd64/hvm/ebs-gp2/ami-id", + ), + ssmSessionPermissions: true, + securityGroup: props.nodeSG, + }); + instance.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY); + + cdk.Tags.of(instance).add("Name", id); + + instance.addToRolePolicy( + new cdk.aws_iam.PolicyStatement({ + effect: cdk.aws_iam.Effect.ALLOW, + actions: ["secretsmanager:GetSecretValue"], + resources: [`arn:aws:secretsmanager:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:secret:${id}-*`], + }), + ); + + new cdk.CfnOutput(this, `${id}InstanceId`, { + value: instance.instanceId, + exportName: `${id}InstanceId`, + }); + + this.instance = instance; + } +} diff --git a/lib/indy/lib/indy-node-stack.ts b/lib/indy/lib/indy-node-stack.ts index 2f32877e..424ce29c 100644 --- a/lib/indy/lib/indy-node-stack.ts +++ b/lib/indy/lib/indy-node-stack.ts @@ -1,66 +1,57 @@ -import * as cdk from 'aws-cdk-lib'; -import { Construct } from 'constructs'; +import * as cdk from "aws-cdk-lib"; +import { Construct } from "constructs"; import * as ec2 from "aws-cdk-lib/aws-ec2"; +import * as s3 from "aws-cdk-lib/aws-s3"; -import { IndyNodeInstance } from './constructs/indy-node-instance'; - -import { readFileSync } from "fs"; +import { IndyStewardNodeInstance } from "./constructs/indy-steward-node-instance"; +import { IndyTrusteeNodeInstance } from "./constructs/indy-trustee-node-instance"; export class IndyNodeStack extends cdk.Stack { - constructor(scope: Construct, id: string, props?: cdk.StackProps) { - super(scope, id, props); - - const vpc = new ec2.Vpc(this, "IndyVpc", { - ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'), - }); - - // SecurityGroup of Nodes for Clients - const clientSG = new ec2.SecurityGroup(this, 'ClientSG', {vpc}); - clientSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(9702), 'Allow 9702 from anywhere'); - - // SecurityGroup of Nodes for Other Nodes - const nodeSG = new ec2.SecurityGroup(this, 'NodeSG', {vpc}); - nodeSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(9701), 'Allow 9701 from anywhere'); - - const node1 = new IndyNodeInstance(this, "Node1",{vpc, clientSG, nodeSG}); - const node2 = new IndyNodeInstance(this, "Node2",{vpc, clientSG, nodeSG}); - const node3 = new IndyNodeInstance(this, "Node3",{vpc, clientSG, nodeSG}); - const node4 = new IndyNodeInstance(this, "Node4",{vpc, clientSG, nodeSG}); - - const trustee = new ec2.Instance(this, 'TrusteeInstance', { - vpc: vpc, - instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM), - machineImage: ec2.MachineImage.fromSsmParameter( - '/aws/service/canonical/ubuntu/server/focal/stable/current/amd64/hvm/ebs-gp2/ami-id' - ), - ssmSessionPermissions: true, - userData: ec2.UserData.custom(readFileSync("./lib/assets/user-data/trustee.sh", "base64")), - }); - trustee.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN); - - new cdk.CfnOutput(this, 'Node1InstanceId', { - value: node1.instance.instanceId, - exportName: 'Node1InstanceId', - }) - - new cdk.CfnOutput(this, 'Node2InstanceId', { - value: node2.instance.instanceId, - exportName: 'Node2InstanceId', - }) - - new cdk.CfnOutput(this, 'Node3InstanceId', { - value: node3.instance.instanceId, - exportName: 'Node3InstanceId', - }) - - new cdk.CfnOutput(this, 'Node4InstanceId', { - value: node4.instance.instanceId, - exportName: 'Node4InstanceId', - }) - - new cdk.CfnOutput(this, 'TrusteeInstanceId', { - value: trustee.instanceId, - exportName: 'TrusteeInstanceId', - }) - } + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const vpc = new ec2.Vpc(this, "IndyVpc", { + ipAddresses: ec2.IpAddresses.cidr("10.0.0.0/16"), + }); + + // SecurityGroup of Nodes for Clients + const clientSG = new ec2.SecurityGroup(this, "ClientSG", { + vpc, + allowAllOutbound: true, + disableInlineRules: true, + }); + clientSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(9702), "Allow 9702 from anywhere"); + + // SecurityGroup of Nodes for Other Nodes + const nodeSG = new ec2.SecurityGroup(this, "NodeSG", { + vpc, + allowAllOutbound: true, + disableInlineRules: true, + }); + nodeSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(9701), "Allow 9701 from anywhere"); + + const ansibleBucket = new s3.Bucket(this, "AnsibleFileTransferBucket", { + bucketName: `${cdk.Stack.of(this).account}-ansible-file-transfer-bucket`, + encryption: s3.BucketEncryption.S3_MANAGED, + blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, + versioned: false, + enforceSSL: true, + autoDeleteObjects: true, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + new IndyStewardNodeInstance(this, "steward1", { vpc, clientSG, nodeSG, ansibleBucket }); + new IndyStewardNodeInstance(this, "steward2", { vpc, clientSG, nodeSG, ansibleBucket }); + new IndyStewardNodeInstance(this, "steward3", { vpc, clientSG, nodeSG, ansibleBucket }); + new IndyStewardNodeInstance(this, "steward4", { vpc, clientSG, nodeSG, ansibleBucket }); + + new IndyTrusteeNodeInstance(this, "trustee1", { vpc, nodeSG }); + new IndyTrusteeNodeInstance(this, "trustee2", { vpc, nodeSG }); + new IndyTrusteeNodeInstance(this, "trustee3", { vpc, nodeSG }); + + new cdk.CfnOutput(this, "AnsibleFileTransferBucketName", { + value: ansibleBucket.bucketName, + exportName: "AnsibleFileTransferBucketName", + }); + } } diff --git a/lib/solana/README.md b/lib/solana/README.md index 720c8135..aeecbeaf 100644 --- a/lib/solana/README.md +++ b/lib/solana/README.md @@ -272,7 +272,7 @@ The result should be like this (the actual balance might change): There are two ways. Using the existing volume or using a new one. If your instance has Instance Store volume attached, it is better to keep your swap on it. - Option 1: Dedicated Instance Store volume - + ```bash sudo mkswap /dev/nvme3n1 sudo swapon /dev/nvme3n1 diff --git a/website/src/theme/Root.js b/website/src/theme/Root.js index bbc66587..83210998 100644 --- a/website/src/theme/Root.js +++ b/website/src/theme/Root.js @@ -10,7 +10,7 @@ export default function Root({children}) { // check if dataLayer exists i.e can we track or not? if (typeof dataLayer !== 'undefined') { - + // send new event to Google Tag Manager dataLayer.push({event: 'pageview'}); @@ -29,10 +29,10 @@ export default function Root({children}) { location="top" overlay="true" setDeclineCookie> - We use cookies to ensure you get the best experience on our website. + We use cookies to ensure you get the best experience on our website. {children} ; -} \ No newline at end of file +} From 6d9f963ea24e35c0058efc32732fca8f2a83e769 Mon Sep 17 00:00:00 2001 From: Satsuki Fukazu Date: Mon, 18 Mar 2024 14:50:39 +0900 Subject: [PATCH 06/22] change security group rule fo indy --- lib/indy/lib/indy-node-stack.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/indy/lib/indy-node-stack.ts b/lib/indy/lib/indy-node-stack.ts index 424ce29c..d794d681 100644 --- a/lib/indy/lib/indy-node-stack.ts +++ b/lib/indy/lib/indy-node-stack.ts @@ -15,20 +15,28 @@ export class IndyNodeStack extends cdk.Stack { }); // SecurityGroup of Nodes for Clients - const clientSG = new ec2.SecurityGroup(this, "ClientSG", { + const clientSG = new ec2.SecurityGroup(this, 'ClientSG', { vpc, allowAllOutbound: true, disableInlineRules: true, }); - clientSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(9702), "Allow 9702 from anywhere"); + clientSG.addIngressRule( + ec2.Peer.securityGroupId(clientSG.securityGroupId), + ec2.Port.tcp(9702), + 'Allow 9702 from internal for client' + ) // SecurityGroup of Nodes for Other Nodes - const nodeSG = new ec2.SecurityGroup(this, "NodeSG", { + const nodeSG = new ec2.SecurityGroup(this, 'NodeSG', { vpc, allowAllOutbound: true, disableInlineRules: true, }); - nodeSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(9701), "Allow 9701 from anywhere"); + nodeSG.addIngressRule( + ec2.Peer.securityGroupId(nodeSG.securityGroupId), + ec2.Port.tcp(9701), + 'Allow 9701 from internal for indy node' + ); const ansibleBucket = new s3.Bucket(this, "AnsibleFileTransferBucket", { bucketName: `${cdk.Stack.of(this).account}-ansible-file-transfer-bucket`, From 5d4888200472414615f4381bb75df36c8c2c84fa Mon Sep 17 00:00:00 2001 From: Nikolay Vlasov Date: Wed, 17 Apr 2024 16:38:38 +1000 Subject: [PATCH 07/22] Indy. Refactoring --- lib/indy/README.md | 23 +++++-------- lib/indy/ansible/inventory/group_vars/all.yml | 2 +- lib/indy/ansible/inventory/inventory.yml | 33 ------------------- .../ansible/inventory/inventory.yml.template | 33 +++++++++++++++++++ lib/indy/app.ts | 10 ++++++ lib/indy/lib/indy-node-stack.ts | 5 +++ 6 files changed, 58 insertions(+), 48 deletions(-) delete mode 100644 lib/indy/ansible/inventory/inventory.yml create mode 100644 lib/indy/ansible/inventory/inventory.yml.template diff --git a/lib/indy/README.md b/lib/indy/README.md index 39cbf0d9..be6c446a 100644 --- a/lib/indy/README.md +++ b/lib/indy/README.md @@ -1,5 +1,9 @@ # Sample AWS Blockchain Node Runner app for Hyperledger Indy +| Contributed by | +|:--------------------:| +| [@fsatsuki](https://github.com/fsatsuki) | + [View this page in Japanese (日本語)](./README_ja.md) ## Architecture Overview @@ -49,7 +53,7 @@ npx cdk bootstrap 3. Deploying resources with CDK ```bash -npx cdk deploy +npx cdk deploy --json --outputs-file indy-test-deploy.json Outputs: IndyNetworkStack.AnsibleFileTransferBucketName = 111122223333-ansible-file-transfer-bucket @@ -75,24 +79,15 @@ When running on a Mac, set the following environment variables. - Create a Python virtual environment and install ansible ``` - $cd ansible - $ Python3 -m venv.venv - $source.venv/bin/activate + $ cd ansible + $ python3 -m venv venv + $ source ./venv/bin/activate ``` ``` $ pip install -r requirements.txt ``` -##### Ansible and Session Manager - -- In order to achieve SSH access to the EC2 instance using Session Manager, refer to [Install the Session Manager plugin for the AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) and install the Session Manager Plugin. By using the Session Manager, deployment by Ansible to an EC2 instance of a private subnet that cannot be accessed from the internet is possible without setting a security group. - -- Installs a ansible plug-in for SSH access to EC2 using the AWS Systems Manager Session Manager. - ``` - $ ansible-galaxy collection install community.aws - ``` - ##### Describe instance information to be built in inventory.yml - Create an indentory file containing information on the EC2 instance that will build the environment. Enter the instance ID described in the CDK output results in the settings column for each node. The value of `indyNetworkStack.ansibleFileTransferBucketName` described in CDK output results is inputted to `ansible_aws_ssm_bucket_name`. When Ansible transfers files to the target host, the Amazon Simple Storage Service (Amazon S3) bucket specified here is used. @@ -136,7 +131,7 @@ Define the parameters referred to by Ansible in the configuration file. Set Indy ``` $ vi inventory/group_vars/all.yml -INDY_NETEORK_NAME: sample-network +INDY_NETWORK_NAME: sample-network ``` ##### Execute environment construction with Ansible diff --git a/lib/indy/ansible/inventory/group_vars/all.yml b/lib/indy/ansible/inventory/group_vars/all.yml index 4af85a59..d505bfda 100644 --- a/lib/indy/ansible/inventory/group_vars/all.yml +++ b/lib/indy/ansible/inventory/group_vars/all.yml @@ -1 +1 @@ -INDY_NETEORK_NAME: sample-network +INDY_NETWORK_NAME: sample-network diff --git a/lib/indy/ansible/inventory/inventory.yml b/lib/indy/ansible/inventory/inventory.yml deleted file mode 100644 index e90b7a8d..00000000 --- a/lib/indy/ansible/inventory/inventory.yml +++ /dev/null @@ -1,33 +0,0 @@ -all: - hosts: - steward1: - ansible_aws_ssm_instance_id: i-1234567890abcdef1 - steward2: - ansible_aws_ssm_instance_id: i-1234567890abcdef2 - steward3: - ansible_aws_ssm_instance_id: i-1234567890abcdef3 - steward4: - ansible_aws_ssm_instance_id: i-1234567890abcdef4 - trustee1: - ansible_aws_ssm_instance_id: i-1234567890abcdef5 - trustee2: - ansible_aws_ssm_instance_id: i-1234567890abcdef6 - trustee3: - ansible_aws_ssm_instance_id: i-1234567890abcdef7 - children: - steward: - hosts: - steward[1:4]: - trustee: - hosts: - trustee[1:3]: - indy: - children: - steward: - trustee: - - vars: - ansible_connection: aws_ssm - ansible_aws_ssm_region: ap-northeast-1 - ansible_aws_ssm_s3_addressing_style: virtual - ansible_aws_ssm_bucket_name: 111122223333-ansible-file-transfer-bucket diff --git a/lib/indy/ansible/inventory/inventory.yml.template b/lib/indy/ansible/inventory/inventory.yml.template new file mode 100644 index 00000000..75404d32 --- /dev/null +++ b/lib/indy/ansible/inventory/inventory.yml.template @@ -0,0 +1,33 @@ +all: + hosts: + steward1: + ansible_aws_ssm_instance_id: _steward1steward1InstanceId_ + steward2: + ansible_aws_ssm_instance_id: _steward2steward1InstanceId_ + steward3: + ansible_aws_ssm_instance_id: _steward3steward1InstanceId_ + steward4: + ansible_aws_ssm_instance_id: _steward1steward4InstanceId_ + trustee1: + ansible_aws_ssm_instance_id: _trustee1trustee1InstanceId_ + trustee2: + ansible_aws_ssm_instance_id: _trustee2trustee2InstanceId_ + trustee3: + ansible_aws_ssm_instance_id: _trustee1trustee1InstanceId_ + children: + steward: + hosts: + steward[1:4]: + trustee: + hosts: + trustee[1:3]: + indy: + children: + steward: + trustee: + + vars: + ansible_connection: aws_ssm + ansible_aws_ssm_region: _aws_region_ + ansible_aws_ssm_s3_addressing_style: virtual + ansible_aws_ssm_bucket_name: _ansible-file-transfer-bucket_ diff --git a/lib/indy/app.ts b/lib/indy/app.ts index c1f174ef..1f4304be 100644 --- a/lib/indy/app.ts +++ b/lib/indy/app.ts @@ -1,7 +1,17 @@ #!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; +import * as nag from "cdk-nag"; import { IndyNodeStack } from "./lib/indy-node-stack"; const app = new cdk.App(); new IndyNodeStack(app, "IndyNodeStack", {}); + +// Security Check +cdk.Aspects.of(app).add( + new nag.AwsSolutionsChecks({ + verbose: false, + reports: true, + logIgnores: false, + }) +); diff --git a/lib/indy/lib/indy-node-stack.ts b/lib/indy/lib/indy-node-stack.ts index d794d681..00b6b14f 100644 --- a/lib/indy/lib/indy-node-stack.ts +++ b/lib/indy/lib/indy-node-stack.ts @@ -61,5 +61,10 @@ export class IndyNodeStack extends cdk.Stack { value: ansibleBucket.bucketName, exportName: "AnsibleFileTransferBucketName", }); + + new cdk.CfnOutput(this, "DeploymentRegion", { + value: cdk.Stack.of(this).region, + exportName: "DeploymentRegion", + }); } } From 25436c5840832298b7817d6d5cc66967eb556e32 Mon Sep 17 00:00:00 2001 From: Nikolay Vlasov Date: Wed, 17 Apr 2024 16:42:37 +1000 Subject: [PATCH 08/22] Indy. Added gitignore for python venv --- lib/indy/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 lib/indy/.gitignore diff --git a/lib/indy/.gitignore b/lib/indy/.gitignore new file mode 100644 index 00000000..f5e96dbf --- /dev/null +++ b/lib/indy/.gitignore @@ -0,0 +1 @@ +venv \ No newline at end of file From 495062f1e49d3eab4d00df9086f6e9f6f86e434d Mon Sep 17 00:00:00 2001 From: Nikolay Vlasov Date: Wed, 17 Apr 2024 17:25:51 +1000 Subject: [PATCH 09/22] Indy. Refactored CDK outputs from constructs for simpler properties extraction --- lib/indy/.gitignore | 3 +- lib/indy/README.md | 2 +- .../{inventory.yml.template => inventory.yml} | 0 .../constructs/indy-steward-node-instance.ts | 11 +++-- .../constructs/indy-trustee-node-instance.ts | 9 ++-- lib/indy/lib/indy-node-stack.ts | 49 ++++++++++++++++--- 6 files changed, 58 insertions(+), 16 deletions(-) rename lib/indy/ansible/inventory/{inventory.yml.template => inventory.yml} (100%) diff --git a/lib/indy/.gitignore b/lib/indy/.gitignore index f5e96dbf..45887caf 100644 --- a/lib/indy/.gitignore +++ b/lib/indy/.gitignore @@ -1 +1,2 @@ -venv \ No newline at end of file +venv +*-deploy-output.json \ No newline at end of file diff --git a/lib/indy/README.md b/lib/indy/README.md index be6c446a..a08f2786 100644 --- a/lib/indy/README.md +++ b/lib/indy/README.md @@ -53,7 +53,7 @@ npx cdk bootstrap 3. Deploying resources with CDK ```bash -npx cdk deploy --json --outputs-file indy-test-deploy.json +npx cdk deploy --json --outputs-file indy-test-deploy-output.json Outputs: IndyNetworkStack.AnsibleFileTransferBucketName = 111122223333-ansible-file-transfer-bucket diff --git a/lib/indy/ansible/inventory/inventory.yml.template b/lib/indy/ansible/inventory/inventory.yml similarity index 100% rename from lib/indy/ansible/inventory/inventory.yml.template rename to lib/indy/ansible/inventory/inventory.yml diff --git a/lib/indy/lib/constructs/indy-steward-node-instance.ts b/lib/indy/lib/constructs/indy-steward-node-instance.ts index 4a864de7..876804cb 100644 --- a/lib/indy/lib/constructs/indy-steward-node-instance.ts +++ b/lib/indy/lib/constructs/indy-steward-node-instance.ts @@ -14,12 +14,15 @@ export interface IndyNodeInstanceProps { export class IndyStewardNodeInstance extends Construct { public readonly instance: ec2.Instance; + public readonly constructId: string; constructor(scope: Construct, id: string, props: IndyNodeInstanceProps) { super(scope, id); const { vpc, clientSG, nodeSG } = props; + constructId: id + const clientNic: ec2.CfnInstance.NetworkInterfaceProperty = { deviceIndex: "0", groupSet: [clientSG.securityGroupId], @@ -54,13 +57,13 @@ export class IndyStewardNodeInstance extends Construct { ], }); - cdk.Tags.of(instance).add("Name", id); + cdk.Tags.of(instance).add("Name", this.constructId); instance.addToRolePolicy( new cdk.aws_iam.PolicyStatement({ effect: cdk.aws_iam.Effect.ALLOW, actions: ["secretsmanager:GetSecretValue"], - resources: [`arn:aws:secretsmanager:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:secret:${id}-*`], + resources: [`arn:aws:secretsmanager:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:secret:${this.constructId}-*`], }), ); @@ -73,9 +76,9 @@ export class IndyStewardNodeInstance extends Construct { instance.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY); - new cdk.CfnOutput(this, `${id}InstanceId`, { + new cdk.CfnOutput(this, `${this.constructId}InstanceId`, { value: instance.instanceId, - exportName: `${id}InstanceId`, + exportName: `${this.constructId}InstanceId`, }); this.instance = instance; diff --git a/lib/indy/lib/constructs/indy-trustee-node-instance.ts b/lib/indy/lib/constructs/indy-trustee-node-instance.ts index e65f2dc4..db970f1b 100644 --- a/lib/indy/lib/constructs/indy-trustee-node-instance.ts +++ b/lib/indy/lib/constructs/indy-trustee-node-instance.ts @@ -9,12 +9,15 @@ export interface IndyNodeInstanceProps { export class IndyTrusteeNodeInstance extends Construct { public readonly instance: ec2.Instance; + public readonly constructId: string; constructor(scope: Construct, id: string, props: IndyNodeInstanceProps) { super(scope, id); const { vpc } = props; + constructId: id + const instance = new ec2.Instance(this, "Instance", { vpc: vpc, instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM), @@ -32,13 +35,13 @@ export class IndyTrusteeNodeInstance extends Construct { new cdk.aws_iam.PolicyStatement({ effect: cdk.aws_iam.Effect.ALLOW, actions: ["secretsmanager:GetSecretValue"], - resources: [`arn:aws:secretsmanager:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:secret:${id}-*`], + resources: [`arn:aws:secretsmanager:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:secret:${this.constructId}-*`], }), ); - new cdk.CfnOutput(this, `${id}InstanceId`, { + new cdk.CfnOutput(this, `${this.constructId}InstanceId`, { value: instance.instanceId, - exportName: `${id}InstanceId`, + exportName: `${this.constructId}InstanceId`, }); this.instance = instance; diff --git a/lib/indy/lib/indy-node-stack.ts b/lib/indy/lib/indy-node-stack.ts index 00b6b14f..68ffd93e 100644 --- a/lib/indy/lib/indy-node-stack.ts +++ b/lib/indy/lib/indy-node-stack.ts @@ -48,14 +48,14 @@ export class IndyNodeStack extends cdk.Stack { removalPolicy: cdk.RemovalPolicy.DESTROY, }); - new IndyStewardNodeInstance(this, "steward1", { vpc, clientSG, nodeSG, ansibleBucket }); - new IndyStewardNodeInstance(this, "steward2", { vpc, clientSG, nodeSG, ansibleBucket }); - new IndyStewardNodeInstance(this, "steward3", { vpc, clientSG, nodeSG, ansibleBucket }); - new IndyStewardNodeInstance(this, "steward4", { vpc, clientSG, nodeSG, ansibleBucket }); + const steward1 = new IndyStewardNodeInstance(this, "steward1", { vpc, clientSG, nodeSG, ansibleBucket }); + const steward2 = new IndyStewardNodeInstance(this, "steward2", { vpc, clientSG, nodeSG, ansibleBucket }); + const steward3 = new IndyStewardNodeInstance(this, "steward3", { vpc, clientSG, nodeSG, ansibleBucket }); + const steward4 = new IndyStewardNodeInstance(this, "steward4", { vpc, clientSG, nodeSG, ansibleBucket }); - new IndyTrusteeNodeInstance(this, "trustee1", { vpc, nodeSG }); - new IndyTrusteeNodeInstance(this, "trustee2", { vpc, nodeSG }); - new IndyTrusteeNodeInstance(this, "trustee3", { vpc, nodeSG }); + const trustee1 = new IndyTrusteeNodeInstance(this, "trustee1", { vpc, nodeSG }); + const trustee2 = new IndyTrusteeNodeInstance(this, "trustee2", { vpc, nodeSG }); + const trustee3 = new IndyTrusteeNodeInstance(this, "trustee3", { vpc, nodeSG }); new cdk.CfnOutput(this, "AnsibleFileTransferBucketName", { value: ansibleBucket.bucketName, @@ -66,5 +66,40 @@ export class IndyNodeStack extends cdk.Stack { value: cdk.Stack.of(this).region, exportName: "DeploymentRegion", }); + + new cdk.CfnOutput(this, "steward1", { + value: steward1.constructId, + exportName: "steward1", + }); + + new cdk.CfnOutput(this, "steward2", { + value: steward2.constructId, + exportName: "steward2", + }); + + new cdk.CfnOutput(this, "steward3", { + value: steward3.constructId, + exportName: "steward3", + }); + + new cdk.CfnOutput(this, "steward4", { + value: steward4.constructId, + exportName: "steward4", + }); + + new cdk.CfnOutput(this, "trustee1", { + value: trustee1.constructId, + exportName: "trustee1", + }); + + new cdk.CfnOutput(this, "trustee2", { + value: trustee2.constructId, + exportName: "trustee2", + }); + + new cdk.CfnOutput(this, "trustee3", { + value: trustee3.constructId, + exportName: "trustee3", + }); } } From 9668f9f872242a6cc444aba831cec904d6f55a3f Mon Sep 17 00:00:00 2001 From: Nikolay Vlasov Date: Wed, 17 Apr 2024 18:08:11 +1000 Subject: [PATCH 10/22] Indy. Refactoring ansible configuration for simplicity and automation --- lib/indy/.gitignore | 3 +- lib/indy/README.md | 46 ++----------------- .../{inventory.yml => inventory.yml.template} | 14 +++--- lib/indy/app.ts | 2 +- lib/indy/configure-ansible-inventory.sh | 27 +++++++++++ .../constructs/indy-steward-node-instance.ts | 11 ++--- .../constructs/indy-trustee-node-instance.ts | 9 ++-- lib/indy/lib/indy-node-stack.ts | 28 +++++------ lib/indy/package-lock.json | 20 -------- lib/indy/package.json | 3 -- 10 files changed, 62 insertions(+), 101 deletions(-) rename lib/indy/ansible/inventory/{inventory.yml => inventory.yml.template} (51%) create mode 100755 lib/indy/configure-ansible-inventory.sh delete mode 100644 lib/indy/package-lock.json diff --git a/lib/indy/.gitignore b/lib/indy/.gitignore index 45887caf..85715538 100644 --- a/lib/indy/.gitignore +++ b/lib/indy/.gitignore @@ -1,2 +1,3 @@ venv -*-deploy-output.json \ No newline at end of file +*-deploy-output.json +inventory.yml \ No newline at end of file diff --git a/lib/indy/README.md b/lib/indy/README.md index a08f2786..aab6ee3b 100644 --- a/lib/indy/README.md +++ b/lib/indy/README.md @@ -33,16 +33,7 @@ npm install #### Building resources -1. Install npm dependency packages - -```bash -cd lib/indy -pwd -# Make sure you are in aws-blockchain-node-runners/lib/indy -npm install -``` - -2. Setting up initial AWS Cloud Development Kit (CDK) +1. Setting up initial AWS Cloud Development Kit (CDK) The following command is executed only when using AWS CDK for the first time in the region where the deployment will be carried out. @@ -93,44 +84,15 @@ When running on a Mac, set the following environment variables. - Create an indentory file containing information on the EC2 instance that will build the environment. Enter the instance ID described in the CDK output results in the settings column for each node. The value of `indyNetworkStack.ansibleFileTransferBucketName` described in CDK output results is inputted to `ansible_aws_ssm_bucket_name`. When Ansible transfers files to the target host, the Amazon Simple Storage Service (Amazon S3) bucket specified here is used. ``` - $ vi inventory/inventory.yml - all: - hosts: - steward1: - ansible_aws_ssm_instance_id: i-1234567890abcdef1 - steward2: - ansible_aws_ssm_instance_id: i-1234567890abcdef2 - steward3: - ansible_aws_ssm_instance_id: i-1234567890abcdef3 - steward4: - ansible_aws_ssm_instance_id: i-1234567890abcdef4 - trustee1: - ansible_aws_ssm_instance_id: i-1234567890abcdef5 - trustee2: - ansible_aws_ssm_instance_id: i-1234567890abcdef6 - trustee3: - ansible_aws_ssm_instance_id: i-1234567890abcdef7 - children: - steward: - hosts: - steward[1:4]: - trustee: - hosts: - trustee1 - - vars: - ansible_connection: aws_ssm - ansible_aws_ssm_region: aa-example-1 - ansible_aws_ssm_s3_addressing_style: virtual - ansible_aws_ssm_bucket_name: 111122223333-ansible-file-transfer-bucket + cd .. + ./configure-ansible-inventory.sh ``` ##### Ansible parameter settings -Define the parameters referred to by Ansible in the configuration file. Set Indy's network name +Open `inventory/group_vars/all.yml` file and define the parameters referred to by Ansible in the configuration file. Set Indy's network name ``` -$ vi inventory/group_vars/all.yml INDY_NETWORK_NAME: sample-network ``` diff --git a/lib/indy/ansible/inventory/inventory.yml b/lib/indy/ansible/inventory/inventory.yml.template similarity index 51% rename from lib/indy/ansible/inventory/inventory.yml rename to lib/indy/ansible/inventory/inventory.yml.template index 75404d32..5635f849 100644 --- a/lib/indy/ansible/inventory/inventory.yml +++ b/lib/indy/ansible/inventory/inventory.yml.template @@ -1,19 +1,19 @@ all: hosts: steward1: - ansible_aws_ssm_instance_id: _steward1steward1InstanceId_ + ansible_aws_ssm_instance_id: _steward1InstanceId_ steward2: - ansible_aws_ssm_instance_id: _steward2steward1InstanceId_ + ansible_aws_ssm_instance_id: _steward2InstanceId_ steward3: - ansible_aws_ssm_instance_id: _steward3steward1InstanceId_ + ansible_aws_ssm_instance_id: _steward3InstanceId_ steward4: - ansible_aws_ssm_instance_id: _steward1steward4InstanceId_ + ansible_aws_ssm_instance_id: _steward1InstanceId_ trustee1: - ansible_aws_ssm_instance_id: _trustee1trustee1InstanceId_ + ansible_aws_ssm_instance_id: _trustee1InstanceId_ trustee2: - ansible_aws_ssm_instance_id: _trustee2trustee2InstanceId_ + ansible_aws_ssm_instance_id: _trustee2InstanceId_ trustee3: - ansible_aws_ssm_instance_id: _trustee1trustee1InstanceId_ + ansible_aws_ssm_instance_id: _trustee1InstanceId_ children: steward: hosts: diff --git a/lib/indy/app.ts b/lib/indy/app.ts index 1f4304be..cbfc3fd9 100644 --- a/lib/indy/app.ts +++ b/lib/indy/app.ts @@ -5,7 +5,7 @@ import * as nag from "cdk-nag"; import { IndyNodeStack } from "./lib/indy-node-stack"; const app = new cdk.App(); -new IndyNodeStack(app, "IndyNodeStack", {}); +new IndyNodeStack(app, "indy-sample-network-stack", {}); // Security Check cdk.Aspects.of(app).add( diff --git a/lib/indy/configure-ansible-inventory.sh b/lib/indy/configure-ansible-inventory.sh new file mode 100755 index 00000000..5aa4a044 --- /dev/null +++ b/lib/indy/configure-ansible-inventory.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +STEWARD1=$(jq -r .[].steward1Output ./indy-test-deploy-output.json) +STEWARD2=$(jq -r .[].steward2Output ./indy-test-deploy-output.json) +STEWARD3=$(jq -r .[].steward3Output ./indy-test-deploy-output.json) +STEWARD4=$(jq -r .[].steward4Output ./indy-test-deploy-output.json) + +TRUSTEE1=$(jq -r .[].trustee1Output ./indy-test-deploy-output.json) +TRUSTEE2=$(jq -r .[].trustee2Output ./indy-test-deploy-output.json) +TRUSTEE3=$(jq -r .[].trustee3Output ./indy-test-deploy-output.json) + +ANSIBLE_BUCKET_NAME=$(jq -r .[].AnsibleFileTransferBucketName ./indy-test-deploy-output.json) +AWS_DEPLOYMENT_REGION=$(jq -r .[].DeploymentRegion ./indy-test-deploy-output.json) + +cp ./ansible/inventory/inventory.yml.template ./ansible/inventory/inventory.yml + +sed -i "s/_steward1InstanceId_/$STEWARD1/" ./ansible/inventory/inventory.yml +sed -i "s/_steward2InstanceId_/$STEWARD2/" ./ansible/inventory/inventory.yml +sed -i "s/_steward3InstanceId_/$STEWARD3/" ./ansible/inventory/inventory.yml +sed -i "s/_steward4InstanceId_/$STEWARD4/" ./ansible/inventory/inventory.yml + +sed -i "s/_trustee1InstanceId_/$TRUSTEE1/" ./ansible/inventory/inventory.yml +sed -i "s/_trustee2InstanceId_/$TRUSTEE2/" ./ansible/inventory/inventory.yml +sed -i "s/_trustee3InstanceId_/$TRUSTEE3/" ./ansible/inventory/inventory.yml + +sed -i "s/_ansible-file-transfer-bucket_/$ANSIBLE_BUCKET_NAME/" ./ansible/inventory/inventory.yml +sed -i "s/_aws_region_/$AWS_DEPLOYMENT_REGION/" ./ansible/inventory/inventory.yml \ No newline at end of file diff --git a/lib/indy/lib/constructs/indy-steward-node-instance.ts b/lib/indy/lib/constructs/indy-steward-node-instance.ts index 876804cb..4a864de7 100644 --- a/lib/indy/lib/constructs/indy-steward-node-instance.ts +++ b/lib/indy/lib/constructs/indy-steward-node-instance.ts @@ -14,15 +14,12 @@ export interface IndyNodeInstanceProps { export class IndyStewardNodeInstance extends Construct { public readonly instance: ec2.Instance; - public readonly constructId: string; constructor(scope: Construct, id: string, props: IndyNodeInstanceProps) { super(scope, id); const { vpc, clientSG, nodeSG } = props; - constructId: id - const clientNic: ec2.CfnInstance.NetworkInterfaceProperty = { deviceIndex: "0", groupSet: [clientSG.securityGroupId], @@ -57,13 +54,13 @@ export class IndyStewardNodeInstance extends Construct { ], }); - cdk.Tags.of(instance).add("Name", this.constructId); + cdk.Tags.of(instance).add("Name", id); instance.addToRolePolicy( new cdk.aws_iam.PolicyStatement({ effect: cdk.aws_iam.Effect.ALLOW, actions: ["secretsmanager:GetSecretValue"], - resources: [`arn:aws:secretsmanager:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:secret:${this.constructId}-*`], + resources: [`arn:aws:secretsmanager:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:secret:${id}-*`], }), ); @@ -76,9 +73,9 @@ export class IndyStewardNodeInstance extends Construct { instance.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY); - new cdk.CfnOutput(this, `${this.constructId}InstanceId`, { + new cdk.CfnOutput(this, `${id}InstanceId`, { value: instance.instanceId, - exportName: `${this.constructId}InstanceId`, + exportName: `${id}InstanceId`, }); this.instance = instance; diff --git a/lib/indy/lib/constructs/indy-trustee-node-instance.ts b/lib/indy/lib/constructs/indy-trustee-node-instance.ts index db970f1b..e65f2dc4 100644 --- a/lib/indy/lib/constructs/indy-trustee-node-instance.ts +++ b/lib/indy/lib/constructs/indy-trustee-node-instance.ts @@ -9,15 +9,12 @@ export interface IndyNodeInstanceProps { export class IndyTrusteeNodeInstance extends Construct { public readonly instance: ec2.Instance; - public readonly constructId: string; constructor(scope: Construct, id: string, props: IndyNodeInstanceProps) { super(scope, id); const { vpc } = props; - constructId: id - const instance = new ec2.Instance(this, "Instance", { vpc: vpc, instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM), @@ -35,13 +32,13 @@ export class IndyTrusteeNodeInstance extends Construct { new cdk.aws_iam.PolicyStatement({ effect: cdk.aws_iam.Effect.ALLOW, actions: ["secretsmanager:GetSecretValue"], - resources: [`arn:aws:secretsmanager:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:secret:${this.constructId}-*`], + resources: [`arn:aws:secretsmanager:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:secret:${id}-*`], }), ); - new cdk.CfnOutput(this, `${this.constructId}InstanceId`, { + new cdk.CfnOutput(this, `${id}InstanceId`, { value: instance.instanceId, - exportName: `${this.constructId}InstanceId`, + exportName: `${id}InstanceId`, }); this.instance = instance; diff --git a/lib/indy/lib/indy-node-stack.ts b/lib/indy/lib/indy-node-stack.ts index 68ffd93e..75a14d2d 100644 --- a/lib/indy/lib/indy-node-stack.ts +++ b/lib/indy/lib/indy-node-stack.ts @@ -67,38 +67,38 @@ export class IndyNodeStack extends cdk.Stack { exportName: "DeploymentRegion", }); - new cdk.CfnOutput(this, "steward1", { - value: steward1.constructId, + new cdk.CfnOutput(this, "steward1Output", { + value: steward1.instance.instanceId, exportName: "steward1", }); - new cdk.CfnOutput(this, "steward2", { - value: steward2.constructId, + new cdk.CfnOutput(this, "steward2Output", { + value: steward2.instance.instanceId, exportName: "steward2", }); - new cdk.CfnOutput(this, "steward3", { - value: steward3.constructId, + new cdk.CfnOutput(this, "steward3Output", { + value: steward3.instance.instanceId, exportName: "steward3", }); - new cdk.CfnOutput(this, "steward4", { - value: steward4.constructId, + new cdk.CfnOutput(this, "steward4Output", { + value: steward4.instance.instanceId, exportName: "steward4", }); - new cdk.CfnOutput(this, "trustee1", { - value: trustee1.constructId, + new cdk.CfnOutput(this, "trustee1Output", { + value: trustee1.instance.instanceId, exportName: "trustee1", }); - new cdk.CfnOutput(this, "trustee2", { - value: trustee2.constructId, + new cdk.CfnOutput(this, "trustee2Output", { + value: trustee2.instance.instanceId, exportName: "trustee2", }); - new cdk.CfnOutput(this, "trustee3", { - value: trustee3.constructId, + new cdk.CfnOutput(this, "trustee3Output", { + value: trustee3.instance.instanceId, exportName: "trustee3", }); } diff --git a/lib/indy/package-lock.json b/lib/indy/package-lock.json deleted file mode 100644 index 0c769d99..00000000 --- a/lib/indy/package-lock.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "aws-blockchain-node-runners-indy", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "aws-blockchain-node-runners-indy", - "version": "0.1.0", - "dependencies": { - "fs": "^0.0.1-security" - } - }, - "node_modules/fs": { - "version": "0.0.1-security", - "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", - "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" - } - } -} diff --git a/lib/indy/package.json b/lib/indy/package.json index 4dc47052..8511125f 100644 --- a/lib/indy/package.json +++ b/lib/indy/package.json @@ -7,8 +7,5 @@ "test": "npx jest", "cdk": "npx cdk", "scan-cdk": "npx cdk synth" - }, - "dependencies": { - "fs": "^0.0.1-security" } } From 085528ed9b6c0219f40f192cbf9249216ecfbb13 Mon Sep 17 00:00:00 2001 From: Nikolay Vlasov Date: Thu, 18 Apr 2024 15:24:51 +1000 Subject: [PATCH 11/22] Indy. Fixing bugs in ansible inventory template file --- lib/indy/ansible/inventory/inventory.yml.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/indy/ansible/inventory/inventory.yml.template b/lib/indy/ansible/inventory/inventory.yml.template index 5635f849..4c243bce 100644 --- a/lib/indy/ansible/inventory/inventory.yml.template +++ b/lib/indy/ansible/inventory/inventory.yml.template @@ -7,13 +7,13 @@ all: steward3: ansible_aws_ssm_instance_id: _steward3InstanceId_ steward4: - ansible_aws_ssm_instance_id: _steward1InstanceId_ + ansible_aws_ssm_instance_id: _steward4InstanceId_ trustee1: ansible_aws_ssm_instance_id: _trustee1InstanceId_ trustee2: ansible_aws_ssm_instance_id: _trustee2InstanceId_ trustee3: - ansible_aws_ssm_instance_id: _trustee1InstanceId_ + ansible_aws_ssm_instance_id: _trustee3InstanceId_ children: steward: hosts: From 31da2e05aa987db88e3f241580d1e073221396a5 Mon Sep 17 00:00:00 2001 From: fsatsuki Date: Fri, 26 Apr 2024 04:38:32 +0000 Subject: [PATCH 12/22] cdk-nag check --- lib/indy/.env-sample | 23 ++++ lib/indy/README.md | 12 ++ lib/indy/README_ja.md | 66 +++-------- .../roles/fetch_genesis_files/tasks/main.yml | 6 +- .../templates/indy_config.py.j2 | 2 +- lib/indy/app.ts | 5 +- lib/indy/lib/config/indyConfig.interface.ts | 12 ++ lib/indy/lib/config/indyConfig.ts | 51 +++++++++ .../constructs/indy-steward-node-instance.ts | 31 ++++- .../constructs/indy-trustee-node-instance.ts | 39 +++++++ lib/indy/lib/indy-node-stack.ts | 108 ++++++++++++++++-- 11 files changed, 291 insertions(+), 64 deletions(-) create mode 100644 lib/indy/.env-sample create mode 100644 lib/indy/lib/config/indyConfig.interface.ts create mode 100644 lib/indy/lib/config/indyConfig.ts diff --git a/lib/indy/.env-sample b/lib/indy/.env-sample new file mode 100644 index 00000000..ced87795 --- /dev/null +++ b/lib/indy/.env-sample @@ -0,0 +1,23 @@ +############################################################## +# Example configuration for Hyperledger Indy nodes runner app on AWS # +############################################################## + +# Set the AWS account is and region for your environment +AWS_ACCOUNT_ID="xxxxxxxxxxx" +AWS_REGION="us-east-2" + +# Student node configuration +INDY_STUDENT_INSTANCE_TYPE="t3.medium" +INDY_STUDENT__CPU_TYPE="ARM_64" # IMPORTANT: Make sure the CPU type matches the instance type used +INDY_STUDENT_DATA_VOL_SIZE="200" # Minimum values in Gibibytes: +INDY_STUDENT_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families +INDY_STUDENT_DATA_VOL_IOPS="6000" # Max IOPS for EBS volumes (not applicable for "instance-store") +INDY_STUDENT_DATA_VOL_THROUGHPUT="400" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") + +# Trustee node configuration +INDY_TRUSTEE_INSTANCE_TYPE="t3.large" +INDY_TRUSTEE__CPU_TYPE="ARM_64" # IMPORTANT: Make sure the CPU type matches the instance type used +INDY_TRUSTEE_DATA_VOL_SIZE="30" # Minimum values in Gibibytes: +INDY_TRUSTEE_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families +INDY_TRUSTEE_DATA_VOL_IOPS="6000" # Max IOPS for EBS volumes (not applicable for "instance-store") +INDY_TRUSTEE_DATA_VOL_THROUGHPUT="400" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") diff --git a/lib/indy/README.md b/lib/indy/README.md index aab6ee3b..d0e4409a 100644 --- a/lib/indy/README.md +++ b/lib/indy/README.md @@ -33,6 +33,18 @@ npm install #### Building resources +1. Configure your setup + +Create your own copy of `.env` file and edit it: +```bash + # Make sure you are in aws-blockchain-node-runners/lib/ethereum + cd lib/ethereum + pwd + cp ./sample-configs/.env-geth-lighthouse .env + nano .env +``` + **NOTE:** You can find more examples inside the `sample-configs` directory. + 1. Setting up initial AWS Cloud Development Kit (CDK) The following command is executed only when using AWS CDK for the first time in the region where the deployment will be carried out. diff --git a/lib/indy/README_ja.md b/lib/indy/README_ja.md index 226188da..c7bb6e96 100644 --- a/lib/indy/README_ja.md +++ b/lib/indy/README_ja.md @@ -29,13 +29,15 @@ npm install #### リソースの構築 -1. npm の依存パッケージをインストール +1. Configure your setup +Create your own copy of `.env` file and edit it: ```bash -cd lib/indy -pwd -# Make sure you are in aws-blockchain-node-runners/lib/indy -npm install + # Make sure you are in aws-blockchain-node-runners/lib/ethereum + cd lib/indy + pwd + cp .env-sample .env + nano .env ``` 2. AWS Cloud Development Kit (CDK) の初期設定 @@ -49,7 +51,7 @@ npx cdk bootstrap 3. CDK でリソースの構築 ```bash -npx cdk deploy +npx cdk deploy --json --outputs-file indy-test-deploy-output.json Outputs: IndyNetworkStack.AnsibleFileTransferBucketName = 111122223333-ansible-file-transfer-bucket @@ -75,64 +77,28 @@ Macで実行する場合は次の環境変数を設定する。 - pythonの仮想環境を作成しansibleを導入する ``` $ cd ansible - $ python3 -m venv .venv - $ source .venv/bin/activate + $ python3 -m venv venv + $ source venv/bin/activate ``` ``` $ pip install -r requirements.txt ``` -## AnsibleとSession Manager - -- EC2 Instanceに対してSession Managerを使用したSSHアクセスを実現するために、 [Install the Session Manager plugin for the AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) を参照して、Session Manager Pluginをインストールする。Session Managerを使用することでセキュリティグループの設定をすることなく、インターネットからアクセスできないPrivate SubnetのEC2 Instanceに対してAnsibleによるデプロイが可能となる。 +##### Describe instance information to be built in inventory.yml -- AnsibleがAWS Systems Manager Session Managerを使用してEC2にSSHログインするためのプラグインをインストールする。 - ``` - $ ansible-galaxy collection install community.aws - ``` +- Create an indentory file containing information on the EC2 instance that will build the environment. Enter the instance ID described in the CDK output results in the settings column for each node. The value of `indyNetworkStack.ansibleFileTransferBucketName` described in CDK output results is inputted to `ansible_aws_ssm_bucket_name`. When Ansible transfers files to the target host, the Amazon Simple Storage Service (Amazon S3) bucket specified here is used. -## 構築対象のインスタンス情報をInventory.ymlに記載する -- 環境構築を行うEC2インスタンスの情報を記載したIndentoryファイルを作成する。CDKの出力結果に記載されているインスタンスのIDをそれぞれのノードの設定欄に記入する。 `ansible_aws_ssm_bucket_name` には CDKの出力結果に記載されている `IndyNetworkStack.AnsibleFileTransferBucketName` の値を入力する。Ansibleが対象のホストに対してファイルを転送する時に、ここで指定したAmazon Simple Storage Service(Amazon S3) Bucketを使用する。 ``` - $ vi inventory/inventory.yml - all: - hosts: - steward1: - ansible_aws_ssm_instance_id: i-1234567890abcdef1 - steward2: - ansible_aws_ssm_instance_id: i-1234567890abcdef2 - steward3: - ansible_aws_ssm_instance_id: i-1234567890abcdef3 - steward4: - ansible_aws_ssm_instance_id: i-1234567890abcdef4 - trustee1: - ansible_aws_ssm_instance_id: i-1234567890abcdef5 - trustee2: - ansible_aws_ssm_instance_id: i-1234567890abcdef6 - trustee3: - ansible_aws_ssm_instance_id: i-1234567890abcdef7 - children: - steward: - hosts: - steward[1:4]: - trustee: - hosts: - trustee1 - - vars: - ansible_connection: aws_ssm - ansible_aws_ssm_region: aa-example-1 - ansible_aws_ssm_s3_addressing_style: virtual - ansible_aws_ssm_bucket_name: 111122223333-ansible-file-transfer-bucket + cd .. + ./configure-ansible-inventory.sh ``` ## Ansibleの設定 -Ansibleが参照するパラメータを設定ファイルで定義する。 +Open `inventory/group_vars/all.yml` file and define the parameters referred to by Ansible in the configuration file. Set Indy's network name ``` -$ vi inventory/group_vars/all.yml -INDY_NETEORK_NAME: sample-network +INDY_NETWORK_NAME: sample-network ``` ​ ## Ansibleによる環境構築の実行 diff --git a/lib/indy/ansible/roles/fetch_genesis_files/tasks/main.yml b/lib/indy/ansible/roles/fetch_genesis_files/tasks/main.yml index 8279a4e3..0e8f5f19 100644 --- a/lib/indy/ansible/roles/fetch_genesis_files/tasks/main.yml +++ b/lib/indy/ansible/roles/fetch_genesis_files/tasks/main.yml @@ -2,7 +2,7 @@ - name: make indy directory become: yes ansible.builtin.file: - path: "/var/lib/indy/{{ INDY_NETEORK_NAME }}" + path: "/var/lib/indy/{{ INDY_NETWORK_NAME }}" state: directory group: indy owner: indy @@ -22,7 +22,7 @@ amazon.aws.s3_object: bucket: "{{ S3_BUCKET_NAME }}" object: "/genesis/{{ item }}" - dest: "/var/lib/indy/{{ INDY_NETEORK_NAME }}/{{ item }}" + dest: "/var/lib/indy/{{ INDY_NETWORK_NAME }}/{{ item }}" mode: get with_items: - pool_transactions_genesis @@ -31,7 +31,7 @@ - name: change owner and group of genesis files become: yes ansible.builtin.file: - path: "/var/lib/indy/{{ INDY_NETEORK_NAME }}/{{ item }}" + path: "/var/lib/indy/{{ INDY_NETWORK_NAME }}/{{ item }}" state: file group: indy owner: indy diff --git a/lib/indy/ansible/roles/install_dependencies/templates/indy_config.py.j2 b/lib/indy/ansible/roles/install_dependencies/templates/indy_config.py.j2 index ee539ec9..df4c77af 100644 --- a/lib/indy/ansible/roles/install_dependencies/templates/indy_config.py.j2 +++ b/lib/indy/ansible/roles/install_dependencies/templates/indy_config.py.j2 @@ -1,5 +1,5 @@ # Current network -NETWORK_NAME = '{{ INDY_NETEORK_NAME }}' +NETWORK_NAME = '{{ INDY_NETWORK_NAME }}' # Disable stdout logging enableStdOutLogging = False diff --git a/lib/indy/app.ts b/lib/indy/app.ts index cbfc3fd9..b220f698 100644 --- a/lib/indy/app.ts +++ b/lib/indy/app.ts @@ -2,10 +2,13 @@ import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import * as nag from "cdk-nag"; +import * as config from "./lib/config/indyConfig"; import { IndyNodeStack } from "./lib/indy-node-stack"; const app = new cdk.App(); -new IndyNodeStack(app, "indy-sample-network-stack", {}); +new IndyNodeStack(app, "indy-sample-network-stack", { + env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, +}); // Security Check cdk.Aspects.of(app).add( diff --git a/lib/indy/lib/config/indyConfig.interface.ts b/lib/indy/lib/config/indyConfig.interface.ts new file mode 100644 index 00000000..01aa46f7 --- /dev/null +++ b/lib/indy/lib/config/indyConfig.interface.ts @@ -0,0 +1,12 @@ +import * as configTypes from "../../../constructs/config.interface"; + +export type IndyClientCombination = "steward" | "trustee"; + +export interface IndyDataVolumeConfig extends configTypes.DataVolumeConfig { +} + +export interface IndyBaseConfig extends configTypes.BaseConfig { +} + +export interface IndyNodeConfig extends configTypes.SingleNodeConfig { +} \ No newline at end of file diff --git a/lib/indy/lib/config/indyConfig.ts b/lib/indy/lib/config/indyConfig.ts new file mode 100644 index 00000000..d6943aa2 --- /dev/null +++ b/lib/indy/lib/config/indyConfig.ts @@ -0,0 +1,51 @@ +import * as ec2 from "aws-cdk-lib/aws-ec2"; +import * as configTypes from "./indyConfig.interface"; +import * as constants from "../../../constructs/constants"; + +const parseDataVolumeType = (dataVolumeType: string) => { + switch (dataVolumeType) { + case "gp3": + return ec2.EbsDeviceVolumeType.GP3; + case "io2": + return ec2.EbsDeviceVolumeType.IO2; + case "io1": + return ec2.EbsDeviceVolumeType.IO1; + case "instance-store": + return constants.InstanceStoreageDeviceVolumeType; + default: + return ec2.EbsDeviceVolumeType.GP3; + } +} + +export const vpcAddresses: string = process.env.INDY_VPC_ADDRESSES || "10.0.0.0/16" + +export const baseConfig: configTypes.IndyBaseConfig = { + accountId: process.env.AWS_ACCOUNT_ID || "xxxxxxxxxxx", // Set your target AWS Account ID + region: process.env.AWS_REGION || "us-east-2", // Set your target AWS Region +}; + +export const studentNodeConfig: configTypes.IndyNodeConfig = { + instanceType: new ec2.InstanceType(process.env.INDY_STUDENT_INSTANCE_TYPE ? process.env.INDY_STUDENT_INSTANCE_TYPE : "m6g.2xlarge"), //InstanceType.of(InstanceClass.M6G, InstanceSize.XLARGE2), + instanceCpuType: process.env.INDY_STUDENT__CPU_TYPE?.toLowerCase() == "x86_64" ? ec2.AmazonLinuxCpuType.X86_64 : ec2.AmazonLinuxCpuType.ARM_64 , + dataVolumes: [ + { + sizeGiB: process.env.INDY_STUDENT_DATA_VOL_SIZE ? parseInt(process.env.INDY_STUDENT_DATA_VOL_SIZE): 50, // Minimum values in Gibibytes: + type: parseDataVolumeType(process.env.INDY_STUDENT_DATA_VOL_TYPE?.toLowerCase() ? process.env.INDY_STUDENT_DATA_VOL_TYPE?.toLowerCase() : "gp3"), + iops: process.env.INDY_STUDENT_DATA_VOL_IOPS ? parseInt(process.env.INDY_STUDENT_DATA_VOL_IOPS): 7000, + throughput: process.env.INDY_STUDENT_DATA_VOL_THROUGHPUT ? parseInt(process.env.INDY_STUDENT_DATA_VOL_THROUGHPUT): 250, + } + ] +}; + +export const trusteeNodeConfig: configTypes.IndyNodeConfig = { + instanceType: new ec2.InstanceType(process.env.INDY_STUDENT_INSTANCE_TYPE ? process.env.INDY_STUDENT_INSTANCE_TYPE : "m6g.2xlarge"), //InstanceType.of(InstanceClass.M6G, InstanceSize.XLARGE2), + instanceCpuType: process.env.INDY_STUDENT__CPU_TYPE?.toLowerCase() == "x86_64" ? ec2.AmazonLinuxCpuType.X86_64 : ec2.AmazonLinuxCpuType.ARM_64 , + dataVolumes: [ + { + sizeGiB: process.env.INDY_STUDENT_DATA_VOL_SIZE ? parseInt(process.env.INDY_STUDENT_DATA_VOL_SIZE): 50, // Minimum values in Gibibytes: + type: parseDataVolumeType(process.env.INDY_STUDENT_DATA_VOL_TYPE?.toLowerCase() ? process.env.INDY_STUDENT_DATA_VOL_TYPE?.toLowerCase() : "gp3"), + iops: process.env.INDY_STUDENT_DATA_VOL_IOPS ? parseInt(process.env.INDY_STUDENT_DATA_VOL_IOPS): 7000, + throughput: process.env.INDY_STUDENT_DATA_VOL_THROUGHPUT ? parseInt(process.env.INDY_STUDENT_DATA_VOL_THROUGHPUT): 250, + } + ] +}; \ No newline at end of file diff --git a/lib/indy/lib/constructs/indy-steward-node-instance.ts b/lib/indy/lib/constructs/indy-steward-node-instance.ts index 4a864de7..c1bff255 100644 --- a/lib/indy/lib/constructs/indy-steward-node-instance.ts +++ b/lib/indy/lib/constructs/indy-steward-node-instance.ts @@ -2,14 +2,19 @@ import * as cdk from "aws-cdk-lib"; import * as ec2 from "aws-cdk-lib/aws-ec2"; import * as S3 from "aws-cdk-lib/aws-s3"; import { Construct } from "constructs"; - +import * as nag from "cdk-nag"; import { readFileSync } from "fs"; +import * as configTypes from "../config/indyConfig.interface"; + export interface IndyNodeInstanceProps { readonly vpc: ec2.IVpc; readonly clientSG: ec2.ISecurityGroup; readonly nodeSG: ec2.ISecurityGroup; readonly ansibleBucket: S3.Bucket; + readonly instanceType: ec2.InstanceType; + readonly instanceCpuType: ec2.AmazonLinuxCpuType; + readonly dataVolumes: configTypes.IndyDataVolumeConfig[]; } export class IndyStewardNodeInstance extends Construct { @@ -79,5 +84,29 @@ export class IndyStewardNodeInstance extends Construct { }); this.instance = instance; + + nag.NagSuppressions.addResourceSuppressions( + this, + [ + { + id: "AwsSolutions-IAM4", + reason: "AmazonSSMManagedInstanceCore are restrictive enough" + }, + { + id: "AwsSolutions-IAM5", + reason: "It is ok to use wildcard in the secret name. this is a specific target" + }, + { + id: "AwsSolutions-EC28", + reason: "Using basic monitoring to save costs" + }, + { + id: "AwsSolutions-EC29", + reason: "Its Ok to terminate this instance as long as we have the data in the snapshot", + + }, + ], + true + ); } } diff --git a/lib/indy/lib/constructs/indy-trustee-node-instance.ts b/lib/indy/lib/constructs/indy-trustee-node-instance.ts index e65f2dc4..beb190c4 100644 --- a/lib/indy/lib/constructs/indy-trustee-node-instance.ts +++ b/lib/indy/lib/constructs/indy-trustee-node-instance.ts @@ -1,10 +1,15 @@ import * as cdk from "aws-cdk-lib"; import * as ec2 from "aws-cdk-lib/aws-ec2"; +import * as nag from "cdk-nag"; import { Construct } from "constructs"; +import * as configTypes from "../config/indyConfig.interface"; export interface IndyNodeInstanceProps { readonly vpc: ec2.IVpc; readonly nodeSG: ec2.ISecurityGroup; + readonly instanceType: ec2.InstanceType; + readonly instanceCpuType: ec2.AmazonLinuxCpuType; + readonly dataVolumes: configTypes.IndyDataVolumeConfig[]; } export class IndyTrusteeNodeInstance extends Construct { @@ -23,6 +28,16 @@ export class IndyTrusteeNodeInstance extends Construct { ), ssmSessionPermissions: true, securityGroup: props.nodeSG, + blockDevices: [ + { + deviceName: "/dev/sda1", + volume: ec2.BlockDeviceVolume.ebs(30, { + volumeType: ec2.EbsDeviceVolumeType.GP3, + encrypted: true, + deleteOnTermination: true, + }), + }, + ], }); instance.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY); @@ -42,5 +57,29 @@ export class IndyTrusteeNodeInstance extends Construct { }); this.instance = instance; + + nag.NagSuppressions.addResourceSuppressions( + this, + [ + { + id: "AwsSolutions-IAM4", + reason: "AmazonSSMManagedInstanceCore are restrictive enough" + }, + { + id: "AwsSolutions-IAM5", + reason: "It is ok to use wildcard in the secret name. this is a specific target" + }, + { + id: "AwsSolutions-EC28", + reason: "Using basic monitoring to save costs" + }, + { + id: "AwsSolutions-EC29", + reason: "Its Ok to terminate this instance as long as we have the data in the snapshot", + + }, + ], + true + ); } } diff --git a/lib/indy/lib/indy-node-stack.ts b/lib/indy/lib/indy-node-stack.ts index 75a14d2d..de7ba206 100644 --- a/lib/indy/lib/indy-node-stack.ts +++ b/lib/indy/lib/indy-node-stack.ts @@ -2,6 +2,8 @@ import * as cdk from "aws-cdk-lib"; import { Construct } from "constructs"; import * as ec2 from "aws-cdk-lib/aws-ec2"; import * as s3 from "aws-cdk-lib/aws-s3"; +import * as config from "../lib/config/indyConfig"; +import * as nag from "cdk-nag"; import { IndyStewardNodeInstance } from "./constructs/indy-steward-node-instance"; import { IndyTrusteeNodeInstance } from "./constructs/indy-trustee-node-instance"; @@ -10,8 +12,35 @@ export class IndyNodeStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); + const serverAccessLogBucket = new s3.Bucket(this, "serverAccessLogBucket", { + encryption: s3.BucketEncryption.S3_MANAGED, + blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, + removalPolicy: cdk.RemovalPolicy.RETAIN, + versioned: true, + enforceSSL: true, + autoDeleteObjects: false, + }) + const vpc = new ec2.Vpc(this, "IndyVpc", { - ipAddresses: ec2.IpAddresses.cidr("10.0.0.0/16"), + ipAddresses: ec2.IpAddresses.cidr(config.vpcAddresses), + flowLogs: { + s3: { + destination: ec2.FlowLogDestination.toS3( + new s3.Bucket(this, "VpcFlowLogBucket", { + encryption: s3.BucketEncryption.S3_MANAGED, + blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, + removalPolicy: cdk.RemovalPolicy.RETAIN, + versioned: true, + enforceSSL: true, + autoDeleteObjects: false, + serverAccessLogsBucket: serverAccessLogBucket, + serverAccessLogsPrefix: "vpcFlowLogs", + }), + "vpcFlowLogs" + ), + trafficType: ec2.FlowLogTrafficType.ALL, + } + } }); // SecurityGroup of Nodes for Clients @@ -46,16 +75,68 @@ export class IndyNodeStack extends cdk.Stack { enforceSSL: true, autoDeleteObjects: true, removalPolicy: cdk.RemovalPolicy.DESTROY, + serverAccessLogsBucket: serverAccessLogBucket, + serverAccessLogsPrefix: "AnsibleFileTransferBucket", }); - const steward1 = new IndyStewardNodeInstance(this, "steward1", { vpc, clientSG, nodeSG, ansibleBucket }); - const steward2 = new IndyStewardNodeInstance(this, "steward2", { vpc, clientSG, nodeSG, ansibleBucket }); - const steward3 = new IndyStewardNodeInstance(this, "steward3", { vpc, clientSG, nodeSG, ansibleBucket }); - const steward4 = new IndyStewardNodeInstance(this, "steward4", { vpc, clientSG, nodeSG, ansibleBucket }); + const steward1 = new IndyStewardNodeInstance(this, "steward1", { + vpc, + clientSG, + nodeSG, + ansibleBucket, + instanceType: config.studentNodeConfig.instanceType, + instanceCpuType: config.studentNodeConfig.instanceCpuType, + dataVolumes: config.studentNodeConfig.dataVolumes, + }); + const steward2 = new IndyStewardNodeInstance(this, "steward2", { + vpc, + clientSG, + nodeSG, + ansibleBucket, + instanceType: config.studentNodeConfig.instanceType, + instanceCpuType: config.studentNodeConfig.instanceCpuType, + dataVolumes: config.studentNodeConfig.dataVolumes + }); + const steward3 = new IndyStewardNodeInstance(this, "steward3", { + vpc, + clientSG, + nodeSG, + ansibleBucket, + instanceType: config.studentNodeConfig.instanceType, + instanceCpuType: config.studentNodeConfig.instanceCpuType, + dataVolumes: config.studentNodeConfig.dataVolumes + }); + const steward4 = new IndyStewardNodeInstance(this, "steward4", { + vpc, + clientSG, + nodeSG, + ansibleBucket, + instanceType: config.studentNodeConfig.instanceType, + instanceCpuType: config.studentNodeConfig.instanceCpuType, + dataVolumes: config.studentNodeConfig.dataVolumes + }); - const trustee1 = new IndyTrusteeNodeInstance(this, "trustee1", { vpc, nodeSG }); - const trustee2 = new IndyTrusteeNodeInstance(this, "trustee2", { vpc, nodeSG }); - const trustee3 = new IndyTrusteeNodeInstance(this, "trustee3", { vpc, nodeSG }); + const trustee1 = new IndyTrusteeNodeInstance(this, "trustee1", { + vpc, + nodeSG, + instanceType: config.trusteeNodeConfig.instanceType, + instanceCpuType: config.trusteeNodeConfig.instanceCpuType, + dataVolumes: config.trusteeNodeConfig.dataVolumes + }); + const trustee2 = new IndyTrusteeNodeInstance(this, "trustee2", { + vpc, + nodeSG, + instanceType: config.trusteeNodeConfig.instanceType, + instanceCpuType: config.trusteeNodeConfig.instanceCpuType, + dataVolumes: config.trusteeNodeConfig.dataVolumes + }); + const trustee3 = new IndyTrusteeNodeInstance(this, "trustee3", { + vpc, + nodeSG, + instanceType: config.trusteeNodeConfig.instanceType, + instanceCpuType: config.trusteeNodeConfig.instanceCpuType, + dataVolumes: config.trusteeNodeConfig.dataVolumes + }); new cdk.CfnOutput(this, "AnsibleFileTransferBucketName", { value: ansibleBucket.bucketName, @@ -101,5 +182,16 @@ export class IndyNodeStack extends cdk.Stack { value: trustee3.instance.instanceId, exportName: "trustee3", }); + + nag.NagSuppressions.addResourceSuppressions( + this, + [ + { + id: "AwsSolutions-S1", + reason: "An access log bucket does not require an access log bucket." + } + ], + true + ); } } From 42e4a85e5c599049d0266b361317f5dad823270c Mon Sep 17 00:00:00 2001 From: Nikolay Vlasov Date: Tue, 7 May 2024 14:08:03 +1000 Subject: [PATCH 13/22] Indy. Refactoring code and README --- lib/indy/README.md | 10 ++++----- lib/indy/lib/config/indyConfig.ts | 26 +++++++++++------------ lib/indy/lib/indy-node-stack.ts | 24 ++++++++++----------- lib/indy/{ => sample-configs}/.env-sample | 12 +++++------ 4 files changed, 36 insertions(+), 36 deletions(-) rename lib/indy/{ => sample-configs}/.env-sample (78%) diff --git a/lib/indy/README.md b/lib/indy/README.md index d0e4409a..18d13c3d 100644 --- a/lib/indy/README.md +++ b/lib/indy/README.md @@ -37,10 +37,10 @@ npm install Create your own copy of `.env` file and edit it: ```bash - # Make sure you are in aws-blockchain-node-runners/lib/ethereum - cd lib/ethereum + # Make sure you are in aws-blockchain-node-runners/lib/indy + cd lib/indy pwd - cp ./sample-configs/.env-geth-lighthouse .env + cp ./sample-configs/.env-sample .env nano .env ``` **NOTE:** You can find more examples inside the `sample-configs` directory. @@ -50,7 +50,7 @@ Create your own copy of `.env` file and edit it: The following command is executed only when using AWS CDK for the first time in the region where the deployment will be carried out. ```bash -npx cdk bootstrap +npx cdk bootstrap aws:/// ``` 3. Deploying resources with CDK @@ -93,7 +93,7 @@ When running on a Mac, set the following environment variables. ##### Describe instance information to be built in inventory.yml -- Create an indentory file containing information on the EC2 instance that will build the environment. Enter the instance ID described in the CDK output results in the settings column for each node. The value of `indyNetworkStack.ansibleFileTransferBucketName` described in CDK output results is inputted to `ansible_aws_ssm_bucket_name`. When Ansible transfers files to the target host, the Amazon Simple Storage Service (Amazon S3) bucket specified here is used. +- Create an inventory file containing information on the EC2 instance that will build the environment. Enter the instance ID described in the CDK output results in the settings column for each node. The value of `indyNetworkStack.ansibleFileTransferBucketName` described in CDK output results is inputted to `ansible_aws_ssm_bucket_name`. When Ansible transfers files to the target host, the Amazon Simple Storage Service (Amazon S3) bucket specified here is used. ``` cd .. diff --git a/lib/indy/lib/config/indyConfig.ts b/lib/indy/lib/config/indyConfig.ts index d6943aa2..48aca12b 100644 --- a/lib/indy/lib/config/indyConfig.ts +++ b/lib/indy/lib/config/indyConfig.ts @@ -24,28 +24,28 @@ export const baseConfig: configTypes.IndyBaseConfig = { region: process.env.AWS_REGION || "us-east-2", // Set your target AWS Region }; -export const studentNodeConfig: configTypes.IndyNodeConfig = { - instanceType: new ec2.InstanceType(process.env.INDY_STUDENT_INSTANCE_TYPE ? process.env.INDY_STUDENT_INSTANCE_TYPE : "m6g.2xlarge"), //InstanceType.of(InstanceClass.M6G, InstanceSize.XLARGE2), - instanceCpuType: process.env.INDY_STUDENT__CPU_TYPE?.toLowerCase() == "x86_64" ? ec2.AmazonLinuxCpuType.X86_64 : ec2.AmazonLinuxCpuType.ARM_64 , +export const stewardNodeConfig: configTypes.IndyNodeConfig = { + instanceType: new ec2.InstanceType(process.env.INDY_STEWARD_INSTANCE_TYPE ? process.env.INDY_STEWARD_INSTANCE_TYPE : "m6g.2xlarge"), //InstanceType.of(InstanceClass.M6G, InstanceSize.XLARGE2), + instanceCpuType: process.env.INDY_STEWARD_CPU_TYPE?.toLowerCase() == "x86_64" ? ec2.AmazonLinuxCpuType.X86_64 : ec2.AmazonLinuxCpuType.ARM_64 , dataVolumes: [ { - sizeGiB: process.env.INDY_STUDENT_DATA_VOL_SIZE ? parseInt(process.env.INDY_STUDENT_DATA_VOL_SIZE): 50, // Minimum values in Gibibytes: - type: parseDataVolumeType(process.env.INDY_STUDENT_DATA_VOL_TYPE?.toLowerCase() ? process.env.INDY_STUDENT_DATA_VOL_TYPE?.toLowerCase() : "gp3"), - iops: process.env.INDY_STUDENT_DATA_VOL_IOPS ? parseInt(process.env.INDY_STUDENT_DATA_VOL_IOPS): 7000, - throughput: process.env.INDY_STUDENT_DATA_VOL_THROUGHPUT ? parseInt(process.env.INDY_STUDENT_DATA_VOL_THROUGHPUT): 250, + sizeGiB: process.env.INDY_STEWARD_DATA_VOL_SIZE ? parseInt(process.env.INDY_STEWARD_DATA_VOL_SIZE): 50, // Minimum values in Gibibytes: + type: parseDataVolumeType(process.env.INDY_STEWARD_DATA_VOL_TYPE?.toLowerCase() ? process.env.INDY_STEWARD_DATA_VOL_TYPE?.toLowerCase() : "gp3"), + iops: process.env.INDY_STEWARD_DATA_VOL_IOPS ? parseInt(process.env.INDY_STEWARD_DATA_VOL_IOPS): 7000, + throughput: process.env.INDY_STEWARD_DATA_VOL_THROUGHPUT ? parseInt(process.env.INDY_STEWARD_DATA_VOL_THROUGHPUT): 250, } ] }; export const trusteeNodeConfig: configTypes.IndyNodeConfig = { - instanceType: new ec2.InstanceType(process.env.INDY_STUDENT_INSTANCE_TYPE ? process.env.INDY_STUDENT_INSTANCE_TYPE : "m6g.2xlarge"), //InstanceType.of(InstanceClass.M6G, InstanceSize.XLARGE2), - instanceCpuType: process.env.INDY_STUDENT__CPU_TYPE?.toLowerCase() == "x86_64" ? ec2.AmazonLinuxCpuType.X86_64 : ec2.AmazonLinuxCpuType.ARM_64 , + instanceType: new ec2.InstanceType(process.env.INDY_STEWARD_INSTANCE_TYPE ? process.env.INDY_STEWARD_INSTANCE_TYPE : "m6g.2xlarge"), //InstanceType.of(InstanceClass.M6G, InstanceSize.XLARGE2), + instanceCpuType: process.env.INDY_STEWARD_CPU_TYPE?.toLowerCase() == "x86_64" ? ec2.AmazonLinuxCpuType.X86_64 : ec2.AmazonLinuxCpuType.ARM_64 , dataVolumes: [ { - sizeGiB: process.env.INDY_STUDENT_DATA_VOL_SIZE ? parseInt(process.env.INDY_STUDENT_DATA_VOL_SIZE): 50, // Minimum values in Gibibytes: - type: parseDataVolumeType(process.env.INDY_STUDENT_DATA_VOL_TYPE?.toLowerCase() ? process.env.INDY_STUDENT_DATA_VOL_TYPE?.toLowerCase() : "gp3"), - iops: process.env.INDY_STUDENT_DATA_VOL_IOPS ? parseInt(process.env.INDY_STUDENT_DATA_VOL_IOPS): 7000, - throughput: process.env.INDY_STUDENT_DATA_VOL_THROUGHPUT ? parseInt(process.env.INDY_STUDENT_DATA_VOL_THROUGHPUT): 250, + sizeGiB: process.env.INDY_STEWARD_DATA_VOL_SIZE ? parseInt(process.env.INDY_STEWARD_DATA_VOL_SIZE): 50, // Minimum values in Gibibytes: + type: parseDataVolumeType(process.env.INDY_STEWARD_DATA_VOL_TYPE?.toLowerCase() ? process.env.INDY_STEWARD_DATA_VOL_TYPE?.toLowerCase() : "gp3"), + iops: process.env.INDY_STEWARD_DATA_VOL_IOPS ? parseInt(process.env.INDY_STEWARD_DATA_VOL_IOPS): 7000, + throughput: process.env.INDY_STEWARD_DATA_VOL_THROUGHPUT ? parseInt(process.env.INDY_STEWARD_DATA_VOL_THROUGHPUT): 250, } ] }; \ No newline at end of file diff --git a/lib/indy/lib/indy-node-stack.ts b/lib/indy/lib/indy-node-stack.ts index de7ba206..1f347e67 100644 --- a/lib/indy/lib/indy-node-stack.ts +++ b/lib/indy/lib/indy-node-stack.ts @@ -84,36 +84,36 @@ export class IndyNodeStack extends cdk.Stack { clientSG, nodeSG, ansibleBucket, - instanceType: config.studentNodeConfig.instanceType, - instanceCpuType: config.studentNodeConfig.instanceCpuType, - dataVolumes: config.studentNodeConfig.dataVolumes, + instanceType: config.stewardNodeConfig.instanceType, + instanceCpuType: config.stewardNodeConfig.instanceCpuType, + dataVolumes: config.stewardNodeConfig.dataVolumes, }); const steward2 = new IndyStewardNodeInstance(this, "steward2", { vpc, clientSG, nodeSG, ansibleBucket, - instanceType: config.studentNodeConfig.instanceType, - instanceCpuType: config.studentNodeConfig.instanceCpuType, - dataVolumes: config.studentNodeConfig.dataVolumes + instanceType: config.stewardNodeConfig.instanceType, + instanceCpuType: config.stewardNodeConfig.instanceCpuType, + dataVolumes: config.stewardNodeConfig.dataVolumes }); const steward3 = new IndyStewardNodeInstance(this, "steward3", { vpc, clientSG, nodeSG, ansibleBucket, - instanceType: config.studentNodeConfig.instanceType, - instanceCpuType: config.studentNodeConfig.instanceCpuType, - dataVolumes: config.studentNodeConfig.dataVolumes + instanceType: config.stewardNodeConfig.instanceType, + instanceCpuType: config.stewardNodeConfig.instanceCpuType, + dataVolumes: config.stewardNodeConfig.dataVolumes }); const steward4 = new IndyStewardNodeInstance(this, "steward4", { vpc, clientSG, nodeSG, ansibleBucket, - instanceType: config.studentNodeConfig.instanceType, - instanceCpuType: config.studentNodeConfig.instanceCpuType, - dataVolumes: config.studentNodeConfig.dataVolumes + instanceType: config.stewardNodeConfig.instanceType, + instanceCpuType: config.stewardNodeConfig.instanceCpuType, + dataVolumes: config.stewardNodeConfig.dataVolumes }); const trustee1 = new IndyTrusteeNodeInstance(this, "trustee1", { diff --git a/lib/indy/.env-sample b/lib/indy/sample-configs/.env-sample similarity index 78% rename from lib/indy/.env-sample rename to lib/indy/sample-configs/.env-sample index ced87795..0e10703b 100644 --- a/lib/indy/.env-sample +++ b/lib/indy/sample-configs/.env-sample @@ -7,12 +7,12 @@ AWS_ACCOUNT_ID="xxxxxxxxxxx" AWS_REGION="us-east-2" # Student node configuration -INDY_STUDENT_INSTANCE_TYPE="t3.medium" -INDY_STUDENT__CPU_TYPE="ARM_64" # IMPORTANT: Make sure the CPU type matches the instance type used -INDY_STUDENT_DATA_VOL_SIZE="200" # Minimum values in Gibibytes: -INDY_STUDENT_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families -INDY_STUDENT_DATA_VOL_IOPS="6000" # Max IOPS for EBS volumes (not applicable for "instance-store") -INDY_STUDENT_DATA_VOL_THROUGHPUT="400" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") +INDY_STEWARD_INSTANCE_TYPE="t3.medium" +INDY_STEWARD_CPU_TYPE="ARM_64" # IMPORTANT: Make sure the CPU type matches the instance type used +INDY_STEWARD_DATA_VOL_SIZE="200" # Minimum values in Gibibytes: +INDY_STEWARD_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families +INDY_STEWARD_DATA_VOL_IOPS="6000" # Max IOPS for EBS volumes (not applicable for "instance-store") +INDY_STEWARD_DATA_VOL_THROUGHPUT="400" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") # Trustee node configuration INDY_TRUSTEE_INSTANCE_TYPE="t3.large" From bec772903eea1ca9fcc5a8291f64b6e78d73cb81 Mon Sep 17 00:00:00 2001 From: Nikolay Vlasov Date: Tue, 7 May 2024 15:43:01 +1000 Subject: [PATCH 14/22] Indy. Debugging initial deployment. --- lib/indy/README.md | 9 +++++++-- lib/indy/app.ts | 1 + lib/indy/lib/constructs/indy-steward-node-instance.ts | 2 +- lib/indy/lib/constructs/indy-trustee-node-instance.ts | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/indy/README.md b/lib/indy/README.md index 18d13c3d..ecfebcb0 100644 --- a/lib/indy/README.md +++ b/lib/indy/README.md @@ -102,7 +102,7 @@ When running on a Mac, set the following environment variables. ##### Ansible parameter settings -Open `inventory/group_vars/all.yml` file and define the parameters referred to by Ansible in the configuration file. Set Indy's network name +To change Indy's network name, open `ansible/inventory/group_vars/all.yml` file and change the parameter used by Ansible ``` INDY_NETWORK_NAME: sample-network @@ -113,7 +113,12 @@ INDY_NETWORK_NAME: sample-network - Use ansible's `ping` module to confirm that ansible can connect to the instance set in inventory/inventory.yml ``` - $ ansible -m ping all -i inventory/inventory.yml + cd ansible + ansible -m ping all -i inventory/inventory.yml + ``` + The response should look like this: + + ``` steward2 | SUCCESS => { "changed": false, "ping": "pong" diff --git a/lib/indy/app.ts b/lib/indy/app.ts index b220f698..fc5484b4 100644 --- a/lib/indy/app.ts +++ b/lib/indy/app.ts @@ -1,4 +1,5 @@ #!/usr/bin/env node +import 'dotenv/config' import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import * as nag from "cdk-nag"; diff --git a/lib/indy/lib/constructs/indy-steward-node-instance.ts b/lib/indy/lib/constructs/indy-steward-node-instance.ts index c1bff255..a44db720 100644 --- a/lib/indy/lib/constructs/indy-steward-node-instance.ts +++ b/lib/indy/lib/constructs/indy-steward-node-instance.ts @@ -102,7 +102,7 @@ export class IndyStewardNodeInstance extends Construct { }, { id: "AwsSolutions-EC29", - reason: "Its Ok to terminate this instance as long as we have the data in the snapshot", + reason: "Its Ok to terminate this instance as the same copies of the data are stored on each node", }, ], diff --git a/lib/indy/lib/constructs/indy-trustee-node-instance.ts b/lib/indy/lib/constructs/indy-trustee-node-instance.ts index beb190c4..fe46381f 100644 --- a/lib/indy/lib/constructs/indy-trustee-node-instance.ts +++ b/lib/indy/lib/constructs/indy-trustee-node-instance.ts @@ -75,7 +75,7 @@ export class IndyTrusteeNodeInstance extends Construct { }, { id: "AwsSolutions-EC29", - reason: "Its Ok to terminate this instance as long as we have the data in the snapshot", + reason: "Its Ok to terminate this instance as the same copies of the data are stored on each node", }, ], From 8e755b74ac7582b2cb39c76a8086e54065e7a116 Mon Sep 17 00:00:00 2001 From: Nikolay Vlasov Date: Wed, 8 May 2024 13:21:04 +1000 Subject: [PATCH 15/22] Indy. Fixed commands in README --- lib/indy/README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/indy/README.md b/lib/indy/README.md index ecfebcb0..5c91df16 100644 --- a/lib/indy/README.md +++ b/lib/indy/README.md @@ -58,7 +58,11 @@ npx cdk bootstrap aws:/// Date: Fri, 10 May 2024 08:46:58 +0000 Subject: [PATCH 16/22] fix ansible build failed --- lib/indy/README.md | 55 +++++++++++++++++- lib/indy/README_ja.md | 50 ++++++++++++++++ lib/indy/ansible/ansible.cfg | 7 ++- .../playbook/997_trustee_instance_start.yml | 5 ++ .../roles/install_dependencies/tasks/main.yml | 19 +++++- lib/indy/doc/assets/Architecture.drawio | 12 ++-- lib/indy/doc/assets/Architecture.png | Bin 52745 -> 55349 bytes lib/indy/lib/indy-node-stack.ts | 12 ++-- 8 files changed, 142 insertions(+), 18 deletions(-) diff --git a/lib/indy/README.md b/lib/indy/README.md index d0e4409a..26341172 100644 --- a/lib/indy/README.md +++ b/lib/indy/README.md @@ -38,9 +38,9 @@ npm install Create your own copy of `.env` file and edit it: ```bash # Make sure you are in aws-blockchain-node-runners/lib/ethereum - cd lib/ethereum + cd lib/indy pwd - cp ./sample-configs/.env-geth-lighthouse .env + cp .env-sample .env nano .env ``` **NOTE:** You can find more examples inside the `sample-configs` directory. @@ -113,6 +113,7 @@ INDY_NETWORK_NAME: sample-network - Use ansible's `ping` module to confirm that ansible can connect to the instance set in inventory/inventory.yml ``` + $ cd ansible $ ansible -m ping all -i inventory/inventory.yml steward2 | SUCCESS => { "changed": false, @@ -144,11 +145,61 @@ INDY_NETWORK_NAME: sample-network } ``` +- Use ansible's `command` module to check the cloud-init status was done. + + ``` + $ ansible -m command all -i inventory/inventory.yml -a "cloud-init status --wait" + + steward4 | CHANGED | rc=0 >> + + status: done + steward3 | CHANGED | rc=0 >> + + status: done + steward2 | CHANGED | rc=0 >> + + status: done + steward1 | CHANGED | rc=0 >> + + status: done + trustee1 | CHANGED | rc=0 >> + + status: done + trustee2 | CHANGED | rc=0 >> + + status: done + trustee3 | CHANGED | rc=0 >> + + status: done + ``` + - Execute Hyperledger Indy environment construction for target EC2 instances defined in `inventory/inventory.yml` in ansible ``` $ ansible-playbook playbook/site.yml ``` +## Clearing up and undeploying everything + +1. Remove Indy's seed, nodeInfo, did on the Secrets Manager + +```bash +$ ansible-playbook playbook/999_cleanup.yml +``` + +2. Undeploy Indy Nodes + +```bash + # Setting the AWS account id and region in case local .env file is lost + export AWS_ACCOUNT_ID= + export AWS_REGION= + + pwd + # Make sure you are in aws-blockchain-node-runners/lib/indy + + # Undeploy Indy Node + cdk destroy --all +``` + #### reference information diff --git a/lib/indy/README_ja.md b/lib/indy/README_ja.md index c7bb6e96..cbd70e5b 100644 --- a/lib/indy/README_ja.md +++ b/lib/indy/README_ja.md @@ -105,6 +105,7 @@ INDY_NETWORK_NAME: sample-network - inventory/inventory.ymlで設定したインスタンスにansibleが接続できることをansibleの `ping` モジュールを使用して確認する ``` + $ cd ansible $ ansible -m ping all -i inventory/inventory.yml steward2 | SUCCESS => { "changed": false, @@ -136,11 +137,60 @@ INDY_NETWORK_NAME: sample-network } ``` +- cloud-initが全て `Done`になっていることを確認する + + ``` + $ ansible -m command all -i inventory/inventory.yml -a "cloud-init status --wait" + + steward4 | CHANGED | rc=0 >> + + status: done + steward3 | CHANGED | rc=0 >> + + status: done + steward2 | CHANGED | rc=0 >> + + status: done + steward1 | CHANGED | rc=0 >> + + status: done + trustee1 | CHANGED | rc=0 >> + + status: done + trustee2 | CHANGED | rc=0 >> + + status: done + trustee3 | CHANGED | rc=0 >> + + status: done + ``` + - ansibleで `inventory/inventory.yml` で定義した対象のEC2インスタンスに対してHyperledger Indyの環境構築を実行する ``` $ ansible-playbook playbook/site.yml ``` +## すべてを削除する方法 + +1. Secrets ManagerからIndyのseed, nodeInfo, didを削除する + +```bash +$ ansible-playbook playbook/999_cleanup.yml +``` + +2. Indy Nodeを削除する + +```bash + # Setting the AWS account id and region in case local .env file is lost + export AWS_ACCOUNT_ID= + export AWS_REGION= + + pwd + # Make sure you are in aws-blockchain-node-runners/lib/indy + + # Undeploy Indy Node + cdk destroy --all +``` ## 参考情報 - [Indy Network の構築](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md) diff --git a/lib/indy/ansible/ansible.cfg b/lib/indy/ansible/ansible.cfg index db0c46b9..21889ea4 100644 --- a/lib/indy/ansible/ansible.cfg +++ b/lib/indy/ansible/ansible.cfg @@ -4,6 +4,7 @@ [defaults] inventory = ./inventory/inventory.yml host_key_checking = False -deprecation_warnings=False -roles_path: ./roles -interpreter_python: /usr/bin/python3 +deprecation_warnings = False +roles_path = ./roles +interpreter_python = /usr/bin/python3 +pipelining = True \ No newline at end of file diff --git a/lib/indy/ansible/playbook/997_trustee_instance_start.yml b/lib/indy/ansible/playbook/997_trustee_instance_start.yml index d425620b..84730cc3 100644 --- a/lib/indy/ansible/playbook/997_trustee_instance_start.yml +++ b/lib/indy/ansible/playbook/997_trustee_instance_start.yml @@ -10,3 +10,8 @@ state: running wait: True loop: "{{ groups['trustee'] }}" + + - name: Wait for start instances + ansible.builtin.wait_for_connection: + delay: 60 + timeout: 300 \ No newline at end of file diff --git a/lib/indy/ansible/roles/install_dependencies/tasks/main.yml b/lib/indy/ansible/roles/install_dependencies/tasks/main.yml index 3ea55dba..17df5954 100644 --- a/lib/indy/ansible/roles/install_dependencies/tasks/main.yml +++ b/lib/indy/ansible/roles/install_dependencies/tasks/main.yml @@ -19,9 +19,22 @@ - "deb https://repo.sovrin.org/deb bionic master" - "deb https://sovrin.jfrog.io/artifactory/deb focal rc" -- name: apt install +- name: Wait for /var/lib/dpkg/lock-frontend to be released + shell: while lsof /var/lib/dpkg/lock-frontend ; do sleep 10; done; + changed_when: false + +- name: apt update ansible.builtin.apt: update_cache: true + register: result_update + until: result_update is succeeded + retries: 10 + delay: 60 + +- name: apt install + ansible.builtin.apt: + update_cache: false + lock_timeout: 300 pkg: - python3-pip - rocksdb=5.8.8 @@ -42,6 +55,10 @@ - sovrin=1.2.0~rc1 - libssl1.0.0 - ursa=0.3.2-1 + register: result_install + until: result_install is succeeded + retries: 10 + delay: 60 - name: pip install boto3 ansible.builtin.pip: diff --git a/lib/indy/doc/assets/Architecture.drawio b/lib/indy/doc/assets/Architecture.drawio index 47546cfa..8b0a9e12 100644 --- a/lib/indy/doc/assets/Architecture.drawio +++ b/lib/indy/doc/assets/Architecture.drawio @@ -1,16 +1,16 @@ - + - + - + - + @@ -78,10 +78,10 @@ - + - + \ No newline at end of file diff --git a/lib/indy/doc/assets/Architecture.png b/lib/indy/doc/assets/Architecture.png index af1432cfc9811f4af61fe7920dc13c8fc38d2d4e..8cb2274c90030bc620071ca04984df65cbd7a7ff 100644 GIT binary patch literal 55349 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 f2N5xz?~Ca&nLM|fdXsYv{Cg^`__$E=x!?Z+B;&

8mRVnTcJp^|Mxz*b#Z;F5;uTW^ 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 diff --git a/lib/indy/lib/indy-node-stack.ts b/lib/indy/lib/indy-node-stack.ts index de7ba206..a2926182 100644 --- a/lib/indy/lib/indy-node-stack.ts +++ b/lib/indy/lib/indy-node-stack.ts @@ -15,10 +15,10 @@ export class IndyNodeStack extends cdk.Stack { const serverAccessLogBucket = new s3.Bucket(this, "serverAccessLogBucket", { encryption: s3.BucketEncryption.S3_MANAGED, blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, - removalPolicy: cdk.RemovalPolicy.RETAIN, - versioned: true, + removalPolicy: cdk.RemovalPolicy.DESTROY, + versioned: false, enforceSSL: true, - autoDeleteObjects: false, + autoDeleteObjects: true, }) const vpc = new ec2.Vpc(this, "IndyVpc", { @@ -29,10 +29,10 @@ export class IndyNodeStack extends cdk.Stack { new s3.Bucket(this, "VpcFlowLogBucket", { encryption: s3.BucketEncryption.S3_MANAGED, blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, - removalPolicy: cdk.RemovalPolicy.RETAIN, - versioned: true, + removalPolicy: cdk.RemovalPolicy.DESTROY, + versioned: false, enforceSSL: true, - autoDeleteObjects: false, + autoDeleteObjects: true, serverAccessLogsBucket: serverAccessLogBucket, serverAccessLogsPrefix: "vpcFlowLogs", }), From cdbd9f9dc9841e2721beb4840ed63cf5b0ca88cd Mon Sep 17 00:00:00 2001 From: fsatsuki Date: Fri, 10 May 2024 09:06:18 +0000 Subject: [PATCH 17/22] run pre-commit --- lib/indy/README.md | 4 +- lib/indy/README_ja.md | 2 +- lib/indy/ansible/ansible.cfg | 2 +- .../playbook/997_trustee_instance_start.yml | 2 +- .../roles/install_dependencies/tasks/main.yml | 2 +- lib/indy/configure-ansible-inventory.sh | 2 +- lib/indy/lib/config/indyConfig.interface.ts | 2 +- lib/indy/lib/config/indyConfig.ts | 2 +- .../constructs/indy-steward-node-instance.ts | 4 +- .../constructs/indy-trustee-node-instance.ts | 2 +- lib/indy/lib/indy-node-stack.ts | 42 +++++++++---------- lib/indy/sample-configs/.env-sample | 4 +- lib/wax/README.md | 2 +- 13 files changed, 36 insertions(+), 36 deletions(-) diff --git a/lib/indy/README.md b/lib/indy/README.md index a13c2379..02538d9e 100644 --- a/lib/indy/README.md +++ b/lib/indy/README.md @@ -101,7 +101,7 @@ When running on a Mac, set the following environment variables. ``` cd .. - ./configure-ansible-inventory.sh + ./configure-ansible-inventory.sh ``` @@ -122,7 +122,7 @@ INDY_NETWORK_NAME: sample-network ``` The response should look like this: - ``` + ``` steward2 | SUCCESS => { "changed": false, "ping": "pong" diff --git a/lib/indy/README_ja.md b/lib/indy/README_ja.md index cbd70e5b..d8fe5b6f 100644 --- a/lib/indy/README_ja.md +++ b/lib/indy/README_ja.md @@ -91,7 +91,7 @@ Macで実行する場合は次の環境変数を設定する。 ``` cd .. - ./configure-ansible-inventory.sh + ./configure-ansible-inventory.sh ``` ## Ansibleの設定 diff --git a/lib/indy/ansible/ansible.cfg b/lib/indy/ansible/ansible.cfg index 21889ea4..e5f6df98 100644 --- a/lib/indy/ansible/ansible.cfg +++ b/lib/indy/ansible/ansible.cfg @@ -7,4 +7,4 @@ host_key_checking = False deprecation_warnings = False roles_path = ./roles interpreter_python = /usr/bin/python3 -pipelining = True \ No newline at end of file +pipelining = True diff --git a/lib/indy/ansible/playbook/997_trustee_instance_start.yml b/lib/indy/ansible/playbook/997_trustee_instance_start.yml index 84730cc3..ce5cf148 100644 --- a/lib/indy/ansible/playbook/997_trustee_instance_start.yml +++ b/lib/indy/ansible/playbook/997_trustee_instance_start.yml @@ -14,4 +14,4 @@ - name: Wait for start instances ansible.builtin.wait_for_connection: delay: 60 - timeout: 300 \ No newline at end of file + timeout: 300 diff --git a/lib/indy/ansible/roles/install_dependencies/tasks/main.yml b/lib/indy/ansible/roles/install_dependencies/tasks/main.yml index 17df5954..7cf8ddd7 100644 --- a/lib/indy/ansible/roles/install_dependencies/tasks/main.yml +++ b/lib/indy/ansible/roles/install_dependencies/tasks/main.yml @@ -19,7 +19,7 @@ - "deb https://repo.sovrin.org/deb bionic master" - "deb https://sovrin.jfrog.io/artifactory/deb focal rc" -- name: Wait for /var/lib/dpkg/lock-frontend to be released +- name: Wait for /var/lib/dpkg/lock-frontend to be released shell: while lsof /var/lib/dpkg/lock-frontend ; do sleep 10; done; changed_when: false diff --git a/lib/indy/configure-ansible-inventory.sh b/lib/indy/configure-ansible-inventory.sh index 5aa4a044..421d4c2e 100755 --- a/lib/indy/configure-ansible-inventory.sh +++ b/lib/indy/configure-ansible-inventory.sh @@ -24,4 +24,4 @@ sed -i "s/_trustee2InstanceId_/$TRUSTEE2/" ./ansible/inventory/inventory.yml sed -i "s/_trustee3InstanceId_/$TRUSTEE3/" ./ansible/inventory/inventory.yml sed -i "s/_ansible-file-transfer-bucket_/$ANSIBLE_BUCKET_NAME/" ./ansible/inventory/inventory.yml -sed -i "s/_aws_region_/$AWS_DEPLOYMENT_REGION/" ./ansible/inventory/inventory.yml \ No newline at end of file +sed -i "s/_aws_region_/$AWS_DEPLOYMENT_REGION/" ./ansible/inventory/inventory.yml diff --git a/lib/indy/lib/config/indyConfig.interface.ts b/lib/indy/lib/config/indyConfig.interface.ts index 01aa46f7..2e70be06 100644 --- a/lib/indy/lib/config/indyConfig.interface.ts +++ b/lib/indy/lib/config/indyConfig.interface.ts @@ -9,4 +9,4 @@ export interface IndyBaseConfig extends configTypes.BaseConfig { } export interface IndyNodeConfig extends configTypes.SingleNodeConfig { -} \ No newline at end of file +} diff --git a/lib/indy/lib/config/indyConfig.ts b/lib/indy/lib/config/indyConfig.ts index 48aca12b..e9bf57a1 100644 --- a/lib/indy/lib/config/indyConfig.ts +++ b/lib/indy/lib/config/indyConfig.ts @@ -48,4 +48,4 @@ export const trusteeNodeConfig: configTypes.IndyNodeConfig = { throughput: process.env.INDY_STEWARD_DATA_VOL_THROUGHPUT ? parseInt(process.env.INDY_STEWARD_DATA_VOL_THROUGHPUT): 250, } ] -}; \ No newline at end of file +}; diff --git a/lib/indy/lib/constructs/indy-steward-node-instance.ts b/lib/indy/lib/constructs/indy-steward-node-instance.ts index a44db720..f6acbe04 100644 --- a/lib/indy/lib/constructs/indy-steward-node-instance.ts +++ b/lib/indy/lib/constructs/indy-steward-node-instance.ts @@ -84,7 +84,7 @@ export class IndyStewardNodeInstance extends Construct { }); this.instance = instance; - + nag.NagSuppressions.addResourceSuppressions( this, [ @@ -103,7 +103,7 @@ export class IndyStewardNodeInstance extends Construct { { id: "AwsSolutions-EC29", reason: "Its Ok to terminate this instance as the same copies of the data are stored on each node", - + }, ], true diff --git a/lib/indy/lib/constructs/indy-trustee-node-instance.ts b/lib/indy/lib/constructs/indy-trustee-node-instance.ts index fe46381f..a5c52006 100644 --- a/lib/indy/lib/constructs/indy-trustee-node-instance.ts +++ b/lib/indy/lib/constructs/indy-trustee-node-instance.ts @@ -76,7 +76,7 @@ export class IndyTrusteeNodeInstance extends Construct { { id: "AwsSolutions-EC29", reason: "Its Ok to terminate this instance as the same copies of the data are stored on each node", - + }, ], true diff --git a/lib/indy/lib/indy-node-stack.ts b/lib/indy/lib/indy-node-stack.ts index 90732781..6711c251 100644 --- a/lib/indy/lib/indy-node-stack.ts +++ b/lib/indy/lib/indy-node-stack.ts @@ -79,58 +79,58 @@ export class IndyNodeStack extends cdk.Stack { serverAccessLogsPrefix: "AnsibleFileTransferBucket", }); - const steward1 = new IndyStewardNodeInstance(this, "steward1", { - vpc, - clientSG, - nodeSG, + const steward1 = new IndyStewardNodeInstance(this, "steward1", { + vpc, + clientSG, + nodeSG, ansibleBucket, instanceType: config.stewardNodeConfig.instanceType, instanceCpuType: config.stewardNodeConfig.instanceCpuType, dataVolumes: config.stewardNodeConfig.dataVolumes, }); - const steward2 = new IndyStewardNodeInstance(this, "steward2", { - vpc, - clientSG, - nodeSG, + const steward2 = new IndyStewardNodeInstance(this, "steward2", { + vpc, + clientSG, + nodeSG, ansibleBucket, instanceType: config.stewardNodeConfig.instanceType, instanceCpuType: config.stewardNodeConfig.instanceCpuType, dataVolumes: config.stewardNodeConfig.dataVolumes }); - const steward3 = new IndyStewardNodeInstance(this, "steward3", { - vpc, - clientSG, - nodeSG, + const steward3 = new IndyStewardNodeInstance(this, "steward3", { + vpc, + clientSG, + nodeSG, ansibleBucket, instanceType: config.stewardNodeConfig.instanceType, instanceCpuType: config.stewardNodeConfig.instanceCpuType, dataVolumes: config.stewardNodeConfig.dataVolumes }); - const steward4 = new IndyStewardNodeInstance(this, "steward4", { - vpc, - clientSG, - nodeSG, + const steward4 = new IndyStewardNodeInstance(this, "steward4", { + vpc, + clientSG, + nodeSG, ansibleBucket, instanceType: config.stewardNodeConfig.instanceType, instanceCpuType: config.stewardNodeConfig.instanceCpuType, dataVolumes: config.stewardNodeConfig.dataVolumes }); - const trustee1 = new IndyTrusteeNodeInstance(this, "trustee1", { - vpc, + const trustee1 = new IndyTrusteeNodeInstance(this, "trustee1", { + vpc, nodeSG, instanceType: config.trusteeNodeConfig.instanceType, instanceCpuType: config.trusteeNodeConfig.instanceCpuType, dataVolumes: config.trusteeNodeConfig.dataVolumes }); - const trustee2 = new IndyTrusteeNodeInstance(this, "trustee2", { - vpc, + const trustee2 = new IndyTrusteeNodeInstance(this, "trustee2", { + vpc, nodeSG, instanceType: config.trusteeNodeConfig.instanceType, instanceCpuType: config.trusteeNodeConfig.instanceCpuType, dataVolumes: config.trusteeNodeConfig.dataVolumes }); - const trustee3 = new IndyTrusteeNodeInstance(this, "trustee3", { + const trustee3 = new IndyTrusteeNodeInstance(this, "trustee3", { vpc, nodeSG, instanceType: config.trusteeNodeConfig.instanceType, diff --git a/lib/indy/sample-configs/.env-sample b/lib/indy/sample-configs/.env-sample index 0e10703b..5fff78ad 100644 --- a/lib/indy/sample-configs/.env-sample +++ b/lib/indy/sample-configs/.env-sample @@ -9,7 +9,7 @@ AWS_REGION="us-east-2" # Student node configuration INDY_STEWARD_INSTANCE_TYPE="t3.medium" INDY_STEWARD_CPU_TYPE="ARM_64" # IMPORTANT: Make sure the CPU type matches the instance type used -INDY_STEWARD_DATA_VOL_SIZE="200" # Minimum values in Gibibytes: +INDY_STEWARD_DATA_VOL_SIZE="200" # Minimum values in Gibibytes: INDY_STEWARD_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families INDY_STEWARD_DATA_VOL_IOPS="6000" # Max IOPS for EBS volumes (not applicable for "instance-store") INDY_STEWARD_DATA_VOL_THROUGHPUT="400" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") @@ -17,7 +17,7 @@ INDY_STEWARD_DATA_VOL_THROUGHPUT="400" # Max throughput for EBS gp3 # Trustee node configuration INDY_TRUSTEE_INSTANCE_TYPE="t3.large" INDY_TRUSTEE__CPU_TYPE="ARM_64" # IMPORTANT: Make sure the CPU type matches the instance type used -INDY_TRUSTEE_DATA_VOL_SIZE="30" # Minimum values in Gibibytes: +INDY_TRUSTEE_DATA_VOL_SIZE="30" # Minimum values in Gibibytes: INDY_TRUSTEE_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families INDY_TRUSTEE_DATA_VOL_IOPS="6000" # Max IOPS for EBS volumes (not applicable for "instance-store") INDY_TRUSTEE_DATA_VOL_THROUGHPUT="400" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") diff --git a/lib/wax/README.md b/lib/wax/README.md index d53ebce5..c9d81197 100644 --- a/lib/wax/README.md +++ b/lib/wax/README.md @@ -19,4 +19,4 @@ The [AWS CDK stack](https://github.com/worldwide-asset-exchange/wax-aws-cdk) has - [Victoria Metrics](https://victoriametrics.com/), a time series database for storing the metrics from Telegraf and WAX nodes - A [Grafana dashboard](https://grafana.com/) to display key system and blockchain metrics from CPU and disc usage to synced blocks and sync difference 3. By default, all API ports, including the one for the Grafana web user interface, are available only to IP addresses from within the same VPC. -4. The logs of the WAX node are published to Amazon CloudWatch. \ No newline at end of file +4. The logs of the WAX node are published to Amazon CloudWatch. From 7f87bdfd4a5ab36233f2fdd09218c6d127563560 Mon Sep 17 00:00:00 2001 From: Nikolay Vlasov Date: Tue, 14 May 2024 17:10:14 +1000 Subject: [PATCH 18/22] Indy. Ansible configuration is fixed and working. --- lib/indy/README.md | 47 +++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/lib/indy/README.md b/lib/indy/README.md index 02538d9e..3ce7cf23 100644 --- a/lib/indy/README.md +++ b/lib/indy/README.md @@ -85,24 +85,24 @@ When running on a Mac, set the following environment variables. ##### Preparing for Ansible - Create a Python virtual environment and install ansible - ``` - cd ansible - python3 -m venv venv - source ./venv/bin/activate + ```bash + cd ansible + python3 -m venv venv + source ./venv/bin/activate ``` - ``` - pip install -r requirements.txt + ```bash + pip install -r requirements.txt ``` ##### Describe instance information to be built in inventory.yml - Create an inventory file containing information on the EC2 instance that will build the environment. Enter the instance ID described in the CDK output results in the settings column for each node. The value of `indyNetworkStack.ansibleFileTransferBucketName` described in CDK output results is inputted to `ansible_aws_ssm_bucket_name`. When Ansible transfers files to the target host, the Amazon Simple Storage Service (Amazon S3) bucket specified here is used. - ``` +```bash cd .. ./configure-ansible-inventory.sh - ``` +``` ##### Ansible parameter settings @@ -116,13 +116,13 @@ INDY_NETWORK_NAME: sample-network - Use ansible's `ping` module to confirm that ansible can connect to the instance set in inventory/inventory.yml - ``` +```bash cd ansible ansible -m ping all -i inventory/inventory.yml - ``` +``` The response should look like this: - ``` +```bash steward2 | SUCCESS => { "changed": false, "ping": "pong" @@ -151,13 +151,16 @@ INDY_NETWORK_NAME: sample-network "changed": false, "ping": "pong" } - ``` +``` -- Use ansible's `command` module to check the cloud-init status was done. +- Before proceeding with configuring Indy, we need to make sure the initialization scripts are finished. To check their stats, use ansible's `command` module. If the check shows cloud-init is not finished wait and try again in 5-10 minutes until status was done. - ``` - $ ansible -m command all -i inventory/inventory.yml -a "cloud-init status --wait" +```bash + ansible -m command all -i inventory/inventory.yml -a "cloud-init status --wait" +``` + The response should look like this: +```bash steward4 | CHANGED | rc=0 >> status: done @@ -179,19 +182,21 @@ INDY_NETWORK_NAME: sample-network trustee3 | CHANGED | rc=0 >> status: done - ``` +``` - Execute Hyperledger Indy environment construction for target EC2 instances defined in `inventory/inventory.yml` in ansible - ``` - $ ansible-playbook playbook/site.yml - ``` +```bash + ansible-playbook playbook/site.yml +``` ## Clearing up and undeploying everything 1. Remove Indy's seed, nodeInfo, did on the Secrets Manager ```bash -$ ansible-playbook playbook/999_cleanup.yml + # make sure you are in 'ansible' directory + cd ansible + ansible-playbook playbook/999_cleanup.yml ``` 2. Undeploy Indy Nodes @@ -205,7 +210,7 @@ $ ansible-playbook playbook/999_cleanup.yml # Make sure you are in aws-blockchain-node-runners/lib/indy # Undeploy Indy Node - cdk destroy --all + npx cdk destroy --all ``` From 719185cfff6cc745f0ff435f8439e2927758b1c8 Mon Sep 17 00:00:00 2001 From: Katsuya Matsuoka Date: Thu, 16 May 2024 16:14:40 +0900 Subject: [PATCH 19/22] fix architecture diagram --- lib/indy/doc/assets/Architecture.drawio | 127 +++++++++++++++--------- lib/indy/doc/assets/Architecture.png | Bin 55349 -> 81682 bytes 2 files changed, 81 insertions(+), 46 deletions(-) diff --git a/lib/indy/doc/assets/Architecture.drawio b/lib/indy/doc/assets/Architecture.drawio index 8b0a9e12..f8835771 100644 --- a/lib/indy/doc/assets/Architecture.drawio +++ b/lib/indy/doc/assets/Architecture.drawio @@ -1,85 +1,120 @@ - + - - + + - + - + - + - - + + - + - + + + + - - + + - + + + + - - - - + - - + + - - + + - - + + - - + + + + + - - + + - - + + - - + + - - + + + + + + + + + + + - - - - + + + + + + + + + + - + - - - - + + + + + + + + + - - + + + + + + + + + + + - - + + diff --git a/lib/indy/doc/assets/Architecture.png b/lib/indy/doc/assets/Architecture.png index 8cb2274c90030bc620071ca04984df65cbd7a7ff..52fe01a62310b60f41112ddc27131241ca12f2d3 100644 GIT binary patch literal 81682 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 literal 55349 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 f2N5xz?~Ca&nLM|fdXsYv{Cg^`__$E=x!?Z+B;&

Date: Thu, 22 Aug 2024 17:17:35 +0900 Subject: [PATCH 20/22] add cdk unit test --- lib/indy/test/indy-node-stack.test.ts | 137 ++++++++++++++++++++++++++ lib/indy/tsconfig.json | 31 ++++++ 2 files changed, 168 insertions(+) create mode 100644 lib/indy/test/indy-node-stack.test.ts create mode 100644 lib/indy/tsconfig.json diff --git a/lib/indy/test/indy-node-stack.test.ts b/lib/indy/test/indy-node-stack.test.ts new file mode 100644 index 00000000..39424245 --- /dev/null +++ b/lib/indy/test/indy-node-stack.test.ts @@ -0,0 +1,137 @@ +import { Match, Template } from "aws-cdk-lib/assertions"; +import * as cdk from "aws-cdk-lib"; +import { IndyNodeStack } from "../lib/indy-node-stack"; + +describe("IndyNodeStack", () => { + test("synthesizes the way we expect", () => { + const app = new cdk.App(); + + // Create the indyNodeStack. + const indyNodeStack = new IndyNodeStack(app, "indy-node", { + stackName: `indy-sample-network-stack`, + }); + + // Prepare the stack for assertions. + const template = Template.fromStack(indyNodeStack); + + // Has serverAccessLogBucket. + template.hasResourceProperties("AWS::S3::Bucket", { + AccessControl: "LogDeliveryWrite", + BucketEncryption: { + ServerSideEncryptionConfiguration: [ + { + ServerSideEncryptionByDefault: { + SSEAlgorithm: "AES256" + } + } + ] + }, + PublicAccessBlockConfiguration: { + BlockPublicAcls: true, + BlockPublicPolicy: true, + IgnorePublicAcls: true, + RestrictPublicBuckets: true + } + }); + + // Has ClientSecurityGroup + template.hasResourceProperties("AWS::EC2::SecurityGroup", { + GroupDescription: Match.stringLikeRegexp('indy-node/ClientSG*'), + VpcId: Match.anyValue(), + }); + + // Has ClientSecurityGroup IngressRule + template.hasResourceProperties('AWS::EC2::SecurityGroupIngress', { + IpProtocol: 'tcp', + FromPort: 9702, + ToPort: 9702, + SourceSecurityGroupId: Match.objectLike({ + 'Fn::GetAtt': Match.arrayWith([ + Match.stringLikeRegexp('ClientSG*'), + 'GroupId' + ]) + }) + }); + + // Has NodeSecurityGroup + template.hasResourceProperties("AWS::EC2::SecurityGroup", { + GroupDescription: Match.stringLikeRegexp('indy-node/NodeSG*'), + VpcId: Match.anyValue(), + }); + + // Has NodeSecurityGroup IngressRule + template.hasResourceProperties('AWS::EC2::SecurityGroupIngress', { + IpProtocol: 'tcp', + FromPort: 9701, + ToPort: 9701, + SourceSecurityGroupId: Match.objectLike({ + 'Fn::GetAtt': Match.arrayWith([ + Match.stringLikeRegexp('NodeSG*'), + 'GroupId' + ]) + }) + }); + + // Has 4 IndyStewardNodeInstance + template.resourcePropertiesCountIs('AWS::EC2::Instance', { + "BlockDeviceMappings": [ { + DeviceName: "/dev/sda1", + Ebs: { + VolumeSize: 200, + VolumeType: "gp3", + Encrypted: true, + DeleteOnTermination: true, + }, + }], + "IamInstanceProfile": Match.objectLike({ + "Ref": Match.stringLikeRegexp('steward*') + }), + "InstanceType": "t3.large", + "NetworkInterfaces": [ + { + "Description": "Client NIC", + "DeviceIndex": "0", + "GroupSet": Match.arrayWith([ + { + 'Fn::GetAtt': Match.arrayWith([ + Match.stringLikeRegexp('ClientSG*'), + 'GroupId' + ]) + } + ]), + "SubnetId": Match.anyValue() + }, + { + "Description": "Node NIC", + "DeviceIndex": "1", + "GroupSet": Match.arrayWith([ + { + 'Fn::GetAtt': Match.arrayWith([ + Match.stringLikeRegexp('NodeSG*'), + 'GroupId' + ]) + } + ]), + "SubnetId": Match.anyValue() + } + ] + }, 4); + + // Has 3 IndyTrusteeNodeInstance + template.resourcePropertiesCountIs('AWS::EC2::Instance', { + "BlockDeviceMappings": [ { + DeviceName: "/dev/sda1", + Ebs: { + VolumeSize: 30, + VolumeType: "gp3", + Encrypted: true, + DeleteOnTermination: true, + }, + }], + "IamInstanceProfile": Match.objectLike({ + "Ref": Match.stringLikeRegexp('trustee*') + }), + "InstanceType": "t3.medium", + }, 3); + }); +}); diff --git a/lib/indy/tsconfig.json b/lib/indy/tsconfig.json new file mode 100644 index 00000000..8e1979f3 --- /dev/null +++ b/lib/indy/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020", + "dom" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "../../node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} From 7c8eb39f93cbf6c91e45aabeb39005e75865eda8 Mon Sep 17 00:00:00 2001 From: Katsuya Matsuoka Date: Thu, 22 Aug 2024 17:56:33 +0900 Subject: [PATCH 21/22] Revert "Merge branch 'aws-samples:main' into cdk-tests" This reverts commit 439e031d63e14e27d9831de3e82b8d62d40d2c5c, reversing changes made to 3af46e372b24da7c04b122a65b3f79e671b7b504. --- docs/pre-merge-tools.md | 2 +- docs/setup-cloud9.md | 24 +- lib/bsc/package.json | 7 + lib/constructs/amb-ethereum-single-node.ts | 6 +- lib/fantom/.gitignore | 8 - lib/fantom/README.md | 331 -- lib/fantom/app.ts | 55 - lib/fantom/cdk.json | 57 - ...Architecture-HA-FANTOM-Node-Runners.drawio | 195 - ...itecture-HA-FANTOM-Node-Runners.drawio.png | Bin 148677 -> 0 bytes ...ture-Single-FANTOM-Node-Runners.drawio.png | Bin 113435 -> 0 bytes lib/fantom/ha-node-deploy.json | 5 - lib/fantom/jest.config.js | 8 - .../lib/assets/cfn-hup/cfn-auto-reloader.conf | 4 - lib/fantom/lib/assets/cfn-hup/cfn-hup.conf | 5 - lib/fantom/lib/assets/cfn-hup/cfn-hup.service | 8 - lib/fantom/lib/assets/cw-agent.json | 76 - lib/fantom/lib/assets/download-snapshot.sh | 22 - .../fantom-checker/syncchecker-fantom.sh | 43 - lib/fantom/lib/assets/fantom/read-template.sh | 22 - lib/fantom/lib/assets/node-cw-dashboard.ts | 235 - .../assets/setup-instance-store-volumes.sh | 37 - lib/fantom/lib/assets/user-data/node.sh | 240 - lib/fantom/lib/common-stack.ts | 66 - .../lib/config/fantomConfig.interface.ts | 27 - lib/fantom/lib/config/fantomConfig.ts | 43 - .../constructs/fantom-node-security-group.ts | 48 - lib/fantom/lib/ha-nodes-stack.ts | 137 - lib/fantom/lib/single-node-stack.ts | 141 - lib/fantom/package.json | 17 - lib/fantom/sample-configs/.env-sample-read | 29 - lib/fantom/test/.env-test | 29 - lib/fantom/test/common-stack.test.ts | 63 - lib/fantom/test/ha-nodes-stack.test.ts | 255 - lib/fantom/test/single-node-stack.test.ts | 125 - lib/fantom/tsconfig.json | 31 - lib/scroll/package-lock.json | 1137 +++++ lib/scroll/package.json | 6 + .../test/scroll-ethereum-l1-node.test.ts | 2 +- lib/scroll/tsconfig.json | 2 +- lib/solana/package.json | 6 + lib/solana/test/single-node-stack.test.ts | 12 +- lib/stacks/package-lock.json | 641 +++ lib/stacks/package.json | 4 + lib/starknet/.gitignore | 11 - lib/starknet/.npmignore | 11 - lib/starknet/README.md | 236 - lib/starknet/app.ts | 47 - lib/starknet/cdk.json | 57 - .../doc/assets/Architecture-SingleNode.drawio | 78 - .../doc/assets/Architecture-SingleNode.png | Bin 38696 -> 0 bytes lib/starknet/jest.config.js | 8 - .../lib/amb-ethereum-single-node-stack.ts | 60 - lib/starknet/lib/assets/cw-agent.json | 76 - .../lib/assets/restore-from-snapshot.sh | 58 - .../assets/setup-instance-store-volumes.sh | 43 - .../lib/assets/starknet/rpc-template.sh | 16 - .../sync-checker/syncchecker-starknet.sh | 30 - lib/starknet/lib/assets/user-data/node.sh | 218 - lib/starknet/lib/common-stack.ts | 62 - .../lib/config/starknetConfig.interface.ts | 25 - lib/starknet/lib/config/starknetConfig.ts | 41 - .../lib/constructs/node-cw-dashboard.ts | 235 - .../starknet-node-security-group.ts | 30 - lib/starknet/lib/single-node-stack.ts | 148 - lib/starknet/package.json | 17 - lib/starknet/sample-configs/.env-sample-full | 24 - lib/starknet/test/.env-test | 19 - .../test/starknet-single-node.test.ts | 110 - lib/starknet/tsconfig.json | 31 - lib/sui/README.md | 233 - lib/sui/app.ts | 36 - lib/sui/cdk.json | 57 - .../doc/assets/Architecture-SingleNode.png | Bin 45249 -> 0 bytes lib/sui/jest.config.js | 8 - lib/sui/lib/assets/cw-agent.json | 76 - lib/sui/lib/assets/user-data/node.sh | 322 -- lib/sui/lib/assets/user-data/sync.sh | 31 - lib/sui/lib/common-stack.ts | 62 - lib/sui/lib/config/suiConfig.interface.ts | 18 - lib/sui/lib/config/suiConfig.ts | 36 - lib/sui/lib/constructs/node-cw-dashboard.ts | 235 - .../lib/constructs/sui-node-security-group.ts | 49 - lib/sui/lib/single-node-stack.ts | 136 - lib/sui/package-lock.json | 424 -- lib/sui/package.json | 17 - lib/sui/sample-configs/.env-sample-full | 16 - lib/sui/test/.env-test | 19 - lib/sui/tsconfig.json | 32 - lib/tezos/.gitignore | 9 - lib/tezos/README.md | 207 - lib/tezos/app.ts | 76 - lib/tezos/cdk.json | 57 - lib/tezos/doc/assets/Architecture-HA.png | Bin 107961 -> 0 bytes lib/tezos/doc/assets/Architecture-Single.png | Bin 40531 -> 0 bytes lib/tezos/jest.config.js | 8 - .../lib/assets/cfn-hup/cfn-auto-reloader.conf | 4 - lib/tezos/lib/assets/cfn-hup/cfn-hup.conf | 5 - lib/tezos/lib/assets/cfn-hup/cfn-hup.service | 8 - lib/tezos/lib/assets/copy-data-to-s3.sh | 12 - lib/tezos/lib/assets/cw-agent.json | 76 - lib/tezos/lib/assets/download-snapshot.sh | 17 - lib/tezos/lib/assets/node-cw-dashboard.ts | 216 - lib/tezos/lib/assets/setup-s3-sync-service.sh | 40 - .../assets/sync-checker/syncchecker-tezos.sh | 21 - lib/tezos/lib/assets/user-data/node.sh | 222 - lib/tezos/lib/common-stack.ts | 66 - lib/tezos/lib/config/tzConfig.interface.ts | 31 - lib/tezos/lib/config/tzConfig.ts | 83 - .../lib/constructs/tz-node-security-group.ts | 47 - lib/tezos/lib/ha-nodes-stack.ts | 152 - lib/tezos/lib/single-node-stack.ts | 141 - lib/tezos/lib/snapshot-node-stack.ts | 215 - lib/tezos/package.json | 17 - lib/tezos/sample-configs/.env-sample-full | 38 - lib/tezos/test/.env-test | 30 - lib/tezos/test/common-stack.test.ts | 63 - lib/tezos/test/ha-nodes-stack.test.ts | 253 - lib/tezos/test/single-node-stack.test.ts | 126 - lib/tezos/tsconfig.json | 32 - lib/theta/.gitignore | 9 - lib/theta/README.md | 249 - lib/theta/app.ts | 39 - lib/theta/cdk.json | 57 - ...Architecture-Theta-Edge-Single-Node.drawio | 330 -- ...itecture-Theta-Edge-Single-Node.drawio.png | Bin 116339 -> 0 bytes lib/theta/jest.config.js | 8 - .../lib/assets/cfn-hup/cfn-auto-reloader.conf | 4 - lib/theta/lib/assets/cfn-hup/cfn-hup.conf | 5 - lib/theta/lib/assets/cfn-hup/cfn-hup.service | 8 - lib/theta/lib/assets/cw-agent.json | 76 - .../assets/setup-instance-store-volumes.sh | 43 - .../assets/sync-checker/syncchecker-theta.sh | 16 - lib/theta/lib/assets/user-data/node.sh | 176 - lib/theta/lib/common-stack.ts | 81 - lib/theta/lib/config/edgeConfig.interface.ts | 26 - lib/theta/lib/config/edgeConfig.ts | 37 - .../constructs/edge-node-security-group.ts | 36 - lib/theta/lib/constructs/node-cw-dashboard.ts | 584 --- lib/theta/lib/single-node-stack.ts | 142 - lib/theta/package.json | 12 - lib/theta/sample-configs/.env-sample-full | 23 - lib/theta/test/.env-test | 23 - lib/theta/test/common-stack.test.ts | 71 - lib/theta/test/single-node-stack.test.ts | 119 - lib/theta/tsconfig.json | 31 - package-lock.json | 4296 +++++++++++++++++ package.json | 20 +- website/docs/Blueprints/Bsc.md | 1 + website/docs/Blueprints/Ethereum.md | 1 + website/docs/Blueprints/Fantom.md | 8 - website/docs/Blueprints/Scroll.md | 1 + website/docs/Blueprints/Solana.md | 1 + website/docs/Blueprints/Stacks.md | 1 + website/docs/Blueprints/Starknet.md | 8 - website/docs/Blueprints/Sui.md | 8 - website/docs/Blueprints/Tezos.md | 8 - website/docs/Blueprints/Theta.md | 8 - website/docs/Blueprints/Wax.md | 1 + website/package-lock.json | 3091 ++++++------ website/package.json | 12 +- 161 files changed, 7733 insertions(+), 11918 deletions(-) delete mode 100644 lib/fantom/.gitignore delete mode 100644 lib/fantom/README.md delete mode 100644 lib/fantom/app.ts delete mode 100644 lib/fantom/cdk.json delete mode 100644 lib/fantom/doc/assets/Architecture-HA-FANTOM-Node-Runners.drawio delete mode 100644 lib/fantom/doc/assets/Architecture-HA-FANTOM-Node-Runners.drawio.png delete mode 100644 lib/fantom/doc/assets/Architecture-Single-FANTOM-Node-Runners.drawio.png delete mode 100644 lib/fantom/ha-node-deploy.json delete mode 100644 lib/fantom/jest.config.js delete mode 100644 lib/fantom/lib/assets/cfn-hup/cfn-auto-reloader.conf delete mode 100644 lib/fantom/lib/assets/cfn-hup/cfn-hup.conf delete mode 100644 lib/fantom/lib/assets/cfn-hup/cfn-hup.service delete mode 100644 lib/fantom/lib/assets/cw-agent.json delete mode 100644 lib/fantom/lib/assets/download-snapshot.sh delete mode 100644 lib/fantom/lib/assets/fantom-checker/syncchecker-fantom.sh delete mode 100644 lib/fantom/lib/assets/fantom/read-template.sh delete mode 100644 lib/fantom/lib/assets/node-cw-dashboard.ts delete mode 100644 lib/fantom/lib/assets/setup-instance-store-volumes.sh delete mode 100644 lib/fantom/lib/assets/user-data/node.sh delete mode 100644 lib/fantom/lib/common-stack.ts delete mode 100644 lib/fantom/lib/config/fantomConfig.interface.ts delete mode 100644 lib/fantom/lib/config/fantomConfig.ts delete mode 100644 lib/fantom/lib/constructs/fantom-node-security-group.ts delete mode 100644 lib/fantom/lib/ha-nodes-stack.ts delete mode 100644 lib/fantom/lib/single-node-stack.ts delete mode 100644 lib/fantom/package.json delete mode 100644 lib/fantom/sample-configs/.env-sample-read delete mode 100644 lib/fantom/test/.env-test delete mode 100644 lib/fantom/test/common-stack.test.ts delete mode 100644 lib/fantom/test/ha-nodes-stack.test.ts delete mode 100644 lib/fantom/test/single-node-stack.test.ts delete mode 100644 lib/fantom/tsconfig.json create mode 100644 lib/scroll/package-lock.json create mode 100644 lib/stacks/package-lock.json delete mode 100644 lib/starknet/.gitignore delete mode 100644 lib/starknet/.npmignore delete mode 100644 lib/starknet/README.md delete mode 100644 lib/starknet/app.ts delete mode 100644 lib/starknet/cdk.json delete mode 100644 lib/starknet/doc/assets/Architecture-SingleNode.drawio delete mode 100644 lib/starknet/doc/assets/Architecture-SingleNode.png delete mode 100644 lib/starknet/jest.config.js delete mode 100644 lib/starknet/lib/amb-ethereum-single-node-stack.ts delete mode 100644 lib/starknet/lib/assets/cw-agent.json delete mode 100644 lib/starknet/lib/assets/restore-from-snapshot.sh delete mode 100644 lib/starknet/lib/assets/setup-instance-store-volumes.sh delete mode 100644 lib/starknet/lib/assets/starknet/rpc-template.sh delete mode 100644 lib/starknet/lib/assets/sync-checker/syncchecker-starknet.sh delete mode 100644 lib/starknet/lib/assets/user-data/node.sh delete mode 100644 lib/starknet/lib/common-stack.ts delete mode 100644 lib/starknet/lib/config/starknetConfig.interface.ts delete mode 100644 lib/starknet/lib/config/starknetConfig.ts delete mode 100644 lib/starknet/lib/constructs/node-cw-dashboard.ts delete mode 100644 lib/starknet/lib/constructs/starknet-node-security-group.ts delete mode 100644 lib/starknet/lib/single-node-stack.ts delete mode 100644 lib/starknet/package.json delete mode 100644 lib/starknet/sample-configs/.env-sample-full delete mode 100644 lib/starknet/test/.env-test delete mode 100644 lib/starknet/test/starknet-single-node.test.ts delete mode 100644 lib/starknet/tsconfig.json delete mode 100644 lib/sui/README.md delete mode 100644 lib/sui/app.ts delete mode 100644 lib/sui/cdk.json delete mode 100644 lib/sui/doc/assets/Architecture-SingleNode.png delete mode 100644 lib/sui/jest.config.js delete mode 100644 lib/sui/lib/assets/cw-agent.json delete mode 100644 lib/sui/lib/assets/user-data/node.sh delete mode 100644 lib/sui/lib/assets/user-data/sync.sh delete mode 100644 lib/sui/lib/common-stack.ts delete mode 100644 lib/sui/lib/config/suiConfig.interface.ts delete mode 100644 lib/sui/lib/config/suiConfig.ts delete mode 100644 lib/sui/lib/constructs/node-cw-dashboard.ts delete mode 100644 lib/sui/lib/constructs/sui-node-security-group.ts delete mode 100644 lib/sui/lib/single-node-stack.ts delete mode 100644 lib/sui/package-lock.json delete mode 100644 lib/sui/package.json delete mode 100644 lib/sui/sample-configs/.env-sample-full delete mode 100644 lib/sui/test/.env-test delete mode 100644 lib/sui/tsconfig.json delete mode 100644 lib/tezos/.gitignore delete mode 100644 lib/tezos/README.md delete mode 100644 lib/tezos/app.ts delete mode 100644 lib/tezos/cdk.json delete mode 100644 lib/tezos/doc/assets/Architecture-HA.png delete mode 100644 lib/tezos/doc/assets/Architecture-Single.png delete mode 100644 lib/tezos/jest.config.js delete mode 100644 lib/tezos/lib/assets/cfn-hup/cfn-auto-reloader.conf delete mode 100644 lib/tezos/lib/assets/cfn-hup/cfn-hup.conf delete mode 100644 lib/tezos/lib/assets/cfn-hup/cfn-hup.service delete mode 100644 lib/tezos/lib/assets/copy-data-to-s3.sh delete mode 100644 lib/tezos/lib/assets/cw-agent.json delete mode 100644 lib/tezos/lib/assets/download-snapshot.sh delete mode 100644 lib/tezos/lib/assets/node-cw-dashboard.ts delete mode 100644 lib/tezos/lib/assets/setup-s3-sync-service.sh delete mode 100644 lib/tezos/lib/assets/sync-checker/syncchecker-tezos.sh delete mode 100644 lib/tezos/lib/assets/user-data/node.sh delete mode 100644 lib/tezos/lib/common-stack.ts delete mode 100644 lib/tezos/lib/config/tzConfig.interface.ts delete mode 100644 lib/tezos/lib/config/tzConfig.ts delete mode 100644 lib/tezos/lib/constructs/tz-node-security-group.ts delete mode 100644 lib/tezos/lib/ha-nodes-stack.ts delete mode 100644 lib/tezos/lib/single-node-stack.ts delete mode 100644 lib/tezos/lib/snapshot-node-stack.ts delete mode 100644 lib/tezos/package.json delete mode 100644 lib/tezos/sample-configs/.env-sample-full delete mode 100644 lib/tezos/test/.env-test delete mode 100644 lib/tezos/test/common-stack.test.ts delete mode 100644 lib/tezos/test/ha-nodes-stack.test.ts delete mode 100644 lib/tezos/test/single-node-stack.test.ts delete mode 100644 lib/tezos/tsconfig.json delete mode 100644 lib/theta/.gitignore delete mode 100644 lib/theta/README.md delete mode 100644 lib/theta/app.ts delete mode 100644 lib/theta/cdk.json delete mode 100644 lib/theta/doc/assets/Architecture-Theta-Edge-Single-Node.drawio delete mode 100644 lib/theta/doc/assets/Architecture-Theta-Edge-Single-Node.drawio.png delete mode 100644 lib/theta/jest.config.js delete mode 100644 lib/theta/lib/assets/cfn-hup/cfn-auto-reloader.conf delete mode 100644 lib/theta/lib/assets/cfn-hup/cfn-hup.conf delete mode 100644 lib/theta/lib/assets/cfn-hup/cfn-hup.service delete mode 100644 lib/theta/lib/assets/cw-agent.json delete mode 100644 lib/theta/lib/assets/setup-instance-store-volumes.sh delete mode 100644 lib/theta/lib/assets/sync-checker/syncchecker-theta.sh delete mode 100644 lib/theta/lib/assets/user-data/node.sh delete mode 100644 lib/theta/lib/common-stack.ts delete mode 100644 lib/theta/lib/config/edgeConfig.interface.ts delete mode 100644 lib/theta/lib/config/edgeConfig.ts delete mode 100644 lib/theta/lib/constructs/edge-node-security-group.ts delete mode 100644 lib/theta/lib/constructs/node-cw-dashboard.ts delete mode 100644 lib/theta/lib/single-node-stack.ts delete mode 100644 lib/theta/package.json delete mode 100644 lib/theta/sample-configs/.env-sample-full delete mode 100644 lib/theta/test/.env-test delete mode 100644 lib/theta/test/common-stack.test.ts delete mode 100644 lib/theta/test/single-node-stack.test.ts delete mode 100644 lib/theta/tsconfig.json create mode 100644 package-lock.json delete mode 100644 website/docs/Blueprints/Fantom.md delete mode 100644 website/docs/Blueprints/Starknet.md delete mode 100644 website/docs/Blueprints/Sui.md delete mode 100644 website/docs/Blueprints/Tezos.md delete mode 100644 website/docs/Blueprints/Theta.md diff --git a/docs/pre-merge-tools.md b/docs/pre-merge-tools.md index a15cbc1e..a154ea8c 100644 --- a/docs/pre-merge-tools.md +++ b/docs/pre-merge-tools.md @@ -42,4 +42,4 @@ npm run install-pre-commit-mac npm run run-pre-commit ``` -4. Optionally, run [shellcheck](https://github.com/koalaman/shellcheck) to check for common problems in your shell scripts. +4. Optionally, run [shellcheck](https://github.com/koalaman/shellcheck) to check for common problems in your shell scripts. \ No newline at end of file diff --git a/docs/setup-cloud9.md b/docs/setup-cloud9.md index 9450b4ec..a48eec57 100644 --- a/docs/setup-cloud9.md +++ b/docs/setup-cloud9.md @@ -16,18 +16,18 @@ Create an instance profile called **Cloud9-Developer-Access** ```bash cat > ec2-trust-policy.json < - -

Well-Architected Checklist - -This is the Well-Architected checklist for Fantom nodes implementation of the AWS Blockchain Node Runner app. This checklist takes into account questions from the [AWS Well-Architected Framework](https://aws.amazon.com/architecture/well-architected/) which are relevant to this workload. Please feel free to add more checks from the framework if required for your workload. - -| Pillar | Control | Question/Check | Remarks | -|:------------------------|:----------------------------------|:---------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Security | Network protection | Are there unnecessary open ports in security groups? | Please note that ports 5050 (TCP/UDP) for Fantom are open to public to support P2P protocols. | -| | | Traffic inspection | Traffic protection is not used in the solution. [AWS Web Applications Firewall (WAF)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html) could be implemented for traffic over HTTP(S), [AWS Shield](https://docs.aws.amazon.com/waf/latest/developerguide/shield-chapter.html) provides Distributed Denial of Service (DDoS) protection. Additional charges will apply. | -| | Compute protection | Reduce attack surface | This solution uses Ubuntu 22.04 AMI(`Ubuntu 22.04.4`). You may choose to run hardening scripts on it. | -| | | Enable people to perform actions at a distance | This solution uses [AWS Systems Manager for terminal session](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-sessions-start.html#start-sys-console), not ssh ports. | -| | Data protection at rest | Use encrypted Amazon Elastic Block Store (Amazon EBS) volumes | This solution uses encrypted Amazon EBS volumes. | -| | Data protection in transit | Use TLS | The AWS Application Load balancer currently uses HTTP listener. Create HTTPS listener with self signed certificate if TLS is desired. | -| | Authorization and access control | Use instance profile with Amazon Elastic Compute Cloud (Amazon EC2) instances | This solution uses AWS Identity and Access Management (AWS IAM) role instead of IAM user. | -| | | Following principle of least privilege access | In all node types, root user is not used (using special user "bcuser" instead). | -| | Application security | Security focused development practices | cdk-nag is being used with appropriate suppressions. | -| Cost optimization | Service selection | Use cost effective resources | 1/ We use x86-based binaries. We recommend using the `m6a.2xlarge` EC2 instance type to optimize computational costs. 2/ Cost-effective EBS gp3 are used instead of io2. | -| | Cost awareness | Estimate costs | Single RPC node with `m6a.2xlarge` EBS gp3 volumes about 4000 GB(1000 IOPS, 700 MBps/s throughput) with On-Demand pricing will cost around US$854.54 per month in the US East (N. Virginia) region. More cost-optimal option with 3 year EC2 Instance Savings plan the cost goes down to $594.15 USD. To create your own estimate use [AWS Pricing Calculator](https://calculator.aws/#/) | -| Reliability | Resiliency implementation | Withstand component failures | This solution uses AWS Application Load Balancer with RPC nodes for high availability. Newly provisioned Fantom nodes triggered by Auto Scaling get up and running in about 300 minutes. | -| | Data backup | How is data backed up? | Considering blockchain data is replicated by nodes automatically and Fantom nodes sync from start within an hour, we don't use any additional mechanisms to backup the data. | -| | Resource monitoring | How are workload resources monitored? | Resources are being monitored using Amazon CloudWatch dashboards. Amazon CloudWatch custom metrics are being pushed via CloudWatch Agent. | -| Performance efficiency | Compute selection | How is compute solution selected? | Compute solution is selected based on best price-performance, i.e. AWS Graviton-based Amazon EC2 instances. | -| | Storage selection | How is storage solution selected? | Storage solution is selected based on best price-performance, i.e. gp3 Amazon EBS volumes with optimal IOPS and throughput. | -| | Architecture selection | How is the best performance architecture selected? | We used a combination of recommendations from the Fantom community and our own testing. | -| Operational excellence | Workload health | How is health of workload determined? | Health of workload is determined via AWS Application Load Balancer Target Group Health Checks, on port 8845. | -| Sustainability | Hardware & services | Select most efficient hardware for your workload | The solution uses Graviton-powered instances. There is a potential to use AWS Graviton-based Amazon EC2 instances which offer the best performance per watt of energy use in Amazon EC2. | - - -
- -Recommended Infrastructure - - -| Usage pattern | Ideal configuration | Primary option on AWS | Config reference | -|---------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------|-------------------------------------------------------| -| 1/ Fullnode | 8 vCPU, 32 GB RAM, Data volume: EBS gp3 2TB, 7K IOPS, 400 MB/s throughput | `m6a.2xlarge` EBS gp3 volumes about 2000 GB(7000 IOPS, 400 MBps/s throughput) | [.env-sample-full](./sample-configs/.env-sample-full) | -
- -## Setup Instructions - -### Setup Cloud9 - -We will use AWS Cloud9 to execute the subsequent commands. Follow the instructions in [Cloud9 Setup](../../docs/setup-cloud9.md) - -### Clone this repository and install dependencies - -```bash -git clone https://github.com/aws-samples/aws-blockchain-node-runners.git -cd aws-blockchain-node-runners -npm install -``` - -### Prepare to deploy nodes - -1. Make sure you are in the root directory of the cloned repository - -2. If you have deleted or don't have the default VPC, create default VPC - - ```bash - aws ec2 create-default-vpc - ``` - - > **NOTE**: - > You may see the following error if the default VPC already exists: `An error occurred (DefaultVpcAlreadyExists) when calling the CreateDefaultVpc operation: A Default VPC already exists for this account in this region.`. That means you can just continue with the following steps. - - -3. Configure the CDK app - - Create your own copy of `.env` file and edit it to update with your AWS Account ID, AWS Region, and optionally the Fantom SNAPSHOTS URI: - - ```bash - cd lib/fantom - pwd - # Make sure you are in aws-blockchain-node-runners/lib/fantom - cp ./sample-configs/.env-sample-read .env - nano .env - ``` - > **IMPORTANT**: - > 1. By default we use the latest Fantom snapshot from [Fantom](https://snapshot.fantom.network/files/snapsync/latest/listtgzfiles.txt) - -4. Deploy common components such as IAM role - - ```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/fantom - npx cdk deploy fantom-common - ``` - - > **IMPORTANT**: - > All AWS CDK v2 deployments use dedicated AWS resources to hold data during deployment. Therefore, your AWS account and Region must be [bootstrapped](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html) to create these resources before you can deploy. If you haven't already bootstrapped, issue the following command: - > ```bash - > cdk bootstrap aws://ACCOUNT-NUMBER/REGION - -### Option 1: Single RPC Node - -1. The inital deployment of a Fantom Fullnode and downloading its snapshot typically takes about 3-12 hours. The Full node uses snapshots data, and downloading and decompressing the data takes time. You can grab a cup of coffee☕️ and patiently wait during this process. Maybe two. After deployment, you'll need to wait for the node to synchronize with the Fantom Blockchain Network (next step). - - ```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/fantom - npx cdk deploy fantom-single-node --json --outputs-file single-node-deploy.json - ``` - -2. After the node is initialised from the snapshot you need to wait from another half a day to a day for the inital syncronization process to complete. The time depends on how fresh the snapshot was. You can use Amazon CloudWatch to track the progress. There is a script that publishes CloudWatch metrics every 5 minutes, where you can watch `sync distance` for consensus client and `blocks behind` for execution client. When the node is fully synced those two metrics shold show 0. To see them: - - - Navigate to [CloudWatch service](https://console.aws.amazon.com/cloudwatch/) (make sure you are in the region you have specified for `AWS_REGION`) - - Open `Dashboards` and select `fantom-single-node---` from the list of dashboards. - -Alternatively, you can manually check [Geth Syncing Status](https://geth.ethereum.org/docs/fundamentals/logs#syncing). Run the following query from within the same VPC and against the private IP of the single RPC node you deployed: - - ```bash - INSTANCE_ID=$(cat single-node-deploy.json | jq -r '..|.singleinstanceid? | select(. != null)') - NODE_INTERNAL_IP=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID --query 'Reservations[*].Instances[*].PrivateIpAddress' --output text --region us-east-1) - - curl http://$NODE_INTERNAL_IP:18545 -X POST -H "Content-Type: application/json" \ - --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' - ``` - - It will return `false` if the node is in sync. If `eth_syncing` returns anything other than false it has not finished syncing. Generally, if syncing is still ongoing, `eth_syncing` will return block info that looks as follows: - - ```javascript - { - jsonrpc: "2.0", - id: 1, - result: { - currentBlock: 37676547, - currentBlockHash: "0x0001ab120000187fd8069d3a4f6501d48ad4800778f40a76d79cf02469272a43", - currentBlockTime: "0x16ec7a4b9a82ebfe", - currentEpoch: "0x1ab13", - highestBlock: 80196141, - highestEpoch: "0x44343", - knownStates: 0, - pulledStates: 0, - startingBlock: 0 - } - } - ``` - -3. Once the initial synchronization is done, you should be able to access the RPC API of that node from within the same VPC. The RPC port is not exposed to the Internet. Run the following query against the private IP of the single RPC node you deployed: - - ```bash - INSTANCE_ID=$(cat single-node-deploy.json | jq -r '..|.singleinstanceid? | select(. != null)') - NODE_INTERNAL_IP=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID --query 'Reservations[*].Instances[*].PrivateIpAddress' --output text) - - # We query token balance of one of the system contracts: https://ftmscan.com/address/0x0000000000000000000000000000000000000001 - curl http://$NODE_INTERNAL_IP:18545 -X POST -H "Content-Type: application/json" \ - --data '{"method":"eth_getBalance","params":["0x0000000000000000000000000000000000000001", "latest"],"id":1,"jsonrpc":"2.0"}' - ``` - You will get a response similar to this: - - ```json - {"jsonrpc":"2.0","id":1,"result":"0xbe9fa76042c4daf622"} - ``` - -### Option 2: Highly Available RPC Nodes - -1. The inital deployment of a Fantom Full node and downloading its snapshot typically takes about 6 hours. The Full node uses snapshots data, and downloading and decompressing the data takes time. You can grab a cup of coffee☕️ and patiently wait during this process. Maybe two. After deployment, you'll need to wait for hour or more for your nodes to synchronize with the Fantom Blockchain Network, depending on how fresh the snapshot was. - - ```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/fantom - npx cdk deploy fantom-ha-nodes --json --outputs-file ha-nodes-deploy.json - ``` - -2. Give the new RPC nodes about few hours to initialize and then run the following query against the load balancer behind the RPC node created - - ```bash - export RPC_ALB_URL=$(cat ha-nodes-deploy.json | jq -r '..|.alburl? | select(. != null)') - echo $RPC_ALB_URL - ``` - - Periodically check [Fantom Syncing Status](https://docs.chainstack.com/reference/fantom-syncing). Run the following query from within the same VPC and against the private IP of the load balancer fronting your nodes: - - ```bash - curl http://$RPC_ALB_URL:18545 -X POST -H "Content-Type: application/json" \ - --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' - ``` - - It will return `false` if the node is in sync. If `eth_syncing` returns anything other than false it has not finished syncing. Generally, if syncing is still ongoing, `eth_syncing` will return block info that looks as follows: - - ```javascript - { - jsonrpc: "2.0", - id: 1, - result: { - currentBlock: 37676547, - currentBlockHash: "0x0001ab120000187fd8069d3a4f6501d48ad4800778f40a76d79cf02469272a43", - currentBlockTime: "0x16ec7a4b9a82ebfe", - currentEpoch: "0x1ab13", - highestBlock: 80196141, - highestEpoch: "0x44343", - knownStates: 0, - pulledStates: 0, - startingBlock: 0 - } - } - ``` - -**NOTE:** By default and for security reasons the load balancer is available only from inside the default VPC in the region where it is deployed. It is not available from the Internet and is not open for external connections. Before you consider opening it up to the internet, **please make sure you protect your RPC APIs**. - -3. Once the initial synchronization is done, you should be able to access the RPC API of that node from within the same VPC. The RPC port is not exposed to the Internet. Run the following query against the private IP of the single RPC node you deployed: - - ```bash - export RPC_ALB_URL=$(cat ha-nodes-deploy.json | jq -r '..|.alburl? | select(. != null)') - echo $RPC_ALB_URL - - # We query token balance of one of the system contracts: https://ftmscan.com/address/0x0000000000000000000000000000000000000001 - curl http://$RPC_ALB_URL:18545 -X POST -H "Content-Type: application/json" \ - --data '{"method":"eth_getBalance","params":["0x0000000000000000000000000000000000000001", "latest"],"id":1,"jsonrpc":"2.0"}' - ``` - You will get a response similar to this: - - ```json - {"jsonrpc":"2.0","id":1,"result":"0xbe9fa76042c4daf622"} - ``` - -### Clearing up and undeploy everything - -1. Undeploy HA Nodes, Single Nodes and Common stacks - -```bash -# Setting the AWS account id and region in case local .env file is lost -export AWS_ACCOUNT_ID= -export AWS_REGION= - -pwd -# Make sure you are in aws-blockchain-node-runners/lib/fantom - -# Undeploy Single Node -cdk destroy fantom-single-node - -# Undeploy HA Nodes -cdk destroy fantom-ha-nodes - - # Delete all common components like IAM role and Security Group -cdk destroy fantom-common -``` - -2. Follow steps to delete the Cloud9 instance in [Cloud9 Setup](../../doc/setup-cloud9.md) - -### FAQ - -1. How to check the logs of the clients running on my sync node? - -Please enter the [AWS Management Console - EC2 Instances](https://us-east-2.console.aws.amazon.com/ec2/home?region=us-east-2#Instances:instanceState=running), choose the correct region, copy the instance ID you need to query. - - **Note:** In this tutorial we chose not to use SSH and use Session Manager instead. That allows you to log all sessions in AWS CloudTrail to see who logged into the server and when. If you receive an error similar to `SessionManagerPlugin is not found`, [install Session Manager plugin for AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) - -```bash -pwd -# Make sure you are in aws-blockchain-node-runners/lib/fantom - -export INSTANCE_ID="i-**************" -echo "INSTANCE_ID=" $INSTANCE_ID -aws ssm start-session --target $INSTANCE_ID --region $AWS_REGION -sudo su ec2-user -sudo journalctl -o cat -fu fantom -``` -2. How to check the logs from the EC2 user-data script? - -Please enter the [AWS Management Console - EC2 Instances](https://us-east-2.console.aws.amazon.com/ec2/home?region=us-east-2#Instances:instanceState=running), choose the correct region, copy the instance ID you need to query. - -```bash -pwd -# Make sure you are in aws-blockchain-node-runners/lib/fantom - -export INSTANCE_ID="i-**************" -echo "INSTANCE_ID=" $INSTANCE_ID -aws ssm start-session --target $INSTANCE_ID --region $AWS_REGION -sudo cat /var/log/user-data.log -``` - -3. How can I restart the Fantom service? - -Please enter the [AWS Management Console - EC2 Instances](https://us-east-2.console.aws.amazon.com/ec2/home?region=us-east-2#Instances:instanceState=running), choose the correct region, copy the instance ID you need to query. - -```bash -pwd -# Make sure you are in aws-blockchain-node-runners/lib/fantom - -export INSTANCE_ID="i-**************" -echo "INSTANCE_ID=" $INSTANCE_ID - -aws ssm start-session --target $INSTANCE_ID --region $AWS_REGION -sudo systemctl stop fantom && sleep 20 && sudo systemctl start fantom -``` -**NOTE:** You can also try the following command to obtain more information: -- Check the Fantom service status - - `sudo systemctl status fantom` -- View Fantom service configuration - - `cat /etc/systemd/system/fantom.service` - -5. Where can I find more information about Fantom? - -Please refer to the [Fantom Developer Documentation](https://docs.fantom.foundation/) - -## Upgrades - -When nodes need to be upgraded or downgraded, [use blue/green pattern to do it](https://aws.amazon.com/blogs/devops/performing-bluegreen-deployments-with-aws-codedeploy-and-auto-scaling-groups/). This is not yet automated and contributions are welcome! diff --git a/lib/fantom/app.ts b/lib/fantom/app.ts deleted file mode 100644 index 82e36cc1..00000000 --- a/lib/fantom/app.ts +++ /dev/null @@ -1,55 +0,0 @@ -import 'dotenv/config' -import "source-map-support/register"; -import * as cdk from "aws-cdk-lib"; -import * as config from "./lib/config/fantomConfig"; -import * as configTypes from "./lib/config/fantomConfig.interface"; -import { FantomCommonStack } from "./lib/common-stack"; -import { FantomSingleNodeStack } from "./lib/single-node-stack"; -import { FantomHANodesStack } from "./lib/ha-nodes-stack"; -import * as nag from "cdk-nag"; - -const app = new cdk.App(); -cdk.Tags.of(app).add("Project", "AWS_FANTOM"); - -new FantomCommonStack(app, "fantom-common", { - stackName: `fantom-nodes-common`, - env: { account: config.baseConfig.accountId, region: config.baseConfig.region } -}); - -new FantomSingleNodeStack(app, "fantom-single-node", { - stackName: `fantom-single-node-${config.baseNodeConfig.nodeConfiguration}-${config.baseNodeConfig.fantomNetwork}`, - - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - nodeRole: "single-node", - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - fantomNetwork: config.baseNodeConfig.fantomNetwork, - nodeConfiguration: config.baseNodeConfig.nodeConfiguration, - snapshotsUrl:config.baseNodeConfig.snapshotsUrl, - dataVolume: config.baseNodeConfig.dataVolume, -}); - -new FantomHANodesStack(app, "fantom-ha-nodes", { - stackName: `fantom-ha-nodes-${config.baseNodeConfig.nodeConfiguration}-${config.baseNodeConfig.fantomNetwork}`, - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - nodeRole: "rpc-node", - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - fantomNetwork: config.baseNodeConfig.fantomNetwork, - nodeConfiguration: config.baseNodeConfig.nodeConfiguration, - snapshotsUrl:config.baseNodeConfig.snapshotsUrl, - dataVolume: config.baseNodeConfig.dataVolume, - - albHealthCheckGracePeriodMin: config.haNodeConfig.albHealthCheckGracePeriodMin, - heartBeatDelayMin: config.haNodeConfig.heartBeatDelayMin, - numberOfNodes: config.haNodeConfig.numberOfNodes -}); - -// Security Check -cdk.Aspects.of(app).add( - new nag.AwsSolutionsChecks({ - verbose: false, - reports: true, - logIgnores: false - }) -); diff --git a/lib/fantom/cdk.json b/lib/fantom/cdk.json deleted file mode 100644 index 7714e8c2..00000000 --- a/lib/fantom/cdk.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "app": "npx ts-node --prefer-ts-exts app.ts", - "watch": { - "include": [ - "**" - ], - "exclude": [ - "README.md", - "cdk*.json", - "**/*.d.ts", - "**/*.js", - "tsconfig.json", - "package*.json", - "yarn.lock", - "node_modules", - "test" - ] - }, - "context": { - "@aws-cdk/aws-lambda:recognizeLayerVersion": true, - "@aws-cdk/core:checkSecretUsage": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ], - "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, - "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, - "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, - "@aws-cdk/aws-iam:minimizePolicies": true, - "@aws-cdk/core:validateSnapshotRemovalPolicy": true, - "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, - "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, - "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, - "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, - "@aws-cdk/core:enablePartitionLiterals": true, - "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, - "@aws-cdk/aws-iam:standardizedServicePrincipals": true, - "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, - "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, - "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, - "@aws-cdk/aws-route53-patters:useCertificate": true, - "@aws-cdk/customresources:installLatestAwsSdkDefault": false, - "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, - "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, - "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, - "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, - "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, - "@aws-cdk/aws-redshift:columnId": true, - "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, - "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, - "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, - "@aws-cdk/aws-kms:aliasNameRef": true, - "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, - "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, - "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true - } -} diff --git a/lib/fantom/doc/assets/Architecture-HA-FANTOM-Node-Runners.drawio b/lib/fantom/doc/assets/Architecture-HA-FANTOM-Node-Runners.drawio deleted file mode 100644 index d6e8669e..00000000 --- a/lib/fantom/doc/assets/Architecture-HA-FANTOM-Node-Runners.drawio +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/fantom/doc/assets/Architecture-HA-FANTOM-Node-Runners.drawio.png b/lib/fantom/doc/assets/Architecture-HA-FANTOM-Node-Runners.drawio.png deleted file mode 100644 index c352fee92992bfe8322ebd238cbd400c44c88576..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148677 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

)cHNGujmALONs<}=2_0hdme2>4IyUo5_ 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/test'], - testMatch: ['**/*.test.ts'], - transform: { - '^.+\\.tsx?$': 'ts-jest' - } -}; diff --git a/lib/fantom/lib/assets/cfn-hup/cfn-auto-reloader.conf b/lib/fantom/lib/assets/cfn-hup/cfn-auto-reloader.conf deleted file mode 100644 index 3cd32a0a..00000000 --- a/lib/fantom/lib/assets/cfn-hup/cfn-auto-reloader.conf +++ /dev/null @@ -1,4 +0,0 @@ -[cfn-auto-reloader-hook] -triggers=post.update -path=Resources.WebServerHost.Metadata.AWS::CloudFormation::Init -action=/opt/aws/bin/cfn-init -v --stack __AWS_STACK_NAME__ --resource WebServerHost --region __AWS_REGION__ diff --git a/lib/fantom/lib/assets/cfn-hup/cfn-hup.conf b/lib/fantom/lib/assets/cfn-hup/cfn-hup.conf deleted file mode 100644 index 2163b37a..00000000 --- a/lib/fantom/lib/assets/cfn-hup/cfn-hup.conf +++ /dev/null @@ -1,5 +0,0 @@ -[main] -stack=__AWS_STACK_ID__ -region=__AWS_REGION__ -# The interval used to check for changes to the resource metadata in minutes. Default is 15 -interval=2 diff --git a/lib/fantom/lib/assets/cfn-hup/cfn-hup.service b/lib/fantom/lib/assets/cfn-hup/cfn-hup.service deleted file mode 100644 index 2660ea46..00000000 --- a/lib/fantom/lib/assets/cfn-hup/cfn-hup.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=cfn-hup daemon -[Service] -Type=simple -ExecStart=/usr/local/bin/cfn-hup -Restart=always -[Install] -WantedBy=multi-user.target diff --git a/lib/fantom/lib/assets/cw-agent.json b/lib/fantom/lib/assets/cw-agent.json deleted file mode 100644 index 28833017..00000000 --- a/lib/fantom/lib/assets/cw-agent.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "agent": { - "metrics_collection_interval": 60, - "run_as_user": "root" - }, - "metrics": { - "aggregation_dimensions": [ - [ - "InstanceId" - ] - ], - "append_dimensions": { - "InstanceId": "${aws:InstanceId}" - }, - "metrics_collected": { - "cpu": { - "measurement": [ - "cpu_usage_idle", - "cpu_usage_iowait", - "cpu_usage_user", - "cpu_usage_system" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ], - "totalcpu": false - }, - "disk": { - "measurement": [ - "used_percent" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ] - }, - "diskio": { - "measurement": [ - "io_time", - "write_bytes", - "read_bytes", - "writes", - "reads", - "write_time", - "read_time", - "iops_in_progress" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ] - }, - "mem": { - "measurement": [ - "mem_used_percent", - "mem_cached" - ], - "metrics_collection_interval": 60 - }, - "netstat": { - "measurement": [ - "tcp_established", - "tcp_time_wait" - ], - "metrics_collection_interval": 60 - }, - "swap": { - "measurement": [ - "swap_used_percent" - ], - "metrics_collection_interval": 60 - } - } - } -} diff --git a/lib/fantom/lib/assets/download-snapshot.sh b/lib/fantom/lib/assets/download-snapshot.sh deleted file mode 100644 index e36a7c63..00000000 --- a/lib/fantom/lib/assets/download-snapshot.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -set +e - -source /etc/environment - -echo "Downloading FANTOM snapshot." - -cd /data - -if [[ -n ${FANTOM_SNAPSHOTS_URI} && ${FANTOM_SNAPSHOTS_URI} != "none" ]]; then - for FILE in `curl ${FANTOM_SNAPSHOTS_URI}`; do - echo $FILE; - axel -n 20 ${FANTOM_SNAPSHOTS_URI%%/latest/listtgzfiles.txt}/$FILE && \ - tar --use-compress-program="pigz -d" -xvf ${FILE##*/} && \ - rm ${FILE##*/} || \ - echo "Problem with downloading or expanding file $FILE" - done -fi - -echo "Downloading FANTOM snapshot finished" - -echo "FANTOM snapshot is ready !!!" diff --git a/lib/fantom/lib/assets/fantom-checker/syncchecker-fantom.sh b/lib/fantom/lib/assets/fantom-checker/syncchecker-fantom.sh deleted file mode 100644 index 04a37d27..00000000 --- a/lib/fantom/lib/assets/fantom-checker/syncchecker-fantom.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -FANTOM_SYNC_STATS=$(su bcuser -c '/home/bcuser/go-opera/build/opera attach --datadir=/data --exec "ftm.syncing"') -# Syncing status results: -# -#{ -# currentBlock: 37676547, -# currentBlockHash: "0x0001ab120000187fd8069d3a4f6501d48ad4800778f40a76d79cf02469272a43", -# currentBlockTime: "0x16ec7a4b9a82ebfe", -# currentEpoch: "0x1ab13", -# highestBlock: 82410549, -# highestEpoch: "0x45f22", -# knownStates: 0, -# pulledStates: 0, -# startingBlock: 0 -#} -# -# Synced status results: -# -# false -# -# TODO: if a node falls behind, does it revert to syncing? - -# If false, then get current block number: -if [ -n "$FANTOM_SYNC_STATS" ] && [ "$FANTOM_SYNC_STATS" != "false" ]; then - FANTOM_SYNC_BLOCK=$(su bcuser -c '/home/bcuser/go-opera/build/opera attach --datadir=/data --exec "ftm.syncing.currentBlock"') - FANTOM_HIGHEST_BLOCK=$(su bcuser -c '/home/bcuser/go-opera/build/opera attach --datadir=/data --exec "ftm.syncing.highestBlock"') - - FANTOM_BLOCKS_BEHIND="$((FANTOM_HIGHEST_BLOCK-FANTOM_SYNC_BLOCK))" - -else - FANTOM_SYNC_BLOCK=$(su bcuser -c '/home/bcuser/go-opera/build/opera attach --datadir=/data --exec "ftm.blockNumber"') - FANTOM_BLOCKS_BEHIND=0 -fi - -# Sending data to CloudWatch -TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") -INSTANCE_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/instance-id) -REGION=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq .region -r) -TIMESTAMP=$(date +"%Y-%m-%dT%H:%M:%S%:z") - -aws cloudwatch put-metric-data --metric-name fantom_sync_block --namespace CWAgent --value $FANTOM_SYNC_BLOCK --timestamp $TIMESTAMP --dimensions InstanceId=$INSTANCE_ID --region $REGION -aws cloudwatch put-metric-data --metric-name fantom_blocks_behind --namespace CWAgent --value $FANTOM_BLOCKS_BEHIND --timestamp $TIMESTAMP --dimensions InstanceId=$INSTANCE_ID --region $REGION diff --git a/lib/fantom/lib/assets/fantom/read-template.sh b/lib/fantom/lib/assets/fantom/read-template.sh deleted file mode 100644 index b3bcc7c4..00000000 --- a/lib/fantom/lib/assets/fantom/read-template.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -set -e -echo "Script is starting..." -ulimit -n 500000 - -# Get local IP -TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") -export EC2_INTERNAL_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s "http://169.254.169.254/latest/meta-data/local-ipv4") - -# Start read node -/home/bcuser/go-opera/build/opera --genesis /data/genesis.g \ - --datadir /data \ - --maxpeers 110 \ - --cache 24000 \ - --nousb \ - --db.preset ldb-1 \ - --syncmode snap \ - --http --http.port=18545 --http.corsdomain="*" \ - --http.addr="${EC2_INTERNAL_IP}" \ - --http.api=eth,web3,net,txpool,ftm - -echo "Script is still running..." diff --git a/lib/fantom/lib/assets/node-cw-dashboard.ts b/lib/fantom/lib/assets/node-cw-dashboard.ts deleted file mode 100644 index 3e4e0089..00000000 --- a/lib/fantom/lib/assets/node-cw-dashboard.ts +++ /dev/null @@ -1,235 +0,0 @@ -export const SyncNodeCWDashboardJSON = { - "widgets": [ - { - "height": 5, - "width": 6, - "y": 0, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "CPUUtilization", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "CPU utilization (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 18, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "NetworkIn", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Network in (bytes)" - } - }, - { - "height": 5, - "width": 6, - "y": 0, - "x": 18, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "NetworkOut", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Network out (bytes)" - } - }, - { - "height": 5, - "width": 6, - "y": 10, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 300, - "metrics": [ - [ "CWAgent", "mem_used_percent", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Mem Used (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 300, - "metrics": [ - [ "CWAgent", "cpu_usage_iowait", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "CPU Usage IO wait (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 0, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ { "expression": "m7/PERIOD(m7)", "label": "Read", "id": "e7" } ], - [ "CWAgent", "diskio_reads", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7", "visible": false, "stat": "Sum", "period": 60 } ], - [ { "expression": "m8/PERIOD(m8)", "label": "Write", "id": "e8" } ], - [ "CWAgent", "diskio_writes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m8", "visible": false, "stat": "Sum", "period": 60 } ] - ], - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Sum", - "period": 60, - "title": "nvme1n1 Volume Read/Write (IO/sec)" - } - }, - { - "height": 4, - "width": 6, - "y": 0, - "x": 12, - "type": "metric", - "properties": { - "metrics": [ - [ "CWAgent", "fantom_sync_block", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "sparkline": true, - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Maximum", - "period": 60, - "title": "FANTOM Client Block Height" - } - }, - { - "height": 4, - "width": 6, - "y": 4, - "x": 12, - "type": "metric", - "properties": { - "sparkline": true, - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Maximum", - "period": 60, - "metrics": [ - [ "CWAgent", "fantom_blocks_behind", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "FANTOM Client Blocks Behind" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 6, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Sum", - "period": 60, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ { "expression": "IF(m7_2 !=0, (m7_1 / m7_2), 0)", "label": "Read", "id": "e7" } ], - [ "CWAgent", "diskio_read_time", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_1", "visible": false, "stat": "Sum", "period": 60 } ], - [ "CWAgent", "diskio_reads", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_2", "visible": false, "stat": "Sum", "period": 60 } ], - [ { "expression": "IF(m7_4 !=0, (m7_3 / m7_4), 0)", "label": "Write", "id": "e8" } ], - [ "CWAgent", "diskio_write_time", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_3", "visible": false, "stat": "Sum", "period": 60 } ], - [ "CWAgent", "diskio_writes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_4", "visible": false, "stat": "Sum", "period": 60 } ] - ], - "title": "nvme1n1 Volume Read/Write latency (ms/op)" - } - }, - { - "height": 5, - "width": 6, - "y": 10, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ { "expression": "(m2/1048576)/PERIOD(m2)", "label": "Read", "id": "e2", "period": 60, "region": "${REGION}" } ], - [ "CWAgent", "diskio_read_bytes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m2", "stat": "Sum", "visible": false, "period": 60 } ], - [ { "expression": "(m3/1048576)/PERIOD(m3)", "label": "Write", "id": "e3", "period": 60, "region": "${REGION}" } ], - [ "CWAgent", "diskio_write_bytes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m3", "stat": "Sum", "visible": false, "period": 60 } ] - ], - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 60, - "title": "nvme1n1 Volume Read/Write throughput (MiB/sec)" - } - }, - { - "height": 3, - "width": 6, - "y": 15, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ "CWAgent", "disk_used_percent", "path", "/data", "InstanceId", "${INSTANCE_ID}", "device", "nvme1n1", "fstype", "xfs", { "region": "${REGION}", "label": "/data" } ] - ], - "sparkline": true, - "view": "singleValue", - "region": "${REGION}", - "title": "nvme1n1 Disk Used (%)", - "period": 60, - "stat": "Average" - } - } - ] -} diff --git a/lib/fantom/lib/assets/setup-instance-store-volumes.sh b/lib/fantom/lib/assets/setup-instance-store-volumes.sh deleted file mode 100644 index 667e872a..00000000 --- a/lib/fantom/lib/assets/setup-instance-store-volumes.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -source /etc/environment - -if [[ "$DATA_VOLUME_TYPE" == "instance-store" ]]; then - echo "Data volume type is instance store" - export DATA_VOLUME_ID=/dev/$(lsblk -lnb | awk 'max < $4 {max = $4; vol = $1} END {print vol}') -fi - -if [ -n "$DATA_VOLUME_ID" ]; then - if [ $(df --output=target | grep -c "/data") -lt 1 ]; then - echo "Checking fstab for Data volume" - - sudo mkfs.xfs -f $DATA_VOLUME_ID - sleep 10 - DATA_VOLUME_UUID=$(lsblk -fn -o UUID $DATA_VOLUME_ID) - DATA_VOLUME_FSTAB_CONF="UUID=$DATA_VOLUME_UUID /data xfs defaults 0 2" - echo "DATA_VOLUME_ID="$DATA_VOLUME_ID - echo "DATA_VOLUME_UUID="$DATA_VOLUME_UUID - echo "DATA_VOLUME_FSTAB_CONF="$DATA_VOLUME_FSTAB_CONF - - # Check if data disc is already in fstab and replace the line if it is with the new disc UUID - if [ $(grep -c "data" /etc/fstab) -gt 0 ]; then - SED_REPLACEMENT_STRING="$(grep -n "/data" /etc/fstab | cut -d: -f1)s#.*#$DATA_VOLUME_FSTAB_CONF#" - sudo cp /etc/fstab /etc/fstab.bak - sudo sed -i "$SED_REPLACEMENT_STRING" /etc/fstab - else - echo $DATA_VOLUME_FSTAB_CONF | sudo tee -a /etc/fstab - fi - - sudo mount -a - - chown bcuser:bcuser -R /data - else - echo "Data volume is mounted, nothing changed" - fi -fi diff --git a/lib/fantom/lib/assets/user-data/node.sh b/lib/fantom/lib/assets/user-data/node.sh deleted file mode 100644 index 2282ffb7..00000000 --- a/lib/fantom/lib/assets/user-data/node.sh +++ /dev/null @@ -1,240 +0,0 @@ -#!/bin/bash -set +e - -{ echo "AWS_REGION=${_AWS_REGION_}" - echo "ASSETS_S3_PATH=${_ASSETS_S3_PATH_}" - echo "FANTOM_SNAPSHOTS_URI=${_FANTOM_SNAPSHOTS_URI_}" - echo "STACK_NAME=${_STACK_NAME_}" - echo "STACK_ID=${_STACK_ID_}" - echo "RESOURCE_ID=${_NODE_CF_LOGICAL_ID_}" - echo "DATA_VOLUME_TYPE=${_DATA_VOLUME_TYPE_}" - echo "DATA_VOLUME_SIZE=${_DATA_VOLUME_SIZE_}" - echo "FANTOM_NODE_TYPE=${_FANTOM_NODE_TYPE_}" - echo "FANTOM_NETWORK=${_FANTOM_NETWORK_}" - echo "LIFECYCLE_HOOK_NAME=${_LIFECYCLE_HOOK_NAME_}" - echo "AUTOSCALING_GROUP_NAME=${_AUTOSCALING_GROUP_NAME_}" - echo "NODE_ROLE=${_NODE_ROLE_}" - } >> /etc/environment - -source /etc/environment - -exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1 - -sleep 10 - -apt -yqq update -apt -yqq install awscli unzip jq python3-pip axel pigz build-essential git - -echo "Assigning Swap Space" -# Check if a swap file already exists -if [ -f /swapfile ]; then - # Remove the existing swap file - swapoff /swapfile - rm -rf /swapfile -fi - -# Create a new swap file -total_mem=$(grep MemTotal /proc/meminfo | awk '{print $2}') -# Calculate the swap size -swap_size=$((total_mem / 3)) -# Convert the swap size to MB -swap_size_mb=$((swap_size / 1024)) -unit=M -fallocate -l $swap_size_mb$unit /swapfile -chmod 600 /swapfile -mkswap /swapfile -swapon /swapfile - -# Enable the swap space to persist after reboot. -echo "/swapfile none swap sw 0 0" | sudo tee -a /etc/fstab - -sysctl vm.swappiness=6 -sysctl vm.vfs_cache_pressure=10 -echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.conf -echo "vm.vfs_cache_pressure=10" | sudo tee -a /etc/sysctl.conf - -free -h - - - -# Download golang -mkdir -p temp && cd temp -wget https://go.dev/dl/go1.19.3.linux-amd64.tar.gz -sudo tar -xvf go1.19.3.linux-amd64.tar.gz -sudo rm -rf /usr/local/go -sudo mv go /usr/local/ -rm go1.19.3.linux-amd64.tar.gz - -# Setup golang environment variables -echo 'export GOROOT=/usr/local/go' > ~/.bash_aliases -echo 'export GOPATH=$HOME/go' >> ~/.bash_aliases -echo 'export PATH=$GOPATH/bin:$GOROOT/bin:$PATH' >> ~/.bash_aliases -source ~/.bash_aliases - -echo 'export GOROOT=/usr/local/go -export GOPATH=$HOME/go -export PATH=$GOPATH/bin:$GOROOT/bin:$PATH' > /etc/profile.d/custom-path.sh - -cd /opt - -echo "Downloading assets zip file" -aws s3 cp $ASSETS_S3_PATH ./assets.zip --region $AWS_REGION -unzip -q assets.zip - -echo "Install and configure CloudWatch agent" -wget -q https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb -sudo dpkg -i -E amazon-cloudwatch-agent.deb - -echo 'Configuring CloudWatch Agent' -mkdir -p /opt/aws/amazon-cloudwatch-agent/etc/ -cp /opt/cw-agent.json /opt/aws/amazon-cloudwatch-agent/etc/custom-amazon-cloudwatch-agent.json - -echo "Starting CloudWatch Agent" -/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ --a fetch-config -c file:/opt/aws/amazon-cloudwatch-agent/etc/custom-amazon-cloudwatch-agent.json -m ec2 -s -systemctl status amazon-cloudwatch-agent - -echo 'Adding bcuser user and group' -sudo groupadd -g 1002 bcuser -sudo useradd -u 1002 -g 1002 -m -s /bin/bash bcuser -sudo usermod -aG sudo bcuser - -echo "Install FANTOM client" - -sudo su -l bcuser -c "git clone https://github.com/Fantom-foundation/go-opera.git && \ - cd go-opera/ && \ - git checkout release/1.1.3-rc.5 && \ - make" - - -echo 'Configuring FANTOM Node service as a system service' -# Copy startup script to correct location -if [[ "$FANTOM_NODE_TYPE" == "read" ]]; then - sudo mkdir /home/bcuser/bin - sudo mv /opt/fantom/read-template.sh /home/bcuser/bin/node.sh -fi - -sudo chmod +x /home/bcuser/bin/node.sh -sudo chown bcuser:bcuser -R /home/bcuser/ - - -if [[ "$STACK_ID" != "none" ]]; then - echo "Install CloudFormation helper scripts" - mkdir -p /opt/aws/ - pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz - sudo ln -s /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup - - echo "Configuring CloudFormation helper scripts" - mkdir -p /etc/cfn/ - mv /opt/cfn-hup/cfn-hup.conf /etc/cfn/cfn-hup.conf - sed -i "s;__AWS_STACK_ID__;\"$STACK_ID\";g" /etc/cfn/cfn-hup.conf - sed -i "s;__AWS_REGION__;\"$AWS_REGION\";g" /etc/cfn/cfn-hup.conf - - mkdir -p /etc/cfn/hooks.d/ - mv /opt/cfn-hup/cfn-auto-reloader.conf /etc/cfn/hooks.d/cfn-auto-reloader.conf - sed -i "s;__AWS_STACK_NAME__;\"$STACK_NAME\";g" /etc/cfn/hooks.d/cfn-auto-reloader.conf - sed -i "s;__AWS_REGION__;\"$AWS_REGION\";g" /etc/cfn/hooks.d/cfn-auto-reloader.conf - - echo "Starting CloudFormation helper scripts as a service" - mv /opt/cfn-hup/cfn-hup.service /etc/systemd/system/cfn-hup.service - - systemctl daemon-reload - systemctl enable --now cfn-hup - systemctl start cfn-hup.service - -fi - -echo "Starting FANTOM as a service" -sudo bash -c 'cat > /etc/systemd/system/fantom.service </tmp/syncchecker.log 2>&1") | crontab - -crontab -l - -if [ "$NODE_ROLE" == "single-node" ]; then - echo "Single node. Signaling completion to CloudFormation" - cfn-signal --stack $STACK_NAME --resource $RESOURCE_ID --region $AWS_REGION -fi - -if [ "$NODE_ROLE" == "single-node" ]; then - echo "Single node. Wait for one minute for the volume to be available" - sleep 60 -fi - -mkdir -p /data - -if [[ "$DATA_VOLUME_TYPE" == "instance-store" ]]; then - echo "Data volume type is instance store" - - cd /opt - chmod +x /opt/setup-instance-store-volumes.sh - - (crontab -l; echo "@reboot /opt/setup-instance-store-volumes.sh >/tmp/setup-instance-store-volumes.log 2>&1") | crontab - - crontab -l - - /opt/setup-instance-store-volumes.sh - -else - echo "Data volume type is EBS" - - DATA_VOLUME_ID=/dev/$(lsblk -lnb | awk -v VOLUME_SIZE_BYTES="$DATA_VOLUME_SIZE" '{if ($4== VOLUME_SIZE_BYTES) {print $1}}') - sudo mkfs.xfs -f $DATA_VOLUME_ID - sleep 10 - DATA_VOLUME_UUID=$(lsblk -fn -o UUID $DATA_VOLUME_ID) - DATA_VOLUME_FSTAB_CONF="UUID=$DATA_VOLUME_UUID /data ext4 defaults 0 2" - echo "DATA_VOLUME_ID="$DATA_VOLUME_ID - echo "DATA_VOLUME_UUID="$DATA_VOLUME_UUID - echo "DATA_VOLUME_FSTAB_CONF="$DATA_VOLUME_FSTAB_CONF - echo $DATA_VOLUME_FSTAB_CONF | tee -a /etc/fstab - mount -a -fi - -lsblk -d - -# download snapshot if network is mainnet -if [ "$FANTOM_NETWORK" == "mainnet" ]; then - echo "Downloading FANTOM snapshot" - chmod +x /opt/download-snapshot.sh - /opt/download-snapshot.sh - if [ "$?" == 0 ]; then - echo "Snapshot download successful" - else - echo "Snapshot download failed, falling back to fresh sync" - fi -fi - -# Download Genesis file -wget https://download.fantom.network/mainnet-109331-no-history.g -O /data/genesis.g - -chown bcuser:bcuser -R /data - -sudo systemctl daemon-reload -sudo systemctl enable --now fantom - -if [[ "$LIFECYCLE_HOOK_NAME" != "none" ]]; then - echo "Signaling ASG lifecycle hook to complete" - TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") - INSTANCE_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/instance-id) - aws autoscaling complete-lifecycle-action --lifecycle-action-result CONTINUE --instance-id $INSTANCE_ID --lifecycle-hook-name "$LIFECYCLE_HOOK_NAME" --auto-scaling-group-name "$AUTOSCALING_GROUP_NAME" --region $AWS_REGION -fi - -echo "All Done!!" -set -e diff --git a/lib/fantom/lib/common-stack.ts b/lib/fantom/lib/common-stack.ts deleted file mode 100644 index 5e292885..00000000 --- a/lib/fantom/lib/common-stack.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as iam from "aws-cdk-lib/aws-iam"; -import * as nag from "cdk-nag"; - -export interface FantomCommonStackProps extends cdk.StackProps { - -} - -export class FantomCommonStack extends cdk.Stack { - AWS_STACK_NAME = cdk.Stack.of(this).stackName; - AWS_ACCOUNT_ID = cdk.Stack.of(this).account; - - constructor(scope: cdkConstructs.Construct, id: string, props: FantomCommonStackProps) { - super(scope, id, props); - - const region = cdk.Stack.of(this).region; - - const instanceRole = new iam.Role(this, "node-role", { - assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com"), - managedPolicies: [ - iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMManagedInstanceCore"), - iam.ManagedPolicy.fromAwsManagedPolicyName("CloudWatchAgentServerPolicy") - ] - }); - - instanceRole.addToPolicy(new iam.PolicyStatement({ - resources: ["*"], - actions: ["cloudformation:SignalResource"] - })); - - instanceRole.addToPolicy(new iam.PolicyStatement({ - resources: [`arn:aws:autoscaling:${region}:${this.AWS_ACCOUNT_ID}:autoScalingGroup:*:autoScalingGroupName/fantom-*`], - actions: ["autoscaling:CompleteLifecycleAction"] - })); - - // in lifecycle - instanceRole.addToPolicy(new iam.PolicyStatement({ - resources: [`arn:aws:autoscaling:${region}:${this.AWS_ACCOUNT_ID}:autoScalingGroup:*:autoScalingGroupName/fantom-*`], - actions: ["autoscaling:RecordLifecycleActionHeartbeat"] - })); - - - new cdk.CfnOutput(this, "Instance Role ARN", { - value: instanceRole.roleArn, - exportName: "FantomNodeInstanceRoleArn" - }); - - - // cdk-nag suppressions - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-IAM4", - reason: "AmazonSSMManagedInstanceCore and CloudWatchAgentServerPolicy are restrictive enough" - }, - { - id: "AwsSolutions-IAM5", - reason: "Can't target specific stack: https://github.com/aws/aws-cdk/issues/22657" - } - ], - true - ); - } -} diff --git a/lib/fantom/lib/config/fantomConfig.interface.ts b/lib/fantom/lib/config/fantomConfig.interface.ts deleted file mode 100644 index 5e3efdf2..00000000 --- a/lib/fantom/lib/config/fantomConfig.interface.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as configTypes from "../../../constructs/config.interface"; - -export type FantomNetwork = "mainnet" ; -export type FantomNodeConfiguration = "read" ; - -export type FantomNodeRole = "rpc-node" | "single-node"; - -export interface FantomDataVolumeConfig extends configTypes.DataVolumeConfig { - -} - -export interface FantomBaseConfig extends configTypes.BaseConfig { - -} - -export interface FantomBaseNodeConfig extends configTypes.BaseNodeConfig { - fantomNetwork: FantomNetwork; - nodeConfiguration: FantomNodeConfiguration; - snapshotsUrl: string; - dataVolume: FantomDataVolumeConfig; -} - -export interface FantomHAConfig { - albHealthCheckGracePeriodMin: number; - heartBeatDelayMin: number; - numberOfNodes: number; -} diff --git a/lib/fantom/lib/config/fantomConfig.ts b/lib/fantom/lib/config/fantomConfig.ts deleted file mode 100644 index ff23bf3f..00000000 --- a/lib/fantom/lib/config/fantomConfig.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as configTypes from "./fantomConfig.interface"; -import * as constants from "../../../constructs/constants"; - -const parseDataVolumeType = (dataVolumeType: string) => { - switch (dataVolumeType) { - case "gp3": - return ec2.EbsDeviceVolumeType.GP3; - case "io2": - return ec2.EbsDeviceVolumeType.IO2; - case "io1": - return ec2.EbsDeviceVolumeType.IO1; - case "instance-store": - return constants.InstanceStoreageDeviceVolumeType; - default: - return ec2.EbsDeviceVolumeType.GP3; - } -}; - -export const baseConfig: configTypes.FantomBaseConfig = { - accountId: process.env.AWS_ACCOUNT_ID || "xxxxxxxxxxx", - region: process.env.AWS_REGION || "us-east-1" -}; - -export const baseNodeConfig: configTypes.FantomBaseNodeConfig = { - instanceType: new ec2.InstanceType(process.env.FANTOM_INSTANCE_TYPE ? process.env.FANTOM_INSTANCE_TYPE : "m6a.2xlarge"), - instanceCpuType: process.env.FANTOM_CPU_TYPE?.toLowerCase() == "x86_64" ? ec2.AmazonLinuxCpuType.X86_64 : ec2.AmazonLinuxCpuType.ARM_64 , - fantomNetwork: process.env.FANTOM_CLUSTER || "mainnet", - nodeConfiguration: process.env.FANTOM_NODE_CONFIGURATION || "read", - snapshotsUrl: process.env.FANTOM_SNAPSHOTS_URL || constants.NoneValue, - dataVolume: { - sizeGiB: process.env.FANTOM_DATA_VOL_SIZE ? parseInt(process.env.FANTOM_DATA_VOL_SIZE) : 2000, - type: parseDataVolumeType(process.env.FANTOM_DATA_VOL_TYPE?.toLowerCase() ? process.env.FANTOM_DATA_VOL_TYPE?.toLowerCase() : "gp3"), - iops: process.env.FANTOM_DATA_VOL_IOPS ? parseInt(process.env.FANTOM_DATA_VOL_IOPS) : 7000, - throughput: process.env.FANTOM_DATA_VOL_THROUGHPUT ? parseInt(process.env.FANTOM_DATA_VOL_THROUGHPUT) : 400 - } -}; - -export const haNodeConfig: configTypes.FantomHAConfig = { - albHealthCheckGracePeriodMin: process.env.FANTOM_HA_ALB_HEALTHCHECK_GRACE_PERIOD_MIN ? parseInt(process.env.FANTOM_HA_ALB_HEALTHCHECK_GRACE_PERIOD_MIN) : 10, - heartBeatDelayMin: process.env.FANTOM_HA_NODES_HEARTBEAT_DELAY_MIN ? parseInt(process.env.FANTOM_HA_NODES_HEARTBEAT_DELAY_MIN) : 40, - numberOfNodes: process.env.FANTOM_HA_NUMBER_OF_NODES ? parseInt(process.env.FANTOM_HA_NUMBER_OF_NODES) : 2 -}; diff --git a/lib/fantom/lib/constructs/fantom-node-security-group.ts b/lib/fantom/lib/constructs/fantom-node-security-group.ts deleted file mode 100644 index c2c879fc..00000000 --- a/lib/fantom/lib/constructs/fantom-node-security-group.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as nag from "cdk-nag"; - -export interface FantomNodeSecurityGroupConstructsProps { - vpc: cdk.aws_ec2.IVpc; -} - -export class FantomNodeSecurityGroupConstructs extends cdkConstructs.Construct { - public securityGroup: cdk.aws_ec2.SecurityGroup; - - constructor(scope: cdkConstructs.Construct, id: string, props: FantomNodeSecurityGroupConstructsProps) { - super(scope, id); - - const { - vpc - } = props; - - const sg = new ec2.SecurityGroup(this, `rpc-node-security-group`, { - vpc, - description: "Security Group for Blockchain nodes", - allowAllOutbound: true - }); - - // public ports - sg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(5050), "P2P"); - sg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.udp(5050), "P2P"); - - // private ports - sg.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(18545), "FANTOM RPC Port"); - sg.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(18546), "FANTOM WebSocket Port"); - - this.securityGroup = sg; - - // cdk-nag suppressions - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-EC23", - reason: "Need to use wildcard for P2P ports" - } - ], - true - ); - } -} diff --git a/lib/fantom/lib/ha-nodes-stack.ts b/lib/fantom/lib/ha-nodes-stack.ts deleted file mode 100644 index 5970fbc0..00000000 --- a/lib/fantom/lib/ha-nodes-stack.ts +++ /dev/null @@ -1,137 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as iam from "aws-cdk-lib/aws-iam"; -import { AmazonLinuxGeneration, AmazonLinuxImage } from "aws-cdk-lib/aws-ec2"; -import * as s3Assets from "aws-cdk-lib/aws-s3-assets"; -import * as configTypes from "./config/fantomConfig.interface"; -import { FantomNodeSecurityGroupConstructs } from "./constructs/fantom-node-security-group"; -import * as fs from "fs"; -import * as path from "path"; -import * as constants from "../../constructs/constants"; -import { HANodesConstruct } from "../../constructs/ha-rpc-nodes-with-alb"; -import * as nag from "cdk-nag"; - -export interface FantomHANodesStackProps extends cdk.StackProps { - nodeRole: configTypes.FantomNodeRole; - instanceType: ec2.InstanceType; - instanceCpuType: ec2.AmazonLinuxCpuType; - fantomNetwork: configTypes.FantomNetwork; - nodeConfiguration: configTypes.FantomNodeConfiguration; - snapshotsUrl: string; - dataVolume: configTypes.FantomDataVolumeConfig; - albHealthCheckGracePeriodMin: number; - heartBeatDelayMin: number; - numberOfNodes: number; -} - -export class FantomHANodesStack extends cdk.Stack { - constructor(scope: cdkConstructs.Construct, id: string, props: FantomHANodesStackProps) { - super(scope, id, props); - - const REGION = cdk.Stack.of(this).region; - const STACK_NAME = cdk.Stack.of(this).stackName; - const lifecycleHookName = STACK_NAME; - const autoScalingGroupName = STACK_NAME; - - const { - nodeRole, - instanceType, - instanceCpuType, - fantomNetwork, - nodeConfiguration, - snapshotsUrl, - dataVolume, - albHealthCheckGracePeriodMin, - heartBeatDelayMin, - numberOfNodes - } = props; - - // using default vpc - const vpc = ec2.Vpc.fromLookup(this, "vpc", { isDefault: true }); - - // setting up the security group for the node from fantom-specific construct - const instanceSG = new FantomNodeSecurityGroupConstructs(this, "security-group", { vpc: vpc }); - - // getting the IAM Role ARM from the common stack - const importedInstanceRoleArn = cdk.Fn.importValue("FantomNodeInstanceRoleArn"); - - const instanceRole = iam.Role.fromRoleArn(this, "iam-role", importedInstanceRoleArn); - - // making our scripts and configs from the local "assets" directory available for instance to download - const asset = new s3Assets.Asset(this, "assets", { - path: path.join(__dirname, "assets") - }); - - asset.bucket.grantRead(instanceRole); - - // parsing user data script and injecting necessary variables - const nodeScript = fs.readFileSync(path.join(__dirname, "assets", "user-data", "node.sh")).toString(); - const dataVolumeSizeBytes = dataVolume.sizeGiB * constants.GibibytesToBytesConversionCoefficient; - - const modifiedInitNodeScript = cdk.Fn.sub(nodeScript, { - _AWS_REGION_: REGION, - _ASSETS_S3_PATH_: `s3://${asset.s3BucketName}/${asset.s3ObjectKey}`, - _STACK_NAME_: STACK_NAME, - _FANTOM_SNAPSHOTS_URI_: snapshotsUrl, - _STACK_ID_: constants.NoneValue, - _NODE_CF_LOGICAL_ID_: constants.NoneValue, - _FANTOM_NODE_TYPE_: nodeConfiguration, - _DATA_VOLUME_TYPE_: dataVolume.type, - _DATA_VOLUME_SIZE_: dataVolumeSizeBytes.toString(), - _NODE_ROLE_: nodeRole, - - _FANTOM_NETWORK_: fantomNetwork, - _LIFECYCLE_HOOK_NAME_: lifecycleHookName, - _AUTOSCALING_GROUP_NAME_: autoScalingGroupName - }); - - // Use Ubuntu 22.04 LTS image for amd64. Find more: https://discourse.ubuntu.com/t/finding-ubuntu-images-with-the-aws-ssm-parameter-store/15507 - const ubuntu2204stableImageSsmName = "/aws/service/canonical/ubuntu/server/22.04/stable/current/amd64/hvm/ebs-gp2/ami-id" - - const rpcNodes = new HANodesConstruct(this, "rpc-nodes", { - instanceType, - dataVolumes: [dataVolume], - rootDataVolumeDeviceName: "/dev/sda1", - machineImage: ec2.MachineImage.fromSsmParameter(ubuntu2204stableImageSsmName), - role: instanceRole, - vpc, - securityGroup: instanceSG.securityGroup, - userData: modifiedInitNodeScript, - numberOfNodes, - rpcPortForALB: 18545, - albHealthCheckGracePeriodMin, - heartBeatDelayMin, - lifecycleHookName: lifecycleHookName, - autoScalingGroupName: autoScalingGroupName - }); - - - - new cdk.CfnOutput(this, "alb-url", { value: rpcNodes.loadBalancerDnsName }); - - // Adding suppressions to the stack - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-AS3", - reason: "No notifications needed" - }, - { - id: "AwsSolutions-S1", - reason: "No access log needed for ALB logs bucket" - }, - { - id: "AwsSolutions-EC28", - reason: "Using basic monitoring to save costs" - }, - { - id: "AwsSolutions-IAM5", - reason: "Need read access to the S3 bucket with assets" - } - ], - true - ); - } -} diff --git a/lib/fantom/lib/single-node-stack.ts b/lib/fantom/lib/single-node-stack.ts deleted file mode 100644 index 67a20ab6..00000000 --- a/lib/fantom/lib/single-node-stack.ts +++ /dev/null @@ -1,141 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as iam from "aws-cdk-lib/aws-iam"; -import * as s3Assets from "aws-cdk-lib/aws-s3-assets"; -import * as path from "path"; -import * as fs from "fs"; -import * as nodeCwDashboard from "./assets/node-cw-dashboard" -import * as cw from 'aws-cdk-lib/aws-cloudwatch'; -import * as constants from "../../constructs/constants"; -import { SingleNodeConstruct } from "../../constructs/single-node" -import * as configTypes from "./config/fantomConfig.interface"; -import { FantomNodeSecurityGroupConstructs } from "./constructs/fantom-node-security-group" -import * as nag from "cdk-nag"; - -export interface FantomSingleNodeStackProps extends cdk.StackProps { - nodeRole: configTypes.FantomNodeRole; - instanceType: ec2.InstanceType; - instanceCpuType: ec2.AmazonLinuxCpuType; - fantomNetwork: configTypes.FantomNetwork; - nodeConfiguration: configTypes.FantomNodeConfiguration; - snapshotsUrl: string; - dataVolume: configTypes.FantomDataVolumeConfig; -} - -export class FantomSingleNodeStack extends cdk.Stack { - constructor(scope: cdkConstructs.Construct, id: string, props: FantomSingleNodeStackProps) { - super(scope, id, props); - - // Setting up necessary environment variables - const REGION = cdk.Stack.of(this).region; - const STACK_NAME = cdk.Stack.of(this).stackName; - const STACK_ID = cdk.Stack.of(this).stackId; - const availabilityZones = cdk.Stack.of(this).availabilityZones; - const chosenAvailabilityZone = availabilityZones.slice(0, 1)[0]; - - // Getting our config from initialization properties - const { - instanceType, - nodeRole, - instanceCpuType, - fantomNetwork, - nodeConfiguration, - snapshotsUrl, - dataVolume, - } = props; - - // Using default VPC - const vpc = ec2.Vpc.fromLookup(this, "vpc", { isDefault: true }); - - // Setting up the security group for the node from Ethereum-specific construct - const instanceSG = new FantomNodeSecurityGroupConstructs (this, "security-group", { - vpc: vpc, - }) - - // Making our scripts and configis from the local "assets" directory available for instance to download - const asset = new s3Assets.Asset(this, "assets", { - path: path.join(__dirname, "assets"), - }); - - // Getting the snapshot bucket name and IAM role ARN from the common stack - const importedInstanceRoleArn = cdk.Fn.importValue("FantomNodeInstanceRoleArn"); - - const instanceRole = iam.Role.fromRoleArn(this, "iam-role", importedInstanceRoleArn); - - // Making sure our instance will be able to read the assets - asset.bucket.grantRead(instanceRole); - - // Use Ubuntu 22.04 LTS image for amd64. Find more: https://discourse.ubuntu.com/t/finding-ubuntu-images-with-the-aws-ssm-parameter-store/15507 - const ubuntu2204stableImageSsmName = "/aws/service/canonical/ubuntu/server/22.04/stable/current/amd64/hvm/ebs-gp2/ami-id" - - // Setting up the node using generic Single Node constract - const node = new SingleNodeConstruct(this, "single-node", { - instanceName: STACK_NAME, - instanceType, - dataVolumes: [dataVolume], - rootDataVolumeDeviceName: "/dev/sda1", - machineImage: ec2.MachineImage.fromSsmParameter(ubuntu2204stableImageSsmName), - vpc, - availabilityZone: chosenAvailabilityZone, - role: instanceRole, - securityGroup: instanceSG.securityGroup, - vpcSubnets: { - subnetType: ec2.SubnetType.PUBLIC, - }, - }); - - // Parsing user data script and injecting necessary variables - const userData = fs.readFileSync(path.join(__dirname, "assets", "user-data", "node.sh")).toString(); - - const dataVolumeSizeBytes = dataVolume.sizeGiB * constants.GibibytesToBytesConversionCoefficient; - - const modifiedUserData = cdk.Fn.sub(userData, { - _AWS_REGION_: REGION, - _ASSETS_S3_PATH_: `s3://${asset.s3BucketName}/${asset.s3ObjectKey}`, - _STACK_NAME_: STACK_NAME, - _FANTOM_SNAPSHOTS_URI_: snapshotsUrl, - _STACK_ID_: STACK_ID, - _NODE_CF_LOGICAL_ID_: node.nodeCFLogicalId, - _FANTOM_NODE_TYPE_: nodeConfiguration, - _DATA_VOLUME_TYPE_: dataVolume.type, - _DATA_VOLUME_SIZE_: dataVolumeSizeBytes.toString(), - _NODE_ROLE_: nodeRole, - - _FANTOM_NETWORK_: fantomNetwork, - _LIFECYCLE_HOOK_NAME_: constants.NoneValue, - _AUTOSCALING_GROUP_NAME_: constants.NoneValue, - }); - - // Adding modified userdata script to the instance prepared fro us by Single Node constract - node.instance.addUserData(modifiedUserData); - - // Adding CloudWatch dashboard to the node - const dashboardString = cdk.Fn.sub(JSON.stringify(nodeCwDashboard.SyncNodeCWDashboardJSON), { - INSTANCE_ID:node.instanceId, - INSTANCE_NAME: STACK_NAME, - REGION: REGION, - }) - - new cw.CfnDashboard(this, 'single-cw-dashboard', { - dashboardName: `${STACK_NAME}-${node.instanceId}`, - dashboardBody: dashboardString, - }); - - new cdk.CfnOutput(this, "single-instance-id", { - value: node.instanceId, - }); - - // Adding suppressions to the stack - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-IAM5", - reason: "Need read and write access to the S3 bucket", - }, - ], - true - ); - } -} diff --git a/lib/fantom/package.json b/lib/fantom/package.json deleted file mode 100644 index 92957cc6..00000000 --- a/lib/fantom/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "aws-blockchain-node-runners-fantom", - "version": "0.1.0", - "bin": { - "scroll": "bin/fantom.js" - }, - "scripts": { - "build": "tsc", - "watch": "tsc -w", - "test": "jest", - "cdk": "cdk", - "cdk_deploy_common": "cdk deploy fantom-common", - "cdk_deploy_ha_nodes": "cdk deploy fantom-ha-nodes", - "cdk_destroy_common": "cdk destroy fantom-common", - "cdk_destroy_ha_nodes": "cdk destroy fantom-ha-nodes" - } -} diff --git a/lib/fantom/sample-configs/.env-sample-read b/lib/fantom/sample-configs/.env-sample-read deleted file mode 100644 index 51eb7195..00000000 --- a/lib/fantom/sample-configs/.env-sample-read +++ /dev/null @@ -1,29 +0,0 @@ -############################################################# -# Example configuration for FANTOM nodes runner app on AWS # -############################################################# - -## Set the AWS account is and region for your environment ## -AWS_ACCOUNT_ID="xxxxxxxxxxx" -AWS_REGION="us-east-1" - -## Optional FANTOM snapshots download link # IMPORTANT !!! Make sure the url is valid before you use it! -FANTOM_SNAPSHOTS_URL=https://snapshot.fantom.network/files/snapsync/latest/listtgzfiles.txt # Optional param. Do a full sync if not provided. - -## Common configuration parameters ## -FANTOM_NETWORK="mainnet" # All options: "mainnet", "testnet" -FANTOM_NODE_CONFIGURATION="read" # All options: "read". Options "api" and "validator" are not yet supported - -## Instance Nodes -FANTOM_INSTANCE_TYPE="i3en.xlarge" -FANTOM_CPU_TYPE="x86_64" # All options: "x86_64", "ARM_64". IMPORTANT: Make sure the CPU type matches the instance type used - -# Data volume configuration -FANTOM_DATA_VOL_TYPE="instance-store" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families -FANTOM_DATA_VOL_SIZE="2000" # Current required data size to keep both snapshot archive and unarchived version of it -FANTOM_DATA_VOL_IOPS="7000" # Max IOPS for EBS volumes (not applicable for "instance-store") -FANTOM_DATA_VOL_THROUGHPUT="400" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") - -## HA nodes configuration ## -FANTOM_HA_NUMBER_OF_NODES="2" # Total number of RPC nodes to be provisioned. Default: 2 -FANTOM_HA_ALB_HEALTHCHECK_GRACE_PERIOD_MIN="10080" # Time enough to initialize the instance -FANTOM_HA_NODES_HEARTBEAT_DELAY_MIN="120" # Time sufficient enough for a node do sync diff --git a/lib/fantom/test/.env-test b/lib/fantom/test/.env-test deleted file mode 100644 index 573b5054..00000000 --- a/lib/fantom/test/.env-test +++ /dev/null @@ -1,29 +0,0 @@ -############################################################# -# Example configuration for FANTOM nodes runner app on AWS # -############################################################# - -## Set the AWS account is and region for your environment ## -AWS_ACCOUNT_ID="xxxxxxxxxxx" -AWS_REGION="ap-southeast-2" - -## Optional FANTOM snapshots download link # IMPORTANT !!! Make sure the url is valid before you use it! -#FANTOM_SNAPSHOTS_URI=https://snapshot.fantom.network/files/snapsync/ # Optional param. We extract the actual URL from https://raw.githubusercontent.com/48Club/fantom-snapshots/main/data.json if nothing is specified. Otherwise, check the latest vesion for Full node here: https://github.com/48Club/fantom-snapshots - -## Common configuration parameters ## -FANTOM_NETWORK="mainnet" # All options: "mainnet", "testnet" -FANTOM_NODE_CONFIGURATION="read" # All options: "read", "api", "validator" - -## Instance Nodes -FANTOM_INSTANCE_TYPE="m6a.2xlarge" -FANTOM_CPU_TYPE="x86_64" # All options: "x86_64", "ARM_64". IMPORTANT: Make sure the CPU type matches the instance type used - -# Data volume configuration -FANTOM_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families -FANTOM_DATA_VOL_SIZE="2000" # Current required data size to keep both snapshot archive and unarchived version of it -FANTOM_DATA_VOL_IOPS="7000" # Max IOPS for EBS volumes (not applicable for "instance-store") -FANTOM_DATA_VOL_THROUGHPUT="400" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") - -## HA nodes configuration ## -FANTOM_HA_NUMBER_OF_NODES="2" # Total number of RPC nodes to be provisioned. Default: 2 -FANTOM_HA_ALB_HEALTHCHECK_GRACE_PERIOD_MIN="300" # Time enough to initialize the instance -FANTOM_HA_NODES_HEARTBEAT_DELAY_MIN="120" # Time sufficient enough for a node do sync diff --git a/lib/fantom/test/common-stack.test.ts b/lib/fantom/test/common-stack.test.ts deleted file mode 100644 index 3c39162c..00000000 --- a/lib/fantom/test/common-stack.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Match, Template } from "aws-cdk-lib/assertions"; -import * as cdk from "aws-cdk-lib"; -import * as dotenv from 'dotenv'; -dotenv.config({ path: './test/.env-test' }); -import * as config from "../lib/config/fantomConfig"; -import { FantomCommonStack } from "../lib/common-stack"; - -describe("FANTOMCommonStack", () => { - test("synthesizes the way we expect", () => { - const app = new cdk.App(); - - // Create the FantomCommonStack. - const fantomCommonStack = new FantomCommonStack(app, "fantom-common", { - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - stackName: `fantom-nodes-common`, - }); - - // Prepare the stack for assertions. - const template = Template.fromStack(fantomCommonStack); - - // Has EC2 instance role. - template.hasResourceProperties("AWS::IAM::Role", { - AssumeRolePolicyDocument: { - Statement: [ - { - Action: "sts:AssumeRole", - Effect: "Allow", - Principal: { - Service: "ec2.amazonaws.com" - } - } - ] - }, - ManagedPolicyArns: [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - Ref: "AWS::Partition" - }, - ":iam::aws:policy/AmazonSSMManagedInstanceCore" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/CloudWatchAgentServerPolicy" - ] - ] - } - ] - }) - - }); -}); diff --git a/lib/fantom/test/ha-nodes-stack.test.ts b/lib/fantom/test/ha-nodes-stack.test.ts deleted file mode 100644 index da218b72..00000000 --- a/lib/fantom/test/ha-nodes-stack.test.ts +++ /dev/null @@ -1,255 +0,0 @@ -import { Match, Template } from "aws-cdk-lib/assertions"; -import * as cdk from "aws-cdk-lib"; -import * as dotenv from 'dotenv'; -dotenv.config({ path: './test/.env-test' }); -import * as config from "../lib/config/fantomConfig"; -import * as configTypes from "../lib/config/fantomConfig.interface"; -import { FantomHANodesStack } from "../lib/ha-nodes-stack"; - -describe("FantomHANodesStack", () => { - test("synthesizes the way we expect", () => { - const app = new cdk.App(); - - // Create the FantomHANodesStack. - const fantomHANodesStack = new FantomHANodesStack(app, "fantom-sync-node", { - stackName: `fantom-ha-nodes-${config.baseNodeConfig.nodeConfiguration}`, - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - nodeRole: "rpc-node", - - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - fantomNetwork: config.baseNodeConfig.fantomNetwork, - nodeConfiguration: config.baseNodeConfig.nodeConfiguration, - snapshotsUrl:config.baseNodeConfig.snapshotsUrl, - dataVolume: config.baseNodeConfig.dataVolume, - - albHealthCheckGracePeriodMin: config.haNodeConfig.albHealthCheckGracePeriodMin, - heartBeatDelayMin: config.haNodeConfig.heartBeatDelayMin, - numberOfNodes: config.haNodeConfig.numberOfNodes, - }); - - // Prepare the stack for assertions. - const template = Template.fromStack(fantomHANodesStack); - - // Has EC2 instance security group. - template.hasResourceProperties("AWS::EC2::SecurityGroup", { - GroupDescription: Match.anyValue(), - VpcId: Match.anyValue(), - SecurityGroupEgress: [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" - } - ], - SecurityGroupIngress: [ - { - "CidrIp": "0.0.0.0/0", - "Description": "P2P", - "FromPort": 5050, - "IpProtocol": "tcp", - "ToPort": 5050 - }, - { - "CidrIp": "0.0.0.0/0", - "Description": "P2P", - "FromPort": 5050, - "IpProtocol": "udp", - "ToPort": 5050 - }, - { - "CidrIp": "1.2.3.4/5", - "Description": "FANTOM RPC Port", - "FromPort": 18545, - "IpProtocol": "tcp", - "ToPort": 18545 - }, - { - "CidrIp": "1.2.3.4/5", - "Description": "FANTOM WebSocket Port", - "FromPort": 18546, - "IpProtocol": "tcp", - "ToPort": 18546 - }, - { - "Description": "Allow access from ALB to Blockchain Node", - "FromPort": 0, - "IpProtocol": "tcp", - "SourceSecurityGroupId": { - "Fn::GetAtt": [ - Match.anyValue(), - "GroupId" - ] - }, - "ToPort": 65535 - }, - ] - }) - - // Has security group from ALB to EC2. - template.hasResourceProperties("AWS::EC2::SecurityGroupIngress", { - Description: "Load balancer to target", - FromPort: 18545, - GroupId: Match.anyValue(), - IpProtocol: "tcp", - SourceSecurityGroupId: Match.anyValue(), - ToPort: 18545, - }) - - // Has launch template profile for EC2 instances. - template.hasResourceProperties("AWS::IAM::InstanceProfile", { - Roles: [Match.anyValue()] - }); - - // Has EC2 launch template. - template.hasResourceProperties("AWS::EC2::LaunchTemplate", { - LaunchTemplateData: { - BlockDeviceMappings: [ - { - "DeviceName": "/dev/sda1", - "Ebs": { - "DeleteOnTermination": true, - "Encrypted": true, - "Iops": 3000, - "Throughput": 125, - "VolumeSize": 46, - "VolumeType": "gp3" - } - }, - { - "DeviceName": "/dev/sdf", - "Ebs": { - "DeleteOnTermination": true, - "Encrypted": true, - "Iops": 7000, - "Throughput": 400, - "VolumeSize": 2000, - "VolumeType": "gp3" - } - } - ], - EbsOptimized: true, - IamInstanceProfile: Match.anyValue(), - ImageId: Match.anyValue(), - InstanceType:"m6a.2xlarge", - SecurityGroupIds: [Match.anyValue()], - UserData: Match.anyValue(), - TagSpecifications: Match.anyValue(), - } - }) - - // Has Auto Scaling Group. - template.hasResourceProperties("AWS::AutoScaling::AutoScalingGroup", { - AutoScalingGroupName: `fantom-ha-nodes-${config.baseNodeConfig.nodeConfiguration}`, - HealthCheckGracePeriod: config.haNodeConfig.albHealthCheckGracePeriodMin * 60, - HealthCheckType: "ELB", - DefaultInstanceWarmup: 60, - MinSize: "0", - MaxSize: "4", - DesiredCapacity: config.haNodeConfig.numberOfNodes.toString(), - VPCZoneIdentifier: Match.anyValue(), - TargetGroupARNs: Match.anyValue(), - }); - - // Has Auto Scaling Lifecycle Hook. - template.hasResourceProperties("AWS::AutoScaling::LifecycleHook", { - DefaultResult: "ABANDON", - HeartbeatTimeout: config.haNodeConfig.heartBeatDelayMin * 60, - LifecycleHookName: `fantom-ha-nodes-${config.baseNodeConfig.nodeConfiguration}`, - LifecycleTransition: "autoscaling:EC2_INSTANCE_LAUNCHING", - }); - - // Has Auto Scaling Security Group. - template.hasResourceProperties("AWS::EC2::SecurityGroup", { - GroupDescription: Match.anyValue(), - SecurityGroupEgress: [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" - } - ], - SecurityGroupIngress: [ - { - "CidrIp": "1.2.3.4/5", - "Description": "Blockchain Node RPC", - "FromPort": 8545, - "IpProtocol": "tcp", - "ToPort": 8545 - } - ], - VpcId: Match.anyValue(), - }); - - // Has ALB. - template.hasResourceProperties("AWS::ElasticLoadBalancingV2::LoadBalancer", { - LoadBalancerAttributes: [ - { - Key: "deletion_protection.enabled", - Value: "false" - }, - { - Key: "access_logs.s3.enabled", - Value: "true" - }, - { - Key: "access_logs.s3.bucket", - Value: Match.anyValue(), - }, - { - Key: "access_logs.s3.prefix", - Value: `fantom-ha-nodes-${config.baseNodeConfig.nodeConfiguration}` - } - ], - Scheme: "internal", - SecurityGroups: [ - Match.anyValue() - ], - "Subnets": [ - Match.anyValue(), - Match.anyValue() - ], - Type: "application", - }); - - // Has ALB listener. - template.hasResourceProperties("AWS::ElasticLoadBalancingV2::Listener", { - "DefaultActions": [ - { - "TargetGroupArn": Match.anyValue(), - Type: "forward" - } - ], - LoadBalancerArn: Match.anyValue(), - Port: 8545, - Protocol: "HTTP" - }) - - // Has ALB target group. - template.hasResourceProperties("AWS::ElasticLoadBalancingV2::TargetGroup", { - HealthCheckEnabled: true, - HealthCheckIntervalSeconds: 30, - HealthCheckPath: "/", - HealthCheckPort: "8545", - HealthyThresholdCount: 3, - Matcher: { - HttpCode: "200-299" - }, - Port: 8545, - Protocol: "HTTP", - TargetGroupAttributes: [ - { - Key: "deregistration_delay.timeout_seconds", - Value: "30" - }, - { - Key: "stickiness.enabled", - Value: "false" - } - ], - TargetType: "instance", - UnhealthyThresholdCount: 2, - VpcId: Match.anyValue(), - }) - }); -}); diff --git a/lib/fantom/test/single-node-stack.test.ts b/lib/fantom/test/single-node-stack.test.ts deleted file mode 100644 index 159e811a..00000000 --- a/lib/fantom/test/single-node-stack.test.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { Match, Template } from "aws-cdk-lib/assertions"; -import * as cdk from "aws-cdk-lib"; -import * as dotenv from 'dotenv'; -dotenv.config({ path: './test/.env-test' }); -import * as config from "../lib/config/fantomConfig"; -import * as configTypes from "../lib/config/fantomConfig.interface"; -import { FantomSingleNodeStack } from "../lib/single-node-stack"; - -describe("FANTOMSingleNodeStack", () => { - test("synthesizes the way we expect", () => { - const app = new cdk.App(); - - // Create the EthSingleNodeStack. - const fantomSingleNodeStack = new FantomSingleNodeStack(app, "fantom-single-node", { - stackName: `fantom-single-node`, - - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - nodeRole: "single-node", - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - fantomNetwork: config.baseNodeConfig.fantomNetwork, - nodeConfiguration: config.baseNodeConfig.nodeConfiguration, - snapshotsUrl:config.baseNodeConfig.snapshotsUrl, - dataVolume: config.baseNodeConfig.dataVolume, - }); - - // Prepare the stack for assertions. - const template = Template.fromStack(fantomSingleNodeStack); - - // Has EC2 instance security group. - template.hasResourceProperties("AWS::EC2::SecurityGroup", { - GroupDescription: Match.anyValue(), - VpcId: Match.anyValue(), - SecurityGroupEgress: [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" - } - ], - SecurityGroupIngress: [ - { - "CidrIp": "0.0.0.0/0", - "Description": "P2P", - "FromPort": 5050, - "IpProtocol": "tcp", - "ToPort": 5050 - }, - { - "CidrIp": "0.0.0.0/0", - "Description": "P2P", - "FromPort": 5050, - "IpProtocol": "udp", - "ToPort": 5050 - }, - { - "CidrIp": "1.2.3.4/5", - "Description": "FANTOM RPC Port", - "FromPort": 18545, - "IpProtocol": "tcp", - "ToPort": 18545 - }, - { - "CidrIp": "1.2.3.4/5", - "Description": "FANTOM WebSocket Port", - "FromPort": 18546, - "IpProtocol": "tcp", - "ToPort": 18546 - } - ] - }) - - // Has EC2 instance with node configuration - template.hasResourceProperties("AWS::EC2::Instance", { - AvailabilityZone: Match.anyValue(), - UserData: Match.anyValue(), - BlockDeviceMappings: [ - { - DeviceName: "/dev/sda1", - Ebs: { - DeleteOnTermination: true, - Encrypted: true, - Iops: 3000, - VolumeSize: 46, - VolumeType: "gp3" - } - } - ], - IamInstanceProfile: Match.anyValue(), - ImageId: Match.anyValue(), - InstanceType: "m6a.2xlarge", - Monitoring: true, - PropagateTagsToVolumeOnCreation: true, - SecurityGroupIds: Match.anyValue(), - SubnetId: Match.anyValue(), - }) - - // Has EBS data volume. - template.hasResourceProperties("AWS::EC2::Volume", { - AvailabilityZone: Match.anyValue(), - Encrypted: true, - Iops: 7000, - MultiAttachEnabled: false, - Size: 2000, - Throughput: 400, - VolumeType: "gp3" - }) - - // Has EBS data volume attachment. - template.hasResourceProperties("AWS::EC2::VolumeAttachment", { - Device: "/dev/sdf", - InstanceId: Match.anyValue(), - VolumeId: Match.anyValue(), - }) - - // Has CloudWatch dashboard. - template.hasResourceProperties("AWS::CloudWatch::Dashboard", { - DashboardBody: Match.anyValue(), - DashboardName: { - "Fn::Join": ["", ["fantom-single-node-",{ "Ref": Match.anyValue() }]] - } - }) - - }); -}); diff --git a/lib/fantom/tsconfig.json b/lib/fantom/tsconfig.json deleted file mode 100644 index 8e1979f3..00000000 --- a/lib/fantom/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": [ - "es2020", - "dom" - ], - "declaration": true, - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": false, - "noUnusedParameters": false, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": false, - "inlineSourceMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictPropertyInitialization": false, - "typeRoots": [ - "../../node_modules/@types" - ] - }, - "exclude": [ - "node_modules", - "cdk.out" - ] -} diff --git a/lib/scroll/package-lock.json b/lib/scroll/package-lock.json new file mode 100644 index 00000000..06e792be --- /dev/null +++ b/lib/scroll/package-lock.json @@ -0,0 +1,1137 @@ +{ + "name": "aws-blockchain-node-runners-scroll", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "aws-blockchain-node-runners-scroll", + "version": "0.1.0", + "dependencies": { + "@types/node": "^20.10.0" + }, + "bin": { + "scroll": "bin/scroll.js" + }, + "devDependencies": { + "@types/jest": "^29.5.11" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.0.tgz", + "integrity": "sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@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" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@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": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@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" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true + }, + "@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3" + } + }, + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "dev": true, + "requires": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "@types/node": { + "version": "20.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.0.tgz", + "integrity": "sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==", + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + }, + "expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "requires": { + "@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" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + } + }, + "jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true + }, + "jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + } + }, + "jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@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": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@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" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + } + } +} diff --git a/lib/scroll/package.json b/lib/scroll/package.json index 0de5200b..9bf0ba0c 100644 --- a/lib/scroll/package.json +++ b/lib/scroll/package.json @@ -13,5 +13,11 @@ "cdk_synth_single_node": "cdk synth scroll-single-node", "cdk_deploy_single_node": "cdk deploy scroll-single-node", "cdk_destroy_single_node": "cdk destroy scroll-single-node" + }, + "dependencies": { + "@types/node": "^20.10.0" + }, + "devDependencies": { + "@types/jest": "^29.5.11" } } diff --git a/lib/scroll/test/scroll-ethereum-l1-node.test.ts b/lib/scroll/test/scroll-ethereum-l1-node.test.ts index 4dc328ee..a8b2b9b7 100644 --- a/lib/scroll/test/scroll-ethereum-l1-node.test.ts +++ b/lib/scroll/test/scroll-ethereum-l1-node.test.ts @@ -39,7 +39,7 @@ describe("ScrollAMBEthereumSingleNodeStack", () => { "NodeId" ] }, - ".t.ethereum.managedblockchain.us-east-1.amazonaws.com/?billingtoken=", + ".t.ethereum.managedblockchain.us-east-1.amazonaws.com?billingtoken=", { "Fn::GetAtt": [ Match.anyValue(), diff --git a/lib/scroll/tsconfig.json b/lib/scroll/tsconfig.json index 8e1979f3..aaa7dc51 100644 --- a/lib/scroll/tsconfig.json +++ b/lib/scroll/tsconfig.json @@ -21,7 +21,7 @@ "experimentalDecorators": true, "strictPropertyInitialization": false, "typeRoots": [ - "../../node_modules/@types" + "./node_modules/@types" ] }, "exclude": [ diff --git a/lib/solana/package.json b/lib/solana/package.json index ceeebb54..4c54543f 100644 --- a/lib/solana/package.json +++ b/lib/solana/package.json @@ -7,5 +7,11 @@ "test": "npx jest", "cdk": "npx cdk", "scan-cdk": "npx cdk synth" + }, + "dependencies": { + "@types/node": "^20.10.0" + }, + "devDependencies": { + "@types/jest": "^29.5.11" } } diff --git a/lib/solana/test/single-node-stack.test.ts b/lib/solana/test/single-node-stack.test.ts index 27dbae03..10afaeeb 100644 --- a/lib/solana/test/single-node-stack.test.ts +++ b/lib/solana/test/single-node-stack.test.ts @@ -137,7 +137,17 @@ describe("SolanaSingleNodeStack", () => { // Has CloudWatch dashboard. template.hasResourceProperties("AWS::CloudWatch::Dashboard", { DashboardBody: Match.anyValue(), - DashboardName: {"Fn::Join": ["", ["solana-single-node-baserpc-",{ "Ref": Match.anyValue() }]]} + DashboardName: { + "Fn::Join": [ + "", + [ + "solana-single-node-baserpc-", + { + "Ref": "syncnodesinglenode" + } + ] + ] + } }) }); diff --git a/lib/stacks/package-lock.json b/lib/stacks/package-lock.json new file mode 100644 index 00000000..5d421062 --- /dev/null +++ b/lib/stacks/package-lock.json @@ -0,0 +1,641 @@ +{ + "name": "aws-blockchain-node-runners-stacks", + "version": "0.2.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "aws-blockchain-node-runners-stacks", + "version": "0.2.0", + "devDependencies": { + "@types/jest": "^29.5.11", + "@types/node": "^20.11.17" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", + "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@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" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@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": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@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" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + } + } +} diff --git a/lib/stacks/package.json b/lib/stacks/package.json index 657d5bb1..8b0eb6c1 100644 --- a/lib/stacks/package.json +++ b/lib/stacks/package.json @@ -7,5 +7,9 @@ "test": "npx jest", "cdk": "npx cdk", "scan-cdk": "npx cdk synth" + }, + "devDependencies": { + "@types/jest": "^29.5.11", + "@types/node": "^20.11.17" } } diff --git a/lib/starknet/.gitignore b/lib/starknet/.gitignore deleted file mode 100644 index 0304fefb..00000000 --- a/lib/starknet/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -*.js -!jest.config.js -*.d.ts -node_modules - -# CDK asset staging directory -.cdk.staging -cdk.out -.idea - -*-node.json diff --git a/lib/starknet/.npmignore b/lib/starknet/.npmignore deleted file mode 100644 index 0304fefb..00000000 --- a/lib/starknet/.npmignore +++ /dev/null @@ -1,11 +0,0 @@ -*.js -!jest.config.js -*.d.ts -node_modules - -# CDK asset staging directory -.cdk.staging -cdk.out -.idea - -*-node.json diff --git a/lib/starknet/README.md b/lib/starknet/README.md deleted file mode 100644 index d446ebd0..00000000 --- a/lib/starknet/README.md +++ /dev/null @@ -1,236 +0,0 @@ -# Sample AWS Blockchain Node Runner app for Starknet Nodes - -| Contributed by | -|:--------------------:| -| [@wojciechos](https://github.com/wojciechos) | - -[Starknet](https://docs.starknet.io/documentation/) is a "Layer 2" scaling solution for Ethereum leveraging zero knowledge proofs. This blueprint helps to deploy Starknet nodes (Juno) on AWS as RPC nodes. It is meant to be used for development, testing or Proof of Concept purposes. - -## Overview of Deployment Architectures for Single Node setups - -### Single node setup - -![Single Node Deployment](./doc/assets/Architecture-SingleNode.png) - -1. A Starknet node deployed in the [Default VPC](https://docs.aws.amazon.com/vpc/latest/userguide/default-vpc.html) continuously synchronizes with the [Sequencer](https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/starknet_architecture_overview/) through [Internet Gateway](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html). -2. The Starknet node is used by dApps or development tools internally from within the Default VPC. JSON RPC API is not exposed to the Internet directly to protect nodes from unauthorized access. -3. You will need access to a fully-synced Ethereum RPC endpoint before running Juno. -4. The Starknet node sends various monitoring metrics for both EC2 and Starknet nodes to Amazon CloudWatch. - -## Additional Materials - -

- -Well-Architected Checklist - -This is the Well-Architected checklist for Stacks nodes implementation of the AWS Blockchain Node Runner app. This checklist takes into account questions from the [AWS Well-Architected Framework](https://aws.amazon.com/architecture/well-architected/) which are relevant to this workload. Please feel free to add more checks from the framework if required for your workload. - -| Pillar | Control | Question/Check | Remarks | -|:------------------------|:----------------------------------|:---------------------------------------------------------------------------------|:-----------------| -| Security | Network protection | Are there unnecessary open ports in security groups? | There are no ports open to public. RPC port 6060 is open only IP addresses from the same VPC. | -| | | Traffic inspection | AWS WAF could be implemented for traffic inspection. Additional charges will apply. | -| | Compute protection | Reduce attack surface | This solution uses Ubuntu Server 20.04 AMI. You may choose to run hardening scripts on it. | -| | | Enable people to perform actions at a distance | This solution uses AWS Systems Manager for terminal session, not ssh ports. | -| | Data protection at rest | Use encrypted Amazon Elastic Block Store (Amazon EBS) volumes | This solution uses encrypted Amazon EBS volumes. | -| | | Use encrypted Amazon Simple Storage Service (Amazon S3) buckets | This solution uses Amazon S3 managed keys (SSE-S3) encryption. | -| | Data protection in transit | Use TLS | TLS is not used in this solution. Port 6060 is the only open port, but you may create HTTPS listener with self signed certificate if TLS is desired. | -| | Authorization and access control | Use instance profile with Amazon Elastic Compute Cloud (Amazon EC2) instances | This solution uses AWS Identity and Access Management (AWS IAM) role instead of IAM user. | -| | | Following principle of least privilege access | In all node types, root user is not used (using special user "ubuntu" instead). | -| | Application security | Security focused development practices | cdk-nag is being used with appropriate suppressions. | -| Cost optimization | Service selection | Use cost effective resources | 1. AMD-based instances are used for Consensus and RPC node to save the costs. Consider compiling Graviton-based binaries to improve costs for compute.
2. Cost-effective EBS gp3 are preferred instead of io2. | -| | Cost awareness | Estimate costs | Single RPC node with `m6a.2xlarge` EBS gp3 volume about 600 GB with On-Demand pricing will cost around US$323.29 per month in the US East (N. Virginia) region not including network requests for follower nodes. More analysis needed. | -| Reliability | Resiliency implementation | Withstand component failures | This solution ues only for a single-node deployment. If the running node failed, you will need to undeploy the existing stack and re-deploy the node again. | -| | Data backup | How is data backed up? | Considering blockchain data is replicated by nodes automatically and Starknet nodes sync from start within an hour and a half, we don't use any additional mechanisms to backup the data. | -| | Resource monitoring | How are workload resources monitored? | Resources are being monitored using Amazon CloudWatch dashboards. Amazon CloudWatch custom metrics are being pushed via CloudWatch Agent. | -| Performance efficiency | Compute selection | How is compute solution selected? | Compute solution is selected based on best price-performance, i.e. AWS AMD-based Amazon EC2 instances. | -| | Storage selection | How is storage solution selected? | Storage solution is selected based on best price-performance, i.e. gp3 Amazon EBS volumes with optimal IOPS and throughput. | -| | Architecture selection | How is the best performance architecture selected? | We used a combination of recommendations from the Starknet community. | -| Operational excellence | Workload health | How is health of workload determined? | We rely on metrics reported to CloudWatch by `/opt/syncchecker.sh` script. | -| Sustainability | Hardware & services | Select most efficient hardware for your workload | The solution uses AMD-powered instances. There is a potential to use AWS Graviton-based Amazon EC2 instances which offer the best performance per watt of energy use in Amazon EC2. | -
- -### Hardware Requirements - -**Minimum for Starknet node** - -- Instance type [m6a.large](https://aws.amazon.com/ec2/instance-types/m6a/). -- 250GB EBS gp3 storage with at least 3000 IOPS. - -**Recommended for Starknet node** - -- Instance type [m6a.2xlarge](https://aws.amazon.com/ec2/instance-types/m6a/). -- 600GB EBS gp3 storage with at least 3000 IOPS to store and upzip snapshots. - -## Setup Instructions - -### Setup Cloud9 - -We will use AWS Cloud9 to execute the subsequent commands. Follow the instructions in [Cloud9 Setup](../../docs/setup-cloud9.md) - -### Clone this repository and install dependencies - -```bash - git clone https://github.com/aws-samples/aws-blockchain-node-runners.git - cd aws-blockchain-node-runners - npm install -``` - -### Deploy Single Node - -1. Make sure you are in the root directory of the cloned repository - -2. If you have deleted or don't have the default VPC, create default VPC - - ```bash - aws ec2 create-default-vpc - ``` - - > NOTE: - > You may see the following error if the default VPC already exists: `An error occurred (DefaultVpcAlreadyExists) when calling the CreateDefaultVpc operation: A Default VPC already exists for this account in this region.`. That means you can just continue with the following steps. - -3. Configure your setup - - Create your own copy of `.env` file and edit it to update with your AWS Account ID and Region: - ```bash - # Make sure you are in aws-blockchain-node-runners/lib/starknet - cd lib/starknet - npm install - pwd - cp ./sample-configs/.env-sample-full .env - nano .env - ``` - > NOTE: - > Example configuration parameters are set in the local `.env-sample` file. You can find more examples inside `sample-configs` directory. - -4. Deploy common components such as IAM role - - ```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/starknet - npx cdk deploy starknet-common - ``` - - > IMPORTANT: - > All AWS CDK v2 deployments use dedicated AWS resources to hold data during deployment. Therefore, your AWS account and Region must be [bootstrapped](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html) to create these resources before you can deploy. If you haven't already bootstrapped, issue the following command: - > ```bash - > cdk bootstrap aws://ACCOUNT-NUMBER/REGION - > ``` - -5. [OPTIONAL] You can use Amazon Managed Blockchain (AMB) Access Ethereum node as L1 node. To do that, leave `STARKNET_L1_ENDPOINT` URL empty and, deploy Amazon Managed Blockchain (AMB) Access Ethereum node. Wait about 35-70 minutes for the node to sync. - - ```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/starknet - npx cdk deploy starknet-ethereum-l1-node --json --outputs-file starknet-ethereum-l1-node.json - ``` - To watch the progress, open the [AMB Web UI](https://console.aws.amazon.com/managedblockchain/home), click the name of your target network from the list (Mainnet, Goerly, etc.) and watch the status of the node to change from `Creating` to `Available`. - -6. Deploy Starknet Full Node - - ```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/starknet - npx cdk deploy starknet-single-node --json --outputs-file single-node-deploy.json - ``` - After starting the node you will need to wait for the initial synchronization process to finish. When using snapshot, the node should become available within a couple of hours, but migh take about 3-4 days to sync it from block 0. To check the progress, you may use SSM to connect into EC2 first and watch the log like this: - - ```bash - export INSTANCE_ID=$(cat single-node-deploy.json | jq -r '..|.nodeinstanceid? | select(. != null)') - echo "INSTANCE_ID="$INSTANCE_ID - export AWS_REGION=us-east-1 - aws ssm start-session --target $INSTANCE_ID --region $AWS_REGION - tail -f /var/log/starknet/error.log - ``` - - -7. Test Starknet RPC API - Use curl to query from within the node instance: - ```bash - export INSTANCE_ID=$(cat single-node-deploy.json | jq -r '..|.node-instance-id? | select(. != null)') - echo "INSTANCE_ID=" $INSTANCE_ID - export AWS_REGION=us-east-1 - aws ssm start-session --target $INSTANCE_ID --region $AWS_REGION - - curl --location 'http://localhost:6060' \ - --header 'Content-Type: application/json' \ - --data '{ - "jsonrpc":"2.0", - "method":"starknet_chainId", - "params":[], - "id":1 - }' - ``` - -### Monitoring -A script on the Starknet node publishes current block and blocks behind metrics to CloudWatch metrics every 5 minutes. When the node is fully synced the blocks behind metric should get to 0.To see the metrics: - -- Navigate to CloudWatch service (make sure you are in the region you have specified for AWS_REGION) -- Open Dashboards and select `starknet-single-node-` from the list of dashboards. - -## Clear up and undeploy everything - -1. Undeploy all Nodes and Common stacks - - ```bash - # Setting the AWS account id and region in case local .env file is lost - export AWS_ACCOUNT_ID= - export AWS_REGION= - - pwd - # Make sure you are in aws-blockchain-node-runners/lib/starknet - - # Undeploy Single Node - npx cdk destroy starknet-single-node - - # Undeploy AMB Etheruem node - npx cdk destroy starknet-ethereum-l1-node - - # Delete all common components like IAM role and Security Group - npx cdk destroy starknet-common - ``` - -2. Follow steps to delete the Cloud9 instance in [Cloud9 Setup](../../doc/setup-cloud9.md) - -## FAQ - -1. How to check the logs of the clients running on my Starknet node? - - **Note:** In this tutorial we chose not to use SSH and use Session Manager instead. That allows you to log all sessions in AWS CloudTrail to see who logged into the server and when. If you receive an error similar to `SessionManagerPlugin is not found`, [install Session Manager plugin for AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) - - ```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/starknet - - export INSTANCE_ID=$(cat single-node-deploy.json | jq -r '..|.nodeinstanceid? | select(. != null)') - echo "INSTANCE_ID="$INSTANCE_ID - export AWS_REGION=us-east-1 - aws ssm start-session --target $INSTANCE_ID --region $AWS_REGION - tail -f /var/log/starknet/error.log - ``` -2. How to check the logs from the EC2 user-data script? - - ```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/starknet - - export INSTANCE_ID=$(cat single-node-deploy.json | jq -r '..|.nodeinstanceid? | select(. != null)') - echo "INSTANCE_ID=" $INSTANCE_ID - export AWS_REGION=us-east-1 - aws ssm start-session --target $INSTANCE_ID --region $AWS_REGION - sudo cat /var/log/cloud-init-output.log - ``` - -3. How can I restart the Starknet service? - - ``` bash - export INSTANCE_ID=$(cat single-node-deploy.json | jq -r '..|.nodeinstanceid? | select(. != null)') - echo "INSTANCE_ID=" $INSTANCE_ID - export AWS_REGION=us-east-1 - aws ssm start-session --target $INSTANCE_ID --region $AWS_REGION - sudo systemctl status starknet.service - sudo systemctl restart starknet.service - ``` -4. Where to find the key juno directories? - - - The directory with binaries is `/home/ubuntu/juno-source`. - - The data directory of juno agent is `/data` diff --git a/lib/starknet/app.ts b/lib/starknet/app.ts deleted file mode 100644 index da551a1d..00000000 --- a/lib/starknet/app.ts +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env node -import 'dotenv/config' -import 'source-map-support/register'; -import * as cdk from 'aws-cdk-lib'; -import * as nag from "cdk-nag"; -import * as config from "./lib/config/starknetConfig"; -import {StarknetCommonStack} from "./lib/common-stack"; -import {StarknetAMBEthereumSingleNodeStack} from "./lib/amb-ethereum-single-node-stack"; -import {StarknetSingleNodeStack} from "./lib/single-node-stack"; - -const app = new cdk.App(); -cdk.Tags.of(app).add("Project", "AWSStarknet"); - -new StarknetCommonStack(app, "starknet-common", { - stackName: `starknet-nodes-common`, - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, -}); - -new StarknetAMBEthereumSingleNodeStack(app, "starknet-ethereum-l1-node", { - stackName: `starknet-amb-ethereum-single-node-${config.baseNodeConfig.ambEntereumNodeNetworkId}`, - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - - ambEthereumNodeNetworkId: config.baseNodeConfig.ambEntereumNodeNetworkId, - ambEthereumNodeInstanceType: config.baseNodeConfig.ambEntereumNodeInstanceType, -}); - -new StarknetSingleNodeStack(app, "starknet-single-node", { - stackName: `starknet-single-node-${config.baseNodeConfig.starknetNetworkId}`, - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - dataVolume: config.baseNodeConfig.dataVolume, - starknetNetworkId: config.baseNodeConfig.starknetNetworkId, - starknetL1Endpoint: config.baseNodeConfig.starknetL1Endpoint, - starknetNodeVersion: config.baseNodeConfig.starknetNodeVersion, - snapshotUrl: config.baseNodeConfig.snapshotUrl -}); - -// Security Check -cdk.Aspects.of(app).add( - new nag.AwsSolutionsChecks({ - verbose: false, - reports: true, - logIgnores: false, - }) -); diff --git a/lib/starknet/cdk.json b/lib/starknet/cdk.json deleted file mode 100644 index 7714e8c2..00000000 --- a/lib/starknet/cdk.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "app": "npx ts-node --prefer-ts-exts app.ts", - "watch": { - "include": [ - "**" - ], - "exclude": [ - "README.md", - "cdk*.json", - "**/*.d.ts", - "**/*.js", - "tsconfig.json", - "package*.json", - "yarn.lock", - "node_modules", - "test" - ] - }, - "context": { - "@aws-cdk/aws-lambda:recognizeLayerVersion": true, - "@aws-cdk/core:checkSecretUsage": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ], - "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, - "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, - "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, - "@aws-cdk/aws-iam:minimizePolicies": true, - "@aws-cdk/core:validateSnapshotRemovalPolicy": true, - "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, - "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, - "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, - "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, - "@aws-cdk/core:enablePartitionLiterals": true, - "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, - "@aws-cdk/aws-iam:standardizedServicePrincipals": true, - "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, - "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, - "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, - "@aws-cdk/aws-route53-patters:useCertificate": true, - "@aws-cdk/customresources:installLatestAwsSdkDefault": false, - "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, - "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, - "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, - "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, - "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, - "@aws-cdk/aws-redshift:columnId": true, - "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, - "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, - "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, - "@aws-cdk/aws-kms:aliasNameRef": true, - "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, - "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, - "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true - } -} diff --git a/lib/starknet/doc/assets/Architecture-SingleNode.drawio b/lib/starknet/doc/assets/Architecture-SingleNode.drawio deleted file mode 100644 index 0f573ab9..00000000 --- a/lib/starknet/doc/assets/Architecture-SingleNode.drawio +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/starknet/doc/assets/Architecture-SingleNode.png b/lib/starknet/doc/assets/Architecture-SingleNode.png deleted file mode 100644 index 5b5549a5494e950c0a64e2b0afd73c63712d884c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38696 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 diff --git a/lib/starknet/jest.config.js b/lib/starknet/jest.config.js deleted file mode 100644 index 08263b89..00000000 --- a/lib/starknet/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - testEnvironment: 'node', - roots: ['/test'], - testMatch: ['**/*.test.ts'], - transform: { - '^.+\\.tsx?$': 'ts-jest' - } -}; diff --git a/lib/starknet/lib/amb-ethereum-single-node-stack.ts b/lib/starknet/lib/amb-ethereum-single-node-stack.ts deleted file mode 100644 index 36f671c1..00000000 --- a/lib/starknet/lib/amb-ethereum-single-node-stack.ts +++ /dev/null @@ -1,60 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as nag from "cdk-nag"; -import * as configTypes from "./config/starknetConfig.interface"; -import { SingleNodeAMBEthereumConstruct } from "../../constructs/amb-ethereum-single-node"; - -export interface ScrollAMBEthereumSingleNodeStackProps extends cdk.StackProps { - ambEthereumNodeNetworkId: configTypes.AMBEthereumNodeNetworkId, - ambEthereumNodeInstanceType: string, -} - -export class StarknetAMBEthereumSingleNodeStack extends cdk.Stack { - constructor(scope: cdkConstructs.Construct, id: string, props: ScrollAMBEthereumSingleNodeStackProps) { - super(scope, id, props); - - // Setting up necessary environment variables - const availabilityZones = cdk.Stack.of(this).availabilityZones; - const chosenAvailabilityZone = availabilityZones.slice(0, 1)[0]; - - // Getting our config from initialization properties - const { - ambEthereumNodeNetworkId, - ambEthereumNodeInstanceType, - } = props; - - // Setting up L1 Ethereum node with AMB Ethereum node construct - - const ambEthereumNode = new SingleNodeAMBEthereumConstruct(this, "amb-ethereum-l1-single-node", { - instanceType: ambEthereumNodeInstanceType, - availabilityZone: chosenAvailabilityZone, - ethNetworkId: ambEthereumNodeNetworkId, - }) - - new cdk.CfnOutput(this, "amb-eth-node-id", { - value: ambEthereumNode.nodeId, - exportName: "AmbEthereumNodeId" - }); - - new cdk.CfnOutput(this, "amb-eth-node-rpc-url-billing-token", { - value: ambEthereumNode.wssRpcUrlWithBillingToken, - exportName: "AmbEthereumNodeRpcUrlWithBillingToken", - }); - - // Adding suppressions to the stack - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-IAM5", - reason: "Need to create custom resources to Create and Delete AMB node and accessor and IAM policy to support a generic case for AMB resources", - }, - { - id: "AwsSolutions-IAM4", - reason: "Need to create custom resources to Create and Delete AMB node and accessor and IAM to support ti", - }, - ], - true - ); - } -} diff --git a/lib/starknet/lib/assets/cw-agent.json b/lib/starknet/lib/assets/cw-agent.json deleted file mode 100644 index 28833017..00000000 --- a/lib/starknet/lib/assets/cw-agent.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "agent": { - "metrics_collection_interval": 60, - "run_as_user": "root" - }, - "metrics": { - "aggregation_dimensions": [ - [ - "InstanceId" - ] - ], - "append_dimensions": { - "InstanceId": "${aws:InstanceId}" - }, - "metrics_collected": { - "cpu": { - "measurement": [ - "cpu_usage_idle", - "cpu_usage_iowait", - "cpu_usage_user", - "cpu_usage_system" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ], - "totalcpu": false - }, - "disk": { - "measurement": [ - "used_percent" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ] - }, - "diskio": { - "measurement": [ - "io_time", - "write_bytes", - "read_bytes", - "writes", - "reads", - "write_time", - "read_time", - "iops_in_progress" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ] - }, - "mem": { - "measurement": [ - "mem_used_percent", - "mem_cached" - ], - "metrics_collection_interval": 60 - }, - "netstat": { - "measurement": [ - "tcp_established", - "tcp_time_wait" - ], - "metrics_collection_interval": 60 - }, - "swap": { - "measurement": [ - "swap_used_percent" - ], - "metrics_collection_interval": 60 - } - } - } -} diff --git a/lib/starknet/lib/assets/restore-from-snapshot.sh b/lib/starknet/lib/assets/restore-from-snapshot.sh deleted file mode 100644 index 6cc246d8..00000000 --- a/lib/starknet/lib/assets/restore-from-snapshot.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash -set +e - -source /etc/environment - -echo "Downloading Snapshot." - -cd /data - -SNAPSHOT_FILE_NAME=snapshot.tar.gz -SNAPSHOT_DIR=/data -SNAPSHOT_DOWNLOAD_STATUS=-1 - -# take about 1 hour to download the Snapshot -while (( SNAPSHOT_DOWNLOAD_STATUS != 0 )) -do - PIDS=$(pgrep aria2c) - if [ -z "$PIDS" ]; then - aria2c $SNAPSHOT_URL -d $SNAPSHOT_DIR -o $SNAPSHOT_FILE_NAME -l /data/download.log --log-level=notice --allow-overwrite=true --allow-piece-length-change=true - fi - SNAPSHOT_DOWNLOAD_STATUS=$? - pid=$(pidof aria2c) - wait $pid - echo "aria2c exit." - case $SNAPSHOT_DOWNLOAD_STATUS in - 3) - echo "File does not exist." - exit 3 - ;; - 9) - echo "No space left on device." - exit 9 - ;; - *) - continue - ;; - esac -done -echo "Downloading Snapshot script finished" - -sleep 60 - -echo "Starting snapshot decompression ..." - -tar -xvf $SNAPSHOT_DIR/$SNAPSHOT_FILE_NAME -C /data 2>&1 | tee unzip.log && echo "decompresed successfully..." || echo "decompression failed..." >> snapshots-decompression.log - -echo "Decompresed snapshot, cleaning up..." - -rm -f /data/juno -mv /data/juno_$STARKNET_NETWORK_ID /data/juno && \ -rm -rf $SNAPSHOT_DIR/$SNAPSHOT_FILE_NAME - -echo "Snapshot is ready, starting the service.." - -chown -R ubuntu:ubuntu /data - -sudo systemctl daemon-reload -sudo systemctl enable --now starknet diff --git a/lib/starknet/lib/assets/setup-instance-store-volumes.sh b/lib/starknet/lib/assets/setup-instance-store-volumes.sh deleted file mode 100644 index e55c630c..00000000 --- a/lib/starknet/lib/assets/setup-instance-store-volumes.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -source /etc/environment - -if [[ "$DATA_VOLUME_TYPE" == "instance-store" ]]; then - echo "Data volume type is instance store" - export DATA_VOLUME_ID=/dev/$(lsblk -lnb | awk 'max < $4 {max = $4; vol = $1} END {print vol}') -fi - -if [ -n "$DATA_VOLUME_ID" ]; then - if [ $(df --output=target | grep -c "/data") -lt 1 ]; then - echo "Checking fstab for Data volume" - - mkfs.ext4 $DATA_VOLUME_ID - echo "Data volume formatted. Mounting..." - echo "waiting for volume to get UUID" - OUTPUT=0; - while [ "$OUTPUT" = 0 ]; do - DATA_VOLUME_UUID=$(lsblk -fn -o UUID $DATA_VOLUME_ID) - OUTPUT=$(echo $DATA_VOLUME_UUID | grep -c - $2) - echo $OUTPUT - done - DATA_VOLUME_UUID=$(lsblk -fn -o UUID $DATA_VOLUME_ID) - DATA_VOLUME_FSTAB_CONF="UUID=$DATA_VOLUME_UUID /data ext4 defaults 0 2" - echo "DATA_VOLUME_ID="$DATA_VOLUME_ID - echo "DATA_VOLUME_UUID="$DATA_VOLUME_UUID - echo "DATA_VOLUME_FSTAB_CONF="$DATA_VOLUME_FSTAB_CONF - - # Check if data disc is already in fstab and replace the line if it is with the new disc UUID - if [ $(grep -c "data" /etc/fstab) -gt 0 ]; then - SED_REPLACEMENT_STRING="$(grep -n "/data" /etc/fstab | cut -d: -f1)s#.*#$DATA_VOLUME_FSTAB_CONF#" - cp /etc/fstab /etc/fstab.bak - sed -i "$SED_REPLACEMENT_STRING" /etc/fstab - else - echo $DATA_VOLUME_FSTAB_CONF | sudo tee -a /etc/fstab - fi - - sudo mount -a - - else - echo "Data volume is mounted, nothing changed" - fi -fi diff --git a/lib/starknet/lib/assets/starknet/rpc-template.sh b/lib/starknet/lib/assets/starknet/rpc-template.sh deleted file mode 100644 index 96359fab..00000000 --- a/lib/starknet/lib/assets/starknet/rpc-template.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -source /etc/environment - -# Get local IP -TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") -export EC2_INTERNAL_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s "http://169.254.169.254/latest/meta-data/local-ipv4") - -# Start Juno node -/home/ubuntu/juno-source/build/juno \ - --db-path "/data/juno" \ - --http \ - --http-host "${EC2_INTERNAL_IP}" \ - --http-port 6060 \ - --network ${STARKNET_NETWORK_ID} \ - --eth-node ${STARKNET_L1_ENDPOINT} diff --git a/lib/starknet/lib/assets/sync-checker/syncchecker-starknet.sh b/lib/starknet/lib/assets/sync-checker/syncchecker-starknet.sh deleted file mode 100644 index c2a8ee99..00000000 --- a/lib/starknet/lib/assets/sync-checker/syncchecker-starknet.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") -EC2_INTERNAL_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s "http://169.254.169.254/latest/meta-data/local-ipv4") - -# Getting syncing status from Starknet node -STARKNET_SYNC_STATS=$(curl -s -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"starknet_syncing","params":[],"id":1}' http://$EC2_INTERNAL_IP:6060 | jq -r ".result") - -if [[ "$STARKNET_SYNC_STATS" == "false" ]]; then - STARKNET_CURRENT_BLOCK=$(curl -s -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"starknet_blockNumber","params":[],"id":1}' http://$EC2_INTERNAL_IP:6060 | jq -r ".result") - STARKNET_HIGHEST_BLOCK=$STARKNET_CURRENT_BLOCK -else - # Extract current and highest block numbers from the syncing status - STARKNET_CURRENT_BLOCK=$(echo "$STARKNET_SYNC_STATS" | jq -r ".current_block_num") - STARKNET_HIGHEST_BLOCK=$(echo "$STARKNET_SYNC_STATS" | jq -r ".highest_block_num") -fi - -STARKNET_BLOCKS_BEHIND="$((STARKNET_HIGHEST_BLOCK-STARKNET_CURRENT_BLOCK))" -# Echo the current and highest block numbers for verification -echo "Current Block: $STARKNET_CURRENT_BLOCK" -echo "Highest Block: $STARKNET_HIGHEST_BLOCK" -echo "STARKNET_BLOCKS_BEHIND Block: $STARKNET_BLOCKS_BEHIND" - -# Sending data to CloudWatch -INSTANCE_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/instance-id) -REGION=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq .region -r) -TIMESTAMP=$(date +"%Y-%m-%dT%H:%M:%S%z") - -aws cloudwatch put-metric-data --metric-name starknet_current_block --namespace CWAgent --value $STARKNET_CURRENT_BLOCK --timestamp $TIMESTAMP --dimensions InstanceId=$INSTANCE_ID --region $REGION -aws cloudwatch put-metric-data --metric-name starknet_blocks_behind --namespace CWAgent --value $STARKNET_BLOCKS_BEHIND --timestamp $TIMESTAMP --dimensions InstanceId=$INSTANCE_ID --region $REGION diff --git a/lib/starknet/lib/assets/user-data/node.sh b/lib/starknet/lib/assets/user-data/node.sh deleted file mode 100644 index 9a371e08..00000000 --- a/lib/starknet/lib/assets/user-data/node.sh +++ /dev/null @@ -1,218 +0,0 @@ -#!/bin/bash -set +e - -{ - echo "AWS_REGION=${_AWS_REGION_}" - echo "ASSETS_S3_PATH=${_ASSETS_S3_PATH_}" - echo "STACK_NAME=${_STACK_NAME_}" - echo "STACK_ID=${_STACK_ID_}" - echo "RESOURCE_ID=${_NODE_CF_LOGICAL_ID_}" - echo "DATA_VOLUME_TYPE=${_DATA_VOLUME_TYPE_}" - echo "DATA_VOLUME_SIZE=${_DATA_VOLUME_SIZE_}" - echo "STARKNET_NODE_VERSION=${_STARKNET_NODE_VERSION_}" - echo "STARKNET_NETWORK_ID=${_STARKNET_NETWORK_ID_}" - echo "STARKNET_L1_ENDPOINT=${_STARKNET_L1_ENDPOINT_}" - echo "SNAPSHOT_URL=${_SNAPSHOT_URL_}" -} >> /etc/environment - -source /etc/environment - -arch=$(uname -m) - -apt-get -yqq update -apt-get -yqq install -y build-essential cargo pkg-config git upx-ucl libjemalloc-dev libjemalloc2 awscli jq unzip python3-pip - -cd /tmp - -# install aria2 a p2p downloader - -if [ "$arch" == "x86_64" ]; then - wget https://github.com/q3aql/aria2-static-builds/releases/download/v1.36.0/aria2-1.36.0-linux-gnu-64bit-build1.tar.bz2 - tar jxvf aria2-1.36.0-linux-gnu-64bit-build1.tar.bz2 - cd aria2-1.36.0-linux-gnu-64bit-build1/ - make install -else - wget https://github.com/q3aql/aria2-static-builds/releases/download/v1.36.0/aria2-1.36.0-linux-gnu-arm-rbpi-build1.tar.bz2 - tar jxvf aria2-1.36.0-linux-gnu-arm-rbpi-build1.tar.bz2 - cd aria2-1.36.0-linux-gnu-arm-rbpi-build1/ - make install -fi - -cd /opt - -echo "Downloading assets zip file" -aws s3 cp $ASSETS_S3_PATH ./assets.zip -unzip -q assets.zip - - -echo "Install and configure CloudWatch agent" -wget -q https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb -dpkg -i -E amazon-cloudwatch-agent.deb - -echo 'Configuring CloudWatch Agent' -mkdir -p /opt/aws/amazon-cloudwatch-agent/etc/ -cp /opt/cw-agent.json /opt/aws/amazon-cloudwatch-agent/etc/custom-amazon-cloudwatch-agent.json - -echo "Starting CloudWatch Agent" -/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ --a fetch-config -c file:/opt/aws/amazon-cloudwatch-agent/etc/custom-amazon-cloudwatch-agent.json -m ec2 -s -systemctl status amazon-cloudwatch-agent - -# Once the EC2 instance ready, notify Cloudformation the instance is ready -if [[ "$STACK_ID" != "none" ]]; then - echo "Install and enable CloudFormation helper scripts" - mkdir -p /opt/aws/ - pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz - sudo ln -s /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup - - echo "Configuring CloudFormation helper scripts" - mkdir -p /etc/cfn/ - mv /opt/cfn-hup/cfn-hup.conf /etc/cfn/cfn-hup.conf - sed -i "s;__AWS_STACK_ID__;\"$STACK_ID\";g" /etc/cfn/cfn-hup.conf - sed -i "s;__AWS_REGION__;\"$AWS_REGION\";g" /etc/cfn/cfn-hup.conf - - mkdir -p /etc/cfn/hooks.d/ - mv /opt/cfn-hup/cfn-auto-reloader.conf /etc/cfn/hooks.d/cfn-auto-reloader.conf - sed -i "s;__AWS_STACK_NAME__;\"$STACK_NAME\";g" /etc/cfn/hooks.d/cfn-auto-reloader.conf - sed -i "s;__AWS_REGION__;\"$AWS_REGION\";g" /etc/cfn/hooks.d/cfn-auto-reloader.conf - - echo "Starting CloudFormation helper scripts as a service" - mv /opt/cfn-hup/cfn-hup.service /etc/systemd/system/cfn-hup.service - - systemctl daemon-reload - systemctl enable --now cfn-hup - systemctl start cfn-hup.service - - cfn-signal --stack $STACK_NAME --resource $RESOURCE_ID --region $AWS_REGION -fi - -echo "Waiting for volumes to be available" -sleep 60 - -mkdir "/data" - -if [[ "$DATA_VOLUME_TYPE" == "instance-store" ]]; then - echo "Data volume type is instance store" - - cd /opt - chmod +x /opt/setup-instance-store-volumes.sh - - (crontab -l; echo "@reboot /opt/setup-instance-store-volumes.sh >/tmp/setup-instance-store-volumes.log 2>&1") | crontab - - crontab -l - - DATA_VOLUME_ID=/dev/$(lsblk -lnb | awk 'max < $4 {max = $4; vol = $1} END {print vol}') - -else - echo "Data volume type is EBS" - - DATA_VOLUME_ID=/dev/$(lsblk -lnb | awk -v VOLUME_SIZE_BYTES="$DATA_VOLUME_SIZE" '{if ($4== VOLUME_SIZE_BYTES) {print $1}}') -fi - -mkfs -t ext4 $DATA_VOLUME_ID -echo "waiting for volume to get UUID" - OUTPUT=0; - while [ "$OUTPUT" = 0 ]; do - DATA_VOLUME_UUID=$(lsblk -fn -o UUID $DATA_VOLUME_ID) - OUTPUT=$(echo $DATA_VOLUME_UUID | grep -c - $2) - echo $OUTPUT - done -DATA_VOLUME_UUID=$(lsblk -fn -o UUID $DATA_VOLUME_ID) -DATA_VOLUME_FSTAB_CONF="UUID=$DATA_VOLUME_UUID /data ext4 defaults 0 2" -echo "DATA_VOLUME_ID="$DATA_VOLUME_ID -echo "DATA_VOLUME_UUID="$DATA_VOLUME_UUID -echo "DATA_VOLUME_FSTAB_CONF="$DATA_VOLUME_FSTAB_CONF -echo $DATA_VOLUME_FSTAB_CONF | tee -a /etc/fstab -mount -a - -mkdir "/data/juno" -mount -a -chown ubuntu:ubuntu -R /data - -echo "Install Juno Starknet agent" - -cd /home/ubuntu - -git clone --branch $STARKNET_NODE_VERSION --single-branch https://github.com/NethermindEth/juno.git juno-source -cd /home/ubuntu/juno-source - -echo "Install Go 1.22 Version" -snap info go -snap install go --channel=1.22/stable --classic - -export GOPATH=/snap/bin/go -go env|grep CACHE -sudo su - ubuntu - -echo "Build" -cd /home/ubuntu/juno-source -echo "Build: running..." -sudo make juno -alias juno=./build/bin/juno -sudo chown ubuntu:ubuntu -R ../juno-source -echo "Build: done" - -# Copy startup script to correct location -sudo mkdir "/home/ubuntu/bin/" -sudo mv /opt/starknet/rpc-template.sh /home/ubuntu/bin/node.sh -sudo chmod +x /home/ubuntu/bin/node.sh -sudo mkdir /var/log/starknet -sudo chown ubuntu:ubuntu /var/log/starknet - -echo "Configuring starknet as a service" -sudo bash -c 'cat > /etc/systemd/system/starknet.service < logrotate.starkneterr < { - switch (dataVolumeType) { - case "gp3": - return ec2.EbsDeviceVolumeType.GP3; - case "io2": - return ec2.EbsDeviceVolumeType.IO2; - case "io1": - return ec2.EbsDeviceVolumeType.IO1; - case "instance-store": - return constants.InstanceStoreageDeviceVolumeType; - default: - return ec2.EbsDeviceVolumeType.GP3; - } -} - -export const baseConfig: configTypes.StarknetBaseConfig = { - accountId: process.env.AWS_ACCOUNT_ID || "xxxxxxxxxxx", - region: process.env.AWS_REGION || "us-east-1", -} - -export const baseNodeConfig: configTypes.StarknetBaseNodeConfig = { - ambEntereumNodeNetworkId: process.env.AMB_ENTEREUM_NODE_NETWORK_ID || "mainnet", - ambEntereumNodeInstanceType: process.env.AMB_ETHEREUM_NODE_INSTANCE_TYPE || "bc.m5.xlarge", - instanceType: new ec2.InstanceType(process.env.STARKNET_INSTANCE_TYPE ? process.env.STARKNET_INSTANCE_TYPE : "m6a.2xlarge"), - instanceCpuType: ec2.AmazonLinuxCpuType.X86_64, - starknetNetworkId: process.env.STARKNET_NETWORK_ID || "mainnet", - starknetNodeVersion: process.env.STARKNET_NODE_VERSION || "v0.11.7", - starknetL1Endpoint: process.env.STARKNET_L1_ENDPOINT || constants.NoneValue, - dataVolume: { - sizeGiB: process.env.STARKNET_DATA_VOL_SIZE ? parseInt(process.env.STARKNET_DATA_VOL_SIZE): 250, - type: parseDataVolumeType(process.env.STARKNET_DATA_VOL_TYPE?.toLowerCase() ? process.env.STARKNET_DATA_VOL_TYPE?.toLowerCase() : "gp3"), - iops: process.env.STARKNET_DATA_VOL_IOPS ? parseInt(process.env.STARKNET_DATA_VOL_IOPS): 3000, - throughput: process.env.STARKNET_DATA_VOL_THROUGHPUT ? parseInt(process.env.STARKNET_DATA_VOL_THROUGHPUT): 250, - }, - snapshotUrl: process.env.STARKNET_SNAPSHOT_URL || constants.NoneValue, -}; diff --git a/lib/starknet/lib/constructs/node-cw-dashboard.ts b/lib/starknet/lib/constructs/node-cw-dashboard.ts deleted file mode 100644 index 8a10d774..00000000 --- a/lib/starknet/lib/constructs/node-cw-dashboard.ts +++ /dev/null @@ -1,235 +0,0 @@ -export const SyncNodeCWDashboardJSON = { - "widgets": [ - { - "height": 5, - "width": 6, - "y": 0, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "CPUUtilization", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "CPU utilization (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 18, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "NetworkIn", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Network in (bytes)" - } - }, - { - "height": 5, - "width": 6, - "y": 0, - "x": 18, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "NetworkOut", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Network out (bytes)" - } - }, - { - "height": 5, - "width": 6, - "y": 10, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 300, - "metrics": [ - [ "CWAgent", "mem_used_percent", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Mem Used (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 300, - "metrics": [ - [ "CWAgent", "cpu_usage_iowait", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "CPU Usage IO wait (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 0, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ { "expression": "m7/PERIOD(m7)", "label": "Read", "id": "e7" } ], - [ "CWAgent", "diskio_reads", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7", "visible": false, "stat": "Sum", "period": 60 } ], - [ { "expression": "m8/PERIOD(m8)", "label": "Write", "id": "e8" } ], - [ "CWAgent", "diskio_writes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m8", "visible": false, "stat": "Sum", "period": 60 } ] - ], - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Sum", - "period": 60, - "title": "nvme1n1 Volume Read/Write (IO/sec)" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 12, - "type": "metric", - "properties": { - "metrics": [ - [ "CWAgent", "starknet_current_block", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "sparkline": true, - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Maximum", - "period": 60, - "title": "Starknet Client Block Height" - } - }, - { - "height": 5, - "width": 6, - "y": 10, - "x": 12, - "type": "metric", - "properties": { - "sparkline": true, - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Maximum", - "period": 60, - "metrics": [ - [ "CWAgent", "starknet_blocks_behind", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Starknet Client Blocks Behind" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 6, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Sum", - "period": 60, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ { "expression": "IF(m7_2 !=0, (m7_1 / m7_2), 0)", "label": "Read", "id": "e7" } ], - [ "CWAgent", "diskio_read_time", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_1", "visible": false, "stat": "Sum", "period": 60 } ], - [ "CWAgent", "diskio_reads", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_2", "visible": false, "stat": "Sum", "period": 60 } ], - [ { "expression": "IF(m7_4 !=0, (m7_3 / m7_4), 0)", "label": "Write", "id": "e8" } ], - [ "CWAgent", "diskio_write_time", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_3", "visible": false, "stat": "Sum", "period": 60 } ], - [ "CWAgent", "diskio_writes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_4", "visible": false, "stat": "Sum", "period": 60 } ] - ], - "title": "nvme1n1 Volume Read/Write latency (ms/op)" - } - }, - { - "height": 5, - "width": 6, - "y": 10, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ { "expression": "(m2/1048576)/PERIOD(m2)", "label": "Read", "id": "e2", "period": 60, "region": "${REGION}" } ], - [ "CWAgent", "diskio_read_bytes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m2", "stat": "Sum", "visible": false, "period": 60 } ], - [ { "expression": "(m3/1048576)/PERIOD(m3)", "label": "Write", "id": "e3", "period": 60, "region": "${REGION}" } ], - [ "CWAgent", "diskio_write_bytes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m3", "stat": "Sum", "visible": false, "period": 60 } ] - ], - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 60, - "title": "nvme1n1 Volume Read/Write throughput (MiB/sec)" - } - }, - { - "height": 5, - "width": 6, - "y": 0, - "x": 12, - "type": "metric", - "properties": { - "metrics": [ - [ "CWAgent", "disk_used_percent", "path", "/data", "InstanceId", "${INSTANCE_ID}", "device", "nvme1n1", "fstype", "ext4", { "region": "${REGION}", "label": "/data" } ] - ], - "sparkline": true, - "view": "singleValue", - "region": "${REGION}", - "title": "nvme1n1 Disk Used (%)", - "period": 60, - "stat": "Average" - } - } - ] -} diff --git a/lib/starknet/lib/constructs/starknet-node-security-group.ts b/lib/starknet/lib/constructs/starknet-node-security-group.ts deleted file mode 100644 index c6624194..00000000 --- a/lib/starknet/lib/constructs/starknet-node-security-group.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from 'constructs'; -import * as ec2 from "aws-cdk-lib/aws-ec2"; - -export interface StarknetNodeSecurityGroupConstructProps { - vpc: cdk.aws_ec2.IVpc; - } - - export class StarknetNodeSecurityGroupConstruct extends cdkConstructs.Construct { - public securityGroup: cdk.aws_ec2.ISecurityGroup; - - constructor(scope: cdkConstructs.Construct, id: string, props: StarknetNodeSecurityGroupConstructProps) { - super(scope, id); - - const { - vpc, - } = props; - - const sg = new ec2.SecurityGroup(this, `rpc-node-security-group`, { - vpc, - description: "Security Group for Blockchain nodes", - allowAllOutbound: true, - }); - - // Private port - sg.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(6060), "Starknet Client RPC"); - - this.securityGroup = sg - } - } diff --git a/lib/starknet/lib/single-node-stack.ts b/lib/starknet/lib/single-node-stack.ts deleted file mode 100644 index af870ecc..00000000 --- a/lib/starknet/lib/single-node-stack.ts +++ /dev/null @@ -1,148 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as iam from "aws-cdk-lib/aws-iam"; -import * as s3Assets from "aws-cdk-lib/aws-s3-assets"; -import * as path from "path"; -import * as fs from "fs"; -import * as nodeCwDashboard from "./constructs/node-cw-dashboard" -import * as cw from 'aws-cdk-lib/aws-cloudwatch'; -import * as nag from "cdk-nag"; -import { SingleNodeConstruct } from "../../constructs/single-node" -import * as configTypes from "./config/starknetConfig.interface"; -import * as constants from "../../constructs/constants"; -import { StarknetNodeSecurityGroupConstruct } from "./constructs/starknet-node-security-group"; - -export interface StarknetSingleNodeStackProps extends cdk.StackProps { - instanceType: ec2.InstanceType; - instanceCpuType: ec2.AmazonLinuxCpuType; - starknetNetworkId: configTypes.StarknetNetworkId; - starknetNodeVersion: string; - dataVolume: configTypes.StarknetDataVolumeConfig; - starknetL1Endpoint: string; - snapshotUrl: string; -} - -export class StarknetSingleNodeStack extends cdk.Stack { - constructor(scope: cdkConstructs.Construct, id: string, props: StarknetSingleNodeStackProps) { - super(scope, id, props); - - // Setting up necessary environment variables - const REGION = cdk.Stack.of(this).region; - const STACK_NAME = cdk.Stack.of(this).stackName; - const STACK_ID = cdk.Stack.of(this).stackId; - const availabilityZones = cdk.Stack.of(this).availabilityZones; - const chosenAvailabilityZone = availabilityZones.slice(0, 1)[0]; - - // Getting our config from initialization properties - const { - instanceType, - instanceCpuType, - starknetNetworkId, - starknetNodeVersion, - dataVolume, - starknetL1Endpoint, - snapshotUrl, - } = props; - - let starknetL1EndpointURL: string; - if (starknetL1Endpoint == constants.NoneValue){ - // STARKNET_L1_ENDPOINT seems to be empty, trying to connect with pre-provioned AMB Access Ethereum URL - starknetL1EndpointURL = cdk.Fn.importValue("AmbEthereumNodeRpcUrlWithBillingToken"); - } else { - starknetL1EndpointURL = starknetL1Endpoint - } - - // Using default VPC - const vpc = ec2.Vpc.fromLookup(this, "vpc", { isDefault: true }); - - // Setting up the security group for the node from Starknet-specific construct - const instanceSG = new StarknetNodeSecurityGroupConstruct (this, "security-group", { - vpc: vpc, - }) - - // Making our scripts and configis from the local "assets" directory available for instance to download - const asset = new s3Assets.Asset(this, "assets", { - path: path.join(__dirname, "assets"), - }); - - // Getting the IAM role ARN from the common stack - const importedInstanceRoleArn = cdk.Fn.importValue("StarknetNodeInstanceRoleArn"); - // const ambEthereumNodeRpcUrlWithBillingToken = cdk.Fn.importValue("AmbEthereumNodeRpcUrlWithBillingToken"); - - const instanceRole = iam.Role.fromRoleArn(this, "iam-role", importedInstanceRoleArn); - - // Making sure our instance will be able to read the assets - asset.bucket.grantRead(instanceRole); - - // Setting up the node using generic Single Node constract - if (instanceCpuType === ec2.AmazonLinuxCpuType.ARM_64) { - throw new Error("ARM_64 is not yet supported"); - } - - // Use Ubuntu 20.04 LTS image for amd64. Find more: https://discourse.ubuntu.com/t/finding-ubuntu-images-with-the-aws-ssm-parameter-store/15507 - const ubuntu204stableImageSsmName = "/aws/service/canonical/ubuntu/server/20.04/stable/current/amd64/hvm/ebs-gp2/ami-id" - - const node = new SingleNodeConstruct(this, "sync-node", { - instanceName: STACK_NAME, - instanceType, - dataVolumes: [dataVolume], - rootDataVolumeDeviceName: "/dev/sda1", - machineImage: ec2.MachineImage.fromSsmParameter(ubuntu204stableImageSsmName), - vpc, - availabilityZone: chosenAvailabilityZone, - role: instanceRole, - securityGroup: instanceSG.securityGroup, - vpcSubnets: { - subnetType: ec2.SubnetType.PUBLIC, - }, - }); - - // Parsing user data script and injecting necessary variables - const nodeStartScript = fs.readFileSync(path.join(__dirname, "assets", "user-data", "node.sh")).toString(); - const dataVolumeSizeBytes = dataVolume.sizeGiB * constants.GibibytesToBytesConversionCoefficient; - - const modifiedInitNodeScript = cdk.Fn.sub(nodeStartScript, { - _AWS_REGION_: REGION, - _ASSETS_S3_PATH_: `s3://${asset.s3BucketName}/${asset.s3ObjectKey}`, - _STACK_NAME_: STACK_NAME, - _STACK_ID_: STACK_ID, - _NODE_CF_LOGICAL_ID_: node.nodeCFLogicalId, - _DATA_VOLUME_TYPE_: dataVolume.type, - _DATA_VOLUME_SIZE_: dataVolumeSizeBytes.toString(), - _STARKNET_NODE_VERSION_: starknetNodeVersion, - _STARKNET_NETWORK_ID_: starknetNetworkId, - _STARKNET_L1_ENDPOINT_: starknetL1EndpointURL, - _SNAPSHOT_URL_: props.snapshotUrl, - }); - node.instance.addUserData(modifiedInitNodeScript); - - // Adding CloudWatch dashboard to the node - const dashboardString = cdk.Fn.sub(JSON.stringify(nodeCwDashboard.SyncNodeCWDashboardJSON), { - INSTANCE_ID:node.instanceId, - INSTANCE_NAME: STACK_NAME, - REGION: REGION, - }) - - new cw.CfnDashboard(this, 'starknet-cw-dashboard', { - dashboardName: STACK_NAME, - dashboardBody: dashboardString, - }); - - new cdk.CfnOutput(this, "node-instance-id", { - value: node.instanceId, - }); - - // Adding suppressions to the stack - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-IAM5", - reason: "Need read access to the S3 bucket with assets", - }, - ], - true - ); - } -} diff --git a/lib/starknet/package.json b/lib/starknet/package.json deleted file mode 100644 index 9c9b8b2d..00000000 --- a/lib/starknet/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "aws-blockchain-node-runners-starknet", - "version": "0.1.0", - "bin": { - "starknet": "app.ts" - }, - "scripts": { - "build": "tsc", - "watch": "tsc -w", - "test": "jest", - "cdk": "cdk", - "cdk_deploy_common": "cdk deploy starknet-common", - "cdk_synth_single_node": "cdk synth starknet-single-node", - "cdk_deploy_single_node": "cdk deploy starknet-single-node", - "cdk_destroy_single_node": "cdk destroy starknet-single-node" - } -} diff --git a/lib/starknet/sample-configs/.env-sample-full b/lib/starknet/sample-configs/.env-sample-full deleted file mode 100644 index 53cf604a..00000000 --- a/lib/starknet/sample-configs/.env-sample-full +++ /dev/null @@ -1,24 +0,0 @@ -############################################################# -# Example configuration for Starknet nodes runner app on AWS # -############################################################# - -## Set the AWS account is and region for your environment ## -AWS_ACCOUNT_ID="xxx" -AWS_REGION="us-east-1" # Regions supported by Amazon Managed Blockchain Access Ethereum: https://docs.aws.amazon.com/general/latest/gr/managedblockchain.html#managedblockchain-access - -## Common configuration parameters # -AMB_ETHEREUM_NODE_NETWORK_ID="mainnet" # All options: "mainnet", "goerli" -AMB_ETHEREUM_NODE_INSTANCE_TYPE="bc.m5.xlarge" # For available options see: https://aws.amazon.com/managed-blockchain/instance-types/ -STARKNET_L1_ENDPOINT="none" # Websocket L1 endpoint, for example "wss://ethereum-rpc.publicnode.com" . Set to "none" or leave empty if using AMB_ETHEREUM_NODE. -STARKNET_NETWORK_ID="mainnet" # All options: "mainnet", "sepolia", "sepolia-integration" -STARKNET_NODE_VERSION="v0.11.7" # Current Juno node version - -STARKNET_SNAPSHOT_URL="none" # Download snapshot to speed up statup time. Copy the larest URL from the docuemntation: https://juno.nethermind.io/next/snapshots/#mainnet - -STARKNET_INSTANCE_TYPE="m6a.2xlarge" # Recommended for Instance Store: i3.2xlarge, x86_64 -STARKNET_CPU_TYPE="x86_64" # All options: "x86_64", "ARM_64". IMPORTANT: Make sure the CPU type matches the instance type used -# Data volume configuration -STARKNET_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families -STARKNET_DATA_VOL_SIZE="600" # Current required data size to keep both smapshot archive and unarchived version of it -STARKNET_DATA_VOL_IOPS="3000" # Max IOPS for EBS volumes (not applicable for "instance-store") -STARKNET_DATA_VOL_THROUGHPUT="700" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") diff --git a/lib/starknet/test/.env-test b/lib/starknet/test/.env-test deleted file mode 100644 index 5ca8cf77..00000000 --- a/lib/starknet/test/.env-test +++ /dev/null @@ -1,19 +0,0 @@ -############################################################# -# Example configuration for Starknet nodes runner app on AWS # -############################################################# - -## Set the AWS account is and region for your environment ## -AWS_ACCOUNT_ID="xxxxxxxxxxx" -AWS_REGION="us-east-1" # Regions supported by Amazon Managed Blockchain Access Ethereum: https://docs.aws.amazon.com/general/latest/gr/managedblockchain.html#managedblockchain-access - -## Common configuration parameters ## -STARKNET_L1_ENDPOINT="wss://ethereum-rpc.publicnode.com" -STARKNET_NETWORK_ID="mainnet" # All options: "mainnet", "sepolia", "sepolia-integration" -STARKNET_NODE_VERSION="v0.11.7" # Current required version of Starknet - -STARKNET_INSTANCE_TYPE="m6a.2xlarge" -# Data volume configuration -STARKNET_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families -STARKNET_DATA_VOL_SIZE="250" # Current required data size to keep both smapshot archive and unarchived version of it -STARKNET_DATA_VOL_IOPS="3000" # Max IOPS for EBS volumes (not applicable for "instance-store") -STARKNET_DATA_VOL_THROUGHPUT="700" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") diff --git a/lib/starknet/test/starknet-single-node.test.ts b/lib/starknet/test/starknet-single-node.test.ts deleted file mode 100644 index 29ad2ec7..00000000 --- a/lib/starknet/test/starknet-single-node.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import {Match, Template} from "aws-cdk-lib/assertions"; -import * as cdk from "aws-cdk-lib"; -import * as dotenv from 'dotenv'; - -dotenv.config({path: './test/.env-test'}); -import * as config from "../lib/config/starknetConfig"; -import {StarknetSingleNodeStack} from "../lib/single-node-stack"; - -describe("StarknetSingleNodeStack", () => { - let app: cdk.App; - let starknetSingleNodeStack: StarknetSingleNodeStack; - let template: Template; - beforeAll(() => { - app = new cdk.App(); - - // Create the StarknetSingleNodeStack. - starknetSingleNodeStack = new StarknetSingleNodeStack(app, "starknet-single-node", { - stackName: `starknet-single-node-${config.baseConfig.accountId}`, - env: {account: config.baseConfig.accountId, region: config.baseConfig.region}, - - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - starknetNodeVersion: config.baseNodeConfig.starknetNodeVersion, - dataVolume: config.baseNodeConfig.dataVolume, - starknetNetworkId: config.baseNodeConfig.starknetNetworkId, - starknetL1Endpoint: config.baseNodeConfig.starknetL1Endpoint, - snapshotUrl: config.baseNodeConfig.snapshotUrl - }); - - template = Template.fromStack(starknetSingleNodeStack); - }); - - test("Check Security Group", () => { - // Has EC2 instance security group. - template.hasResourceProperties("AWS::EC2::SecurityGroup", { - GroupDescription: Match.anyValue(), - VpcId: Match.anyValue(), - SecurityGroupEgress: [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" - } - ], - SecurityGroupIngress: [ - { - "CidrIp": "1.2.3.4/5", - "Description": "Starknet Client RPC", - "FromPort": 6060, - "IpProtocol": "tcp", - "ToPort": 6060 - } - ] - }) - }); - - - test("Check EC2 Settings", () => { - // Has EC2 instance with node configuration - template.hasResourceProperties("AWS::EC2::Instance", { - AvailabilityZone: Match.anyValue(), - UserData: Match.anyValue(), - BlockDeviceMappings: [ - { - DeviceName: "/dev/sda1", - Ebs: { - DeleteOnTermination: true, - Encrypted: true, - Iops: 3000, - VolumeSize: 46, - VolumeType: "gp3", - } - } - ], - IamInstanceProfile: Match.anyValue(), - ImageId: Match.anyValue(), - InstanceType: "m6a.2xlarge", - Monitoring: true, - PropagateTagsToVolumeOnCreation: true, - SecurityGroupIds: Match.anyValue(), - SubnetId: Match.anyValue(), - }); - - // // Has EBS data volume. - template.hasResourceProperties("AWS::EC2::Volume", { - AvailabilityZone: Match.anyValue(), - Encrypted: true, - Iops: 3000, - MultiAttachEnabled: false, - Size: 250, - Throughput: 700, - VolumeType: "gp3", - }) - - // Has EBS data volume attachment. - template.hasResourceProperties("AWS::EC2::VolumeAttachment", { - Device: "/dev/sdf", - InstanceId: Match.anyValue(), - VolumeId: Match.anyValue(), - }) - }); - - test("Check CloudWatch Dashboard", () => { - // Has CloudWatch dashboard. - template.hasResourceProperties("AWS::CloudWatch::Dashboard", { - DashboardBody: Match.anyValue(), - DashboardName: `starknet-single-node-${config.baseConfig.accountId}` - }) - }); -}); diff --git a/lib/starknet/tsconfig.json b/lib/starknet/tsconfig.json deleted file mode 100644 index 8e1979f3..00000000 --- a/lib/starknet/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": [ - "es2020", - "dom" - ], - "declaration": true, - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": false, - "noUnusedParameters": false, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": false, - "inlineSourceMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictPropertyInitialization": false, - "typeRoots": [ - "../../node_modules/@types" - ] - }, - "exclude": [ - "node_modules", - "cdk.out" - ] -} diff --git a/lib/sui/README.md b/lib/sui/README.md deleted file mode 100644 index 83ff82ce..00000000 --- a/lib/sui/README.md +++ /dev/null @@ -1,233 +0,0 @@ -# Sample AWS Blockchain Node Runner app for Sui Full Node - -| Contributed by | -|:--------------------:| -| [@yinalaws](https://github.com/yinalaws), [@evertonfraga](https://github.com/evertonfraga) | - -## Architecture Overview - -This blueprint has step by step guides to set up a single Sui Full Node. - - -### Sui Full Node setup -![SingleNodeSetup](./doc/assets/Architecture-SingleNode.png) - -This setup is for PoC or development environments and it supports Devnet, Testnet and Mainnet. It deploys a single EC2 instance with Sui client. The RPC port is exposed only to internal IP range of the VPC, while P2P ports allow external access to keep the client synced. - -## Solution Walkthrough - -### Setup Cloud9 - -We will use AWS Cloud9 to execute the subsequent commands. Follow the instructions in [Cloud9 Setup](../../docs/setup-cloud9.md) - -### Clone this repository and install dependencies - -```bash - git clone https://github.com/aws-samples/aws-blockchain-node-runners.git - cd aws-blockchain-node-runners - npm install -``` - -**NOTE:** In this tutorial we will set all major configuration through environment variables, but you also can modify parameters in `config/config.ts`. - -### Prepare to deploy nodes - -1. Make sure you are in the root directory of the cloned repository - -2. If you have deleted or don't have the default VPC, create default VPC - -```bash - aws ec2 create-default-vpc - ``` - - **NOTE:** You may see the following error if the default VPC already exists: `An error occurred (DefaultVpcAlreadyExists) when calling the CreateDefaultVpc operation: A Default VPC already exists for this account in this region.`. That means you can just continue with the following steps. - - **NOTE:** The default VPC must have at least two public subnets in different Availability Zones, and public subnet must set `Auto-assign public IPv4 address` to `YES` - -3. Configure your setup - -Create your own copy of `.env` file and edit it: -```bash - # Make sure you are in aws-blockchain-node-runners/lib/Sui - cd lib/sui - pwd - cp ./sample-configs/.env-sample-full .env - nano .env -``` - **NOTE:** You can find more examples inside the `sample-configs` directory. - - -4. Deploy common components such as IAM role, and Amazon S3 bucket to store data snapshots - -```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/sui - npx cdk deploy sui-common -``` - -### Deploy Sui Full-Node - -1. Deploy Full Node - -```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/sui - npx cdk deploy sui-single-node --json --outputs-file single-node-deploy.json -``` - **NOTE:** The default VPC must have at least two public subnets in different Availability Zones, and public subnet must set `Auto-assign public IPv4 address` to `YES`. - - The EC2 instance will deploy, initialize the node and start the first sync. In Cloudformation the instance will show as successful once the node is running. From that point it still takes a while until the node is synced to the blockchain. You can check the sync status with the REST call below in step 4. If the `curl cannot connect to the node on port 9000, then the node is still importing. Once that's done, the curl command works. - -2. After starting the node you need to wait for the inital syncronization process to finish. It may take from an hour to half a day depending on the the state of the network. You can use Amazon CloudWatch to track the progress. To see them: - - - Navigate to [CloudWatch service](https://console.aws.amazon.com/cloudwatch/) (make sure you are in the region you have specified for `AWS_REGION`) - - Open `Dashboards` and select `tz-single-node--` from the list of dashboards. - -4. Once the initial synchronization is done, you should be able to access the RPC API of that node from within the same VPC. The RPC port is not exposed to the Internet. Check if the JSON-RPC port is open and working — run the following command from a terminal: - -``` We query if the node is synced to main - ## replace with your server IP address -curl --location --request POST :9000 \ ---header 'Content-Type: application/json' \ ---data-raw '{ "jsonrpc":"2.0", "method":"rpc.discover","id":1}' -``` - -The result should start like like this (the actual balance might change): - -``` -{"jsonrpc":"2.0","result":{"openrpc":"1.2.6","info":{"title":"Sui JSON-RPC","description":"Sui JSON-RPC API for interaction with Sui Full node. Make RPC calls using https://fullnode.NETWORK.sui.io:443, where NETWORK is the network you want to use (testnet, devnet, mainnet). By default, local networks use port 9000.","contact":{"name":"Mysten Labs","url":"https://mystenlabs.com","email":"build@mystenlabs.com"},"license":{"name":"Apache-2.0","url":"https://raw.githubusercontent.com/MystenLabs/sui/main/LICENSE"},"version":"1.28.2"},"methods -``` - - - - - - - - -### Clearing up and undeploying everything - -1. Undeploy RPC Nodes, Sync Nodes and Common components - -```bash - # Setting the AWS account id and region in case local .env file is lost - export AWS_ACCOUNT_ID= - export AWS_REGION= - - pwd - # Make sure you are in aws-blockchain-node-runners/lib/Sui - - # Undeploy Single Fullnode - cdk destroy sui-single-node - - - # You need to manually delete an s3 bucket with a name similar to 'sui-snapshots-$accountid-tz-nodes-common' on the console,firstly empty the bucket,secondly delete the bucket,and then execute - # Delete all common components like IAM role and Security Group - cdk destroy sui-common -``` - -2. Follow steps to delete the Cloud9 instance in [Cloud9 Setup](../../doc/setup-cloud9.md) - -### FAQ - -1. How to check the logs from the EC2 user-data script? - - **Note:** In this tutorial we chose not to use SSH and use Session Manager instead. That allows you to log all sessions in AWS CloudTrail to see who logged into the server and when. If you receive an error similar to `SessionManagerPlugin is not found`, [install Session Manager plugin for AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) - -```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/Sui - - export INSTANCE_ID=$(jq -r '.["sui-single-node-testnet"].nodeinstanceid' single-node-deploy.json) - echo "INSTANCE_ID=" $INSTANCE_ID - aws ssm start-session --target $INSTANCE_ID - sudo cat /var/log/cloud-init-output.log -``` - -2. If SSH is disabled, how to login to fullnode instance? - -```bash - NODE_INTERNAL_IP=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID --query 'Reservations[*].Instances[*].PrivateIpAddress' --output text) - echo "NODE_INTERNAL_IP="$NODE_INTERNAL_IP - pwd - # Make sure you are in aws-blockchain-node-runners/lib/Sui - aws ssm start-session --target $INSTANCE_ID - -``` - -3. Service Tools - -``` -#Check Sui version -sui -V -# Check node logs -sudo journalctl -fu suid -o cat -# Check node status -sudo service suid status -# Restart node -sudo systemctl restart suid -# Stop Node -sudo systemctl stop suid -# Start Node -sudo systemctl start suid -``` - -4. Journalctl and Node Status throws errors: - -Set up archival fallback to enable your node to fallback to an archive in case of lag, add this block to your fullnode.yaml file as described here https://docs.sui.io/guides/operator/archives#set-up-archival-fallback. Restart Node - -``` -Example: - -state-archive-read-config: - - object-store-config: - object-store: "S3" - # Use mysten-testnet-archives for testnet - # Use mysten-mainnet-archives for mainnet - bucket: "mysten-testnet-archives" - # Use your AWS account access key id - aws-access-key-id: "" - # Use your AWS account secret access key - aws-secret-access-key: "" - aws-region: "us-west-2" - object-store-connection-limit: 20 - # How many objects to read ahead when catching up - concurrency: 5 - # Whether to prune local state based on latest checkpoint in archive. - # This should stay false for most use cases - use-for-pruning-watermark: false - -``` - -5. Restoring a Full node using snapshots: Restoring using RocksDB snapshots to restore from a RocksDB snapshot, follow these steps (https://docs.sui.io/guides/operator/snapshots): -``` -Syntax: -sui-tool download-db-snapshot --latest \ - --network --snapshot-bucket \ - --snapshot-bucket-type --path \ - --num-parallel-downloads 25 \ - --skip-indexes \ - --no-sign-request - - -Example: -sudo sui-tool download-db-snapshot --latest --network testnet --path /data/sui/db/live --num-parallel-downloads 50 --skip-indexes --no-sign-request - -``` - -6. Compare the number of checkpoints on your node and on chain - -```bash - ## replace with your server IP address -curl -q :9184/metrics 2>/dev/null |grep '^highest_synced_checkpoint'; echo -``` -```bash -## replace with devnet| testnet | mainnet -curl --location --request POST 'https://fullnode..sui.io:443/' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0", "id":1,"method":"sui_getLatestCheckpointSequenceNumber"}'; echo -``` - -7. Monitoring Sui node metrics over port TCP/9184 - -``` -Enter your node's external IP at https://node.sui.zvalid.com/ -``` diff --git a/lib/sui/app.ts b/lib/sui/app.ts deleted file mode 100644 index 1cbc3a60..00000000 --- a/lib/sui/app.ts +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env node -import "dotenv/config" -import "source-map-support/register"; -import * as cdk from "aws-cdk-lib"; -import * as nag from "cdk-nag"; -import * as config from "./lib/config/suiConfig"; -import {SuiCommonStack} from "./lib/common-stack"; -import {SuiSingleNodeStack} from "./lib/single-node-stack"; - -const app = new cdk.App(); -cdk.Tags.of(app).add("Project", "AWS_Sui"); - -new SuiCommonStack(app, "sui-common", { - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - stackName: "sui-nodes-common", -}); - - - -new SuiSingleNodeStack(app, "sui-single-node", { - stackName: `sui-single-node-${config.baseNodeConfig.suiNetworkId}`, - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - dataVolume: config.baseNodeConfig.dataVolume, - suiNetworkId: config.baseNodeConfig.suiNetworkId, -}); - -// Security Check -cdk.Aspects.of(app).add( - new nag.AwsSolutionsChecks({ - verbose: false, - reports: true, - logIgnores: false, - }) -); diff --git a/lib/sui/cdk.json b/lib/sui/cdk.json deleted file mode 100644 index 7714e8c2..00000000 --- a/lib/sui/cdk.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "app": "npx ts-node --prefer-ts-exts app.ts", - "watch": { - "include": [ - "**" - ], - "exclude": [ - "README.md", - "cdk*.json", - "**/*.d.ts", - "**/*.js", - "tsconfig.json", - "package*.json", - "yarn.lock", - "node_modules", - "test" - ] - }, - "context": { - "@aws-cdk/aws-lambda:recognizeLayerVersion": true, - "@aws-cdk/core:checkSecretUsage": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ], - "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, - "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, - "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, - "@aws-cdk/aws-iam:minimizePolicies": true, - "@aws-cdk/core:validateSnapshotRemovalPolicy": true, - "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, - "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, - "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, - "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, - "@aws-cdk/core:enablePartitionLiterals": true, - "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, - "@aws-cdk/aws-iam:standardizedServicePrincipals": true, - "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, - "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, - "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, - "@aws-cdk/aws-route53-patters:useCertificate": true, - "@aws-cdk/customresources:installLatestAwsSdkDefault": false, - "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, - "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, - "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, - "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, - "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, - "@aws-cdk/aws-redshift:columnId": true, - "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, - "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, - "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, - "@aws-cdk/aws-kms:aliasNameRef": true, - "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, - "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, - "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true - } -} diff --git a/lib/sui/doc/assets/Architecture-SingleNode.png b/lib/sui/doc/assets/Architecture-SingleNode.png deleted file mode 100644 index 73f864ad23eccbe7db9797a8b0703c7741517c78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45249 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/test'], - testMatch: ['**/*.test.ts'], - transform: { - '^.+\\.tsx?$': 'ts-jest' - } -}; diff --git a/lib/sui/lib/assets/cw-agent.json b/lib/sui/lib/assets/cw-agent.json deleted file mode 100644 index 28833017..00000000 --- a/lib/sui/lib/assets/cw-agent.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "agent": { - "metrics_collection_interval": 60, - "run_as_user": "root" - }, - "metrics": { - "aggregation_dimensions": [ - [ - "InstanceId" - ] - ], - "append_dimensions": { - "InstanceId": "${aws:InstanceId}" - }, - "metrics_collected": { - "cpu": { - "measurement": [ - "cpu_usage_idle", - "cpu_usage_iowait", - "cpu_usage_user", - "cpu_usage_system" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ], - "totalcpu": false - }, - "disk": { - "measurement": [ - "used_percent" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ] - }, - "diskio": { - "measurement": [ - "io_time", - "write_bytes", - "read_bytes", - "writes", - "reads", - "write_time", - "read_time", - "iops_in_progress" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ] - }, - "mem": { - "measurement": [ - "mem_used_percent", - "mem_cached" - ], - "metrics_collection_interval": 60 - }, - "netstat": { - "measurement": [ - "tcp_established", - "tcp_time_wait" - ], - "metrics_collection_interval": 60 - }, - "swap": { - "measurement": [ - "swap_used_percent" - ], - "metrics_collection_interval": 60 - } - } - } -} diff --git a/lib/sui/lib/assets/user-data/node.sh b/lib/sui/lib/assets/user-data/node.sh deleted file mode 100644 index 3bd0727a..00000000 --- a/lib/sui/lib/assets/user-data/node.sh +++ /dev/null @@ -1,322 +0,0 @@ -#!/bin/bash - -# via https://github.com/Contribution-DAO/sui-node-setup -# Modifications made by yinalaws in 2024 -# ASCII art removed for brevity, silenced interactions, added testnet p2p, Ubuntu 24.04 LTS tests - -echo "[LOG] script start" -echo "[LOG] User: $(whoami)" - -export USER=ubuntu -set +e - -{ - echo "AWS_REGION=${_AWS_REGION_}" - echo "STACK_NAME=${_STACK_NAME_}" - echo "STACK_ID=${_STACK_ID_}" - echo "RESOURCE_ID=${_NODE_CF_LOGICAL_ID_}" - echo "ASSETS_S3_PATH=${_ASSETS_S3_PATH_}" - echo "DATA_VOLUME_TYPE=${_DATA_VOLUME_TYPE_}" - echo "DATA_VOLUME_SIZE=${_DATA_VOLUME_SIZE_}" - echo "NETWORK_ID=${_NETWORK_ID_}" - - echo "HOME=/home/ubuntu" -} >> /etc/environment -source /etc/environment - -# Validate the release channel -case $NETWORK_ID in - mainnet|testnet|devnet) - ;; - *) - echo "Invalid NETWORK_ID. Please use mainnet, testnet, or devnet." - exit - ;; -esac - - -# Updating packages -echo "[LOG] updating packages" -export DEBIAN_FRONTEND=noninteractive -sudo apt-get -qq update && sudo apt upgrade -y -sudo apt-get -qq install -y build-essential -sudo apt-get -qq install -y libclang-dev pkg-config libssl-dev -sudo apt-get -qq install -y awscli jq unzip -sudo apt-get -qq install -y libpq-dev # dependency of sui-tool - -# emitting cfn-signal event -sudo apt-get -qq install -y python3-pip -sudo pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz -cfn-signal --stack $STACK_NAME --resource $RESOURCE_ID --region $AWS_REGION - -# Check if GCC is installed silently -if ! command -v gcc &> /dev/null; then - exit 1 -fi - -# Create symlink for cc to gcc if not exists -if ! command -v cc &> /dev/null; then - sudo ln -sf /usr/bin/gcc /usr/bin/cc -fi - -# Add /usr/bin to PATH if not already in PATH -if [[ ":$PATH:" != *":/usr/bin:"* ]]; then - export PATH=$PATH:/usr/bin - # Add to ~/.bashrc or ~/.bash_profile to make it permanent - echo 'export PATH=$PATH:/usr/bin' >> ~/.bashrc - source ~/.bashrc -fi - -# Install dependencies -echo "[LOG] Install dependencies" -sudo apt-get -qq install -y --no-install-recommends tzdata git ca-certificates curl cmake -sudo apt-get -qq install -y libprotobuf-dev protobuf-compiler - - -#### Mounting volume #### - -echo "Waiting for volumes to be available" -sleep 60 - -# Define base mount point directory -MOUNT_POINT_BASE="/data" - -# Create the base mount point directory if it doesn't exist -if [ ! -d "$MOUNT_POINT_BASE" ]; then - echo "Creating mount point $MOUNT_POINT_BASE" - sudo mkdir -p "$MOUNT_POINT_BASE" -fi - -# Specify the volume name -volume="/dev/nvme1n1" - -# Create an ext4 filesystem on the volume -echo "Creating ext4 filesystem on $volume" -sudo mkfs -t ext4 -F $volume - -# Get the UUID of the volume -UUID=$(sudo blkid -s UUID -o value $volume) - -# Add the volume to /etc/fstab if it's not already there -if ! grep -q "$UUID" /etc/fstab; then - echo "Adding $volume to /etc/fstab" - echo "UUID=$UUID $MOUNT_POINT_BASE ext4 defaults,nofail 0 2" | sudo tee -a /etc/fstab -else - echo "Volume $volume is already in /etc/fstab" -fi - -# Mount the volume to the base mount point -echo "Mounting $volume to $MOUNT_POINT_BASE" -sudo mount $volume $MOUNT_POINT_BASE - -# Change ownership to the 'ubuntu' user -echo "Changing ownership of $MOUNT_POINT_BASE to ubuntu:ubuntu" -sudo chown ubuntu:ubuntu -R $MOUNT_POINT_BASE - -# Switch to the large disk -cd $MOUNT_POINT_BASE - -echo "Volume $volume is mounted and ready to use at $MOUNT_POINT_BASE" - -#### End of mounting EBS #### - - -# Download Assets -cd /opt # -echo "Downloading assets zip file" -aws s3 cp $ASSETS_S3_PATH ./assets.zip -unzip -q assets.zip - -echo "Install and configure CloudWatch agent" -wget -q https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb -dpkg -i -E amazon-cloudwatch-agent.deb - -echo 'Configuring CloudWatch Agent' -mkdir -p /opt/aws/amazon-cloudwatch-agent/etc/ -cp /opt/cw-agent.json /opt/aws/amazon-cloudwatch-agent/etc/custom-amazon-cloudwatch-agent.json # TODO: TEST AGAIN - copy from assets - -echo "Starting CloudWatch Agent" -/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ --a fetch-config -c file:/opt/aws/amazon-cloudwatch-agent/etc/custom-amazon-cloudwatch-agent.json -m ec2 -s -systemctl status amazon-cloudwatch-agent - - - -# Download Sui artifacts -cd $HOME - -download_sui_binaries() { - echo "Fetching tags for ${!NETWORK_ID}..." - - # test with export - - # Fetch tags using git ls-remote and filter based on the channel - LATEST_TAG=$(git ls-remote --tags --refs https://github.com/MystenLabs/sui.git | - awk '{print $2}' | - sed 's/^refs\/tags\///' | - grep "^${!NETWORK_ID}-" | - sort -V | - tail -n 1) - - if [ -z "$!LATEST_TAG" ]; then - echo "No matching tag found for ${!NETWORK_ID}" - exit 1 - fi - - echo "Latest ${!NETWORK_ID} tag: ${!LATEST_TAG}" - - # Construct the download URL - DOWNLOAD_URL="https://github.com/MystenLabs/sui/releases/download/${!LATEST_TAG}/sui-${!LATEST_TAG}-ubuntu-x86_64.tgz" - - echo "Downloading from: ${!DOWNLOAD_URL}" - - # Download the tar.gz file - curl -L -o "sui-${!LATEST_TAG}-ubuntu-x86_64.tgz" "${!DOWNLOAD_URL}" - - if [ $? -ne 0 ]; then - echo "Failed to download the file" - exit 1 - fi - - echo "Download complete. Unpacking..." - - # Unpack the tar.gz file - tar -xzvf "sui-${!LATEST_TAG}-ubuntu-x86_64.tgz" - - if [ $? -ne 0 ]; then - echo "Failed to unpack the file" - exit 1 - fi - - echo "Unpacking complete. Cleaning up..." - - # Remove the tar.gz file - rm "sui-${!LATEST_TAG}-ubuntu-x86_64.tgz" - - echo "Done! Sui ${!NETWORK_ID} release ${!LATEST_TAG} has been downloaded and unpacked." - -} - -# Executing function that downloads official binaries from github -download_sui_binaries -sudo cp ./sui-node /usr/local/bin/ -sudo cp ./sui /usr/local/bin/ -sudo cp ./sui-tool /usr/local/bin/ - -# Update Configs -echo "[LOG] update configs" -mkdir -p $HOME/.sui/ -cd $HOME/.sui/ - -# Genesis for release channel (testnet|mainnet|devnet) -wget -O genesis.blob https://github.com/MystenLabs/sui-genesis/raw/main/$NETWORK_ID/genesis.blob - -wget -O $HOME/.sui/fullnode.yaml https://raw.githubusercontent.com/MystenLabs/sui/$NETWORK_ID/crates/sui-config/data/fullnode-template.yaml -sed -i 's/127.0.0.1/0.0.0.0/' $HOME/.sui/fullnode.yaml -sed -i "s|db-path:.*|db-path: $MOUNT_POINT_BASE/sui/db|g" $HOME/.sui/fullnode.yaml -sed -i "s|genesis-file-location:.*|genesis-file-location: $HOME/.sui/genesis.blob|g" $HOME/.sui/fullnode.yaml - -# Define the path to the configuration file -echo "[LOG] Adding p2p peers to corresponding release channel" - -# Check the value of _NETWORK_ID_ and append the appropriate P2P configuration -case "$NETWORK_ID" in - "mainnet") - echo "Adding mainnet peer configuration" - cat << EOF >> $HOME/.sui/fullnode.yaml - -p2p-config: - seed-peers: - - address: /dns/mel-00.mainnet.sui.io/udp/8084 - peer-id: d32b55bdf1737ec415df8c88b3bf91e194b59ee3127e3f38ea46fd88ba2e7849 - - address: /dns/ewr-00.mainnet.sui.io/udp/8084 - peer-id: c7bf6cb93ca8fdda655c47ebb85ace28e6931464564332bf63e27e90199c50ee - - address: /dns/ewr-01.mainnet.sui.io/udp/8084 - peer-id: 3227f8a05f0faa1a197c075d31135a366a1c6f3d4872cb8af66c14dea3e0eb66 - - address: /dns/lhr-00.mainnet.sui.io/udp/8084 - peer-id: c619a5e0f8f36eac45118c1f8bda28f0f508e2839042781f1d4a9818043f732c - - address: /dns/sui-mainnet-ssfn-1.nodeinfra.com/udp/8084 - peer-id: 0c52ca8d2b9f51be4a50eb44ace863c05aadc940a7bd15d4d3f498deb81d7fc6 - - address: /dns/sui-mainnet-ssfn-2.nodeinfra.com/udp/8084 - peer-id: 1dbc28c105aa7eb9d1d3ac07ae663ea638d91f2b99c076a52bbded296bd3ed5c - - address: /dns/sui-mainnet-ssfn-ashburn-na.overclock.run/udp/8084 - peer-id: 5ff8461ab527a8f241767b268c7aaf24d0312c7b923913dd3c11ee67ef181e45 - - address: /dns/sui-mainnet-ssfn-dallas-na.overclock.run/udp/8084 - peer-id: e1a4f40d66f1c89559a195352ba9ff84aec28abab1d3aa1c491901a252acefa6 - - address: /dns/ssn01.mainnet.sui.rpcpool.com/udp/8084 - peer-id: fadb7ccb0b7fc99223419176e707f5122fef4ea686eb8e80d1778588bf5a0bcd - - address: /dns/ssn02.mainnet.sui.rpcpool.com/udp/8084 - peer-id: 13783584a90025b87d4604f1991252221e5fd88cab40001642f4b00111ae9b7e - -EOF - ;; - "testnet") - echo "Adding testnet peer configuration" - cat << EOF >> $HOME/.sui/fullnode.yaml - -p2p-config: - seed-peers: - - address: /dns/yto-tnt-ssfn-01.testnet.sui.io/udp/8084 - peer-id: 2ed53564d5581ded9b6773970ac2f1c84d39f9edf01308ff5a1ffe09b1add7b3 - - address: /dns/yto-tnt-ssfn-00.testnet.sui.io/udp/8084 - peer-id: 6563732e5ab33b4ae09c73a98fd37499b71b8f03c27b5cc51acc26934974aff2 - - address: /dns/nrt-tnt-ssfn-00.testnet.sui.io/udp/8084 - peer-id: 23a1f7cd901b6277cbedaa986b3fc183f171d800cabba863d48f698f518967e1 - - address: /dns/ewr-tnt-ssfn-00.testnet.sui.io/udp/8084 - peer-id: df8a8d128051c249e224f95fcc463f518a0ebed8986bbdcc11ed751181fecd38 - - address: /dns/lax-tnt-ssfn-00.testnet.sui.io/udp/8084 - peer-id: f9a72a0a6c17eed09c27898eab389add704777c03e135846da2428f516a0c11d - - address: /dns/lhr-tnt-ssfn-00.testnet.sui.io/udp/8084 - peer-id: 9393d6056bb9c9d8475a3cf3525c747257f17c6a698a7062cbbd1875bc6ef71e - - address: /dns/mel-tnt-ssfn-00.testnet.sui.io/udp/8084 - peer-id: c88742f46e66a11cb8c84aca488065661401ef66f726cb9afeb8a5786d83456e - -EOF - ;; -esac - -# Make Sui Service -echo "[LOG] make Sui service" - -sudo tee /etc/systemd/system/suid.service > /dev/null < /dev/null <&2 - exit 1 -fi - -# Start fullnode -sudo systemctl start suid diff --git a/lib/sui/lib/common-stack.ts b/lib/sui/lib/common-stack.ts deleted file mode 100644 index 7e6f0541..00000000 --- a/lib/sui/lib/common-stack.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as iam from 'aws-cdk-lib/aws-iam'; -import * as nag from "cdk-nag"; - -export interface SuiCommonStackProps extends cdk.StackProps { - -} - -export class SuiCommonStack extends cdk.Stack { - AWS_STACKNAME = cdk.Stack.of(this).stackName; - AWS_ACCOUNT_ID = cdk.Stack.of(this).account; - - constructor(scope: cdkConstructs.Construct, id: string, props: SuiCommonStackProps) { - super(scope, id, props); - - const region = cdk.Stack.of(this).region; - - const instanceRole = new iam.Role(this, `node-role`, { - assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com"), - managedPolicies: [ - iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMManagedInstanceCore"), - iam.ManagedPolicy.fromAwsManagedPolicyName("CloudWatchAgentServerPolicy"), - ], - }); - - instanceRole.addToPolicy(new iam.PolicyStatement({ - // Can't target specific stack: https://github.com/aws/aws-cdk/issues/22657 - resources: ["*"], - actions: ["cloudformation:SignalResource"], - })); - - instanceRole.addToPolicy(new iam.PolicyStatement({ - resources: [`arn:aws:autoscaling:${region}:${this.AWS_ACCOUNT_ID}:autoScalingGroup:*:autoScalingGroupName/sui-*`], - actions: ["autoscaling:CompleteLifecycleAction"], - })); - - new cdk.CfnOutput(this, "Instance Role ARN", { - value: instanceRole.roleArn, - exportName: "SuiNodeInstanceRoleArn", - }); - - /** - * cdk-nag suppressions - */ - - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-IAM4", - reason: "AmazonSSMManagedInstanceCore and CloudWatchAgentServerPolicy are restrictive enough", - }, - { - id: "AwsSolutions-IAM5", - reason: "Can't target specific stack: https://github.com/aws/aws-cdk/issues/22657", - }, - ], - true - ); - } -} diff --git a/lib/sui/lib/config/suiConfig.interface.ts b/lib/sui/lib/config/suiConfig.interface.ts deleted file mode 100644 index 9762586c..00000000 --- a/lib/sui/lib/config/suiConfig.interface.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as configTypes from "../../../constructs/config.interface"; - -export type SuiNetworkId = "mainnet" | "testnet" | "devnet"; -export type SuiL1Endpoint = string; - -export interface SuiDataVolumeConfig extends configTypes.DataVolumeConfig { -} - -export interface SuiAccountsVolumeConfig extends configTypes.DataVolumeConfig { -} - -export interface SuiBaseConfig extends configTypes.BaseConfig { -} - -export interface SuiBaseNodeConfig extends configTypes.BaseNodeConfig { - suiNetworkId: SuiNetworkId; - dataVolume: SuiDataVolumeConfig; -} diff --git a/lib/sui/lib/config/suiConfig.ts b/lib/sui/lib/config/suiConfig.ts deleted file mode 100644 index 88bd2e23..00000000 --- a/lib/sui/lib/config/suiConfig.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as configTypes from "./suiConfig.interface"; -import * as constants from "../../../constructs/constants"; - - -const parseDataVolumeType = (dataVolumeType: string) => { - switch (dataVolumeType) { - case "gp3": - return ec2.EbsDeviceVolumeType.GP3; - case "io2": - return ec2.EbsDeviceVolumeType.IO2; - case "io1": - return ec2.EbsDeviceVolumeType.IO1; - case "instance-store": - return constants.InstanceStoreageDeviceVolumeType; - default: - return ec2.EbsDeviceVolumeType.GP3; - } -} - -export const baseConfig: configTypes.SuiBaseConfig = { - accountId: process.env.AWS_ACCOUNT_ID || "458259478447", - region: process.env.AWS_REGION || "us-east-1", -} - -export const baseNodeConfig: configTypes.SuiBaseNodeConfig = { - instanceType: new ec2.InstanceType(process.env.SUI_INSTANCE_TYPE ? process.env.SUI_INSTANCE_TYPE : "m6i.4xlarge"), - instanceCpuType: ec2.AmazonLinuxCpuType.X86_64, - suiNetworkId: process.env.SUI_NETWORK_ID || "testnet", - dataVolume: { - sizeGiB: process.env.SUI_DATA_VOL_SIZE ? parseInt(process.env.SUI_DATA_VOL_SIZE): 4000, - type: parseDataVolumeType(process.env.SUI_DATA_VOL_TYPE?.toLowerCase() ? process.env.SUI_DATA_VOL_TYPE?.toLowerCase() : "gp3"), - iops: process.env.SUI_DATA_VOL_IOPS ? parseInt(process.env.SUI_DATA_VOL_IOPS): 3000, - throughput: process.env.SUI_DATA_VOL_THROUGHPUT ? parseInt(process.env.SUI_DATA_VOL_THROUGHPUT): 700, - }, -}; diff --git a/lib/sui/lib/constructs/node-cw-dashboard.ts b/lib/sui/lib/constructs/node-cw-dashboard.ts deleted file mode 100644 index 7ee2f7e5..00000000 --- a/lib/sui/lib/constructs/node-cw-dashboard.ts +++ /dev/null @@ -1,235 +0,0 @@ -export const SyncNodeCWDashboardJSON = { - "widgets": [ - { - "height": 5, - "width": 6, - "y": 0, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "CPUUtilization", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "CPU utilization (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 18, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "NetworkIn", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Network in (bytes)" - } - }, - { - "height": 5, - "width": 6, - "y": 0, - "x": 18, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "NetworkOut", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Network out (bytes)" - } - }, - { - "height": 5, - "width": 6, - "y": 10, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 300, - "metrics": [ - [ "CWAgent", "mem_used_percent", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Mem Used (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 300, - "metrics": [ - [ "CWAgent", "cpu_usage_iowait", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "CPU Usage IO wait (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 0, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ { "expression": "m7/PERIOD(m7)", "label": "Read", "id": "e7" } ], - [ "CWAgent", "diskio_reads", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7", "visible": false, "stat": "Sum", "period": 60 } ], - [ { "expression": "m8/PERIOD(m8)", "label": "Write", "id": "e8" } ], - [ "CWAgent", "diskio_writes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m8", "visible": false, "stat": "Sum", "period": 60 } ] - ], - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Sum", - "period": 60, - "title": "nvme1n1 Volume Read/Write (IO/sec)" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 12, - "type": "metric", - "properties": { - "metrics": [ - [ "CWAgent", "sui_current_block", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "sparkline": true, - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Maximum", - "period": 60, - "title": "Sui Client Block Height" - } - }, - { - "height": 5, - "width": 6, - "y": 10, - "x": 12, - "type": "metric", - "properties": { - "sparkline": true, - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Maximum", - "period": 60, - "metrics": [ - [ "CWAgent", "sui_blocks_behind", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Sui Client Blocks Behind" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 6, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Sum", - "period": 60, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ { "expression": "IF(m7_2 !=0, (m7_1 / m7_2), 0)", "label": "Read", "id": "e7" } ], - [ "CWAgent", "diskio_read_time", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_1", "visible": false, "stat": "Sum", "period": 60 } ], - [ "CWAgent", "diskio_reads", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_2", "visible": false, "stat": "Sum", "period": 60 } ], - [ { "expression": "IF(m7_4 !=0, (m7_3 / m7_4), 0)", "label": "Write", "id": "e8" } ], - [ "CWAgent", "diskio_write_time", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_3", "visible": false, "stat": "Sum", "period": 60 } ], - [ "CWAgent", "diskio_writes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_4", "visible": false, "stat": "Sum", "period": 60 } ] - ], - "title": "nvme1n1 Volume Read/Write latency (ms/op)" - } - }, - { - "height": 5, - "width": 6, - "y": 10, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ { "expression": "(m2/1048576)/PERIOD(m2)", "label": "Read", "id": "e2", "period": 60, "region": "${REGION}" } ], - [ "CWAgent", "diskio_read_bytes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m2", "stat": "Sum", "visible": false, "period": 60 } ], - [ { "expression": "(m3/1048576)/PERIOD(m3)", "label": "Write", "id": "e3", "period": 60, "region": "${REGION}" } ], - [ "CWAgent", "diskio_write_bytes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m3", "stat": "Sum", "visible": false, "period": 60 } ] - ], - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 60, - "title": "nvme1n1 Volume Read/Write throughput (MiB/sec)" - } - }, - { - "height": 5, - "width": 6, - "y": 0, - "x": 12, - "type": "metric", - "properties": { - "metrics": [ - [ "CWAgent", "disk_used_percent", "path", "/data", "InstanceId", "${INSTANCE_ID}", "device", "nvme1n1", "fstype", "ext4", { "region": "${REGION}", "label": "/data" } ] - ], - "sparkline": true, - "view": "singleValue", - "region": "${REGION}", - "title": "nvme1n1 Disk Used (%)", - "period": 60, - "stat": "Average" - } - } - ] -} diff --git a/lib/sui/lib/constructs/sui-node-security-group.ts b/lib/sui/lib/constructs/sui-node-security-group.ts deleted file mode 100644 index 1228db97..00000000 --- a/lib/sui/lib/constructs/sui-node-security-group.ts +++ /dev/null @@ -1,49 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from 'constructs'; -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as nag from "cdk-nag"; - -export interface SuiNodeSecurityGroupConstructProps { - vpc: cdk.aws_ec2.IVpc; - } - -export class SuiNodeSecurityGroupConstruct extends cdkConstructs.Construct { - public securityGroup: cdk.aws_ec2.ISecurityGroup; - - constructor(scope: cdkConstructs.Construct, id: string, props: SuiNodeSecurityGroupConstructProps) { - super(scope, id); - - const { - vpc, - } = props; - - const sg = new ec2.SecurityGroup(this, `rpc-node-security-group`, { - vpc, - description: "Security Group for Blockchain nodes", - allowAllOutbound: true, - }); - - // Private port - sg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.udp(8084), "Sui P2P"); - sg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(9184), "Sui Metrics"); - sg.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(9000), "JSON-RPC"); - sg.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(22), "SSH"); - - this.securityGroup = sg - - /** - * cdk-nag suppressions - */ - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-EC23", - reason: "Sui requires wildcard inbound for specific ports", - }, - ], - true - ); - - } -} diff --git a/lib/sui/lib/single-node-stack.ts b/lib/sui/lib/single-node-stack.ts deleted file mode 100644 index a31d8fa1..00000000 --- a/lib/sui/lib/single-node-stack.ts +++ /dev/null @@ -1,136 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as iam from "aws-cdk-lib/aws-iam"; -import * as s3Assets from "aws-cdk-lib/aws-s3-assets"; -import * as path from "path"; -import * as fs from "fs"; -import * as nodeCwDashboard from "./constructs/node-cw-dashboard" -import * as cw from 'aws-cdk-lib/aws-cloudwatch'; -import * as nag from "cdk-nag"; -import { SingleNodeConstruct } from "../../constructs/single-node" -import * as configTypes from "./config/suiConfig.interface"; -import * as constants from "../../constructs/constants"; -import { SuiNodeSecurityGroupConstruct } from "./constructs/sui-node-security-group"; - -export interface SuiSingleNodeStackProps extends cdk.StackProps { - instanceType: ec2.InstanceType; - instanceCpuType: ec2.AmazonLinuxCpuType; - suiNetworkId: configTypes.SuiNetworkId; - dataVolume: configTypes.SuiDataVolumeConfig; -} - -export class SuiSingleNodeStack extends cdk.Stack { - constructor(scope: cdkConstructs.Construct, id: string, props: SuiSingleNodeStackProps) { - super(scope, id, props); - - // Setting up necessary environment variables - const REGION = cdk.Stack.of(this).region; - const STACK_NAME = cdk.Stack.of(this).stackName; - const STACK_ID = cdk.Stack.of(this).stackId; - const availabilityZones = cdk.Stack.of(this).availabilityZones; - const chosenAvailabilityZone = availabilityZones.slice(0, 1)[0]; - - // Getting our config from initialization properties - const { - instanceType, - instanceCpuType, - suiNetworkId, - dataVolume, - } = props; - - // Using default VPC - const vpc = ec2.Vpc.fromLookup(this, "vpc", { isDefault: true }); - - // Setting up the security group for the node from Sui-specific construct - const instanceSG = new SuiNodeSecurityGroupConstruct (this, "security-group", { - vpc: vpc, - }) - - // Making our scripts and configis from the local "assets" directory available for instance to download - const asset = new s3Assets.Asset(this, "assets", { - path: path.join(__dirname, "assets"), - }); - - // Getting the IAM role ARN from the common stack - const importedInstanceRoleArn = cdk.Fn.importValue("SuiNodeInstanceRoleArn"); - - const instanceRole = iam.Role.fromRoleArn(this, "iam-role", importedInstanceRoleArn); - - // Making sure our instance will be able to read the assets - asset.bucket.grantRead(instanceRole); - - // Setting up the node using generic Single Node constract - if (instanceCpuType === ec2.AmazonLinuxCpuType.ARM_64) { - throw new Error("ARM_64 is not yet supported"); - } - - // Use Ubuntu 20.04 LTS image for amd64. Find more: https://discourse.ubuntu.com/t/finding-ubuntu-images-with-the-aws-ssm-parameter-store/15507 - const ubuntu204stableImageSsmName = "/aws/service/canonical/ubuntu/server/20.04/stable/current/amd64/hvm/ebs-gp2/ami-id" - - const node = new SingleNodeConstruct(this, "fullnode", { - instanceName: STACK_NAME, - instanceType, - dataVolumes: [dataVolume], - rootDataVolumeDeviceName: "/dev/sda1", - machineImage: ec2.MachineImage.fromSsmParameter(ubuntu204stableImageSsmName), - vpc, - availabilityZone: chosenAvailabilityZone, - role: instanceRole, - securityGroup: instanceSG.securityGroup, - vpcSubnets: { - subnetType: ec2.SubnetType.PUBLIC, - }, - }); - - // Assuming the SingleNodeConstruct exposes the EC2 instance as a property named 'instance' - // Set a CreationPolicy with a 30-minute timeout - - // Parsing user data script and injecting necessary variables - const nodeStartScript = fs.readFileSync(path.join(__dirname, "assets", "user-data", "node.sh")).toString(); - const dataVolumeSizeBytes = dataVolume.sizeGiB * constants.GibibytesToBytesConversionCoefficient; - - const modifiedInitNodeScript = cdk.Fn.sub(nodeStartScript, { - _AWS_REGION_: REGION, - _ASSETS_S3_PATH_: `s3://${asset.s3BucketName}/${asset.s3ObjectKey}`, - _STACK_NAME_: STACK_NAME, - _STACK_ID_: STACK_ID, - _NODE_CF_LOGICAL_ID_: node.nodeCFLogicalId, - _DATA_VOLUME_TYPE_: dataVolume.type, - _DATA_VOLUME_SIZE_: dataVolumeSizeBytes.toString(), - _NETWORK_ID_: suiNetworkId, - }); - - // /var/log/cloud-init.log and - // /var/log/cloud-init-output.log - node.instance.addUserData(modifiedInitNodeScript); - - // Adding CloudWatch dashboard to the node - const dashboardString = cdk.Fn.sub(JSON.stringify(nodeCwDashboard.SyncNodeCWDashboardJSON), { - INSTANCE_ID:node.instanceId, - INSTANCE_NAME: STACK_NAME, - REGION: REGION, - }) - - new cw.CfnDashboard(this, 'sui-cw-dashboard', { - dashboardName: STACK_NAME, - dashboardBody: dashboardString, - }); - - new cdk.CfnOutput(this, "node-instance-id", { - value: node.instanceId, - }); - - // Adding suppressions to the stack - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-IAM5", - reason: "Need read access to the S3 bucket with assets", - }, - ], - true - ); - } -} diff --git a/lib/sui/package-lock.json b/lib/sui/package-lock.json deleted file mode 100644 index 796a6ee0..00000000 --- a/lib/sui/package-lock.json +++ /dev/null @@ -1,424 +0,0 @@ -{ - "name": "aws-blockchain-node-runners-sui", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "aws-blockchain-node-runners-sui", - "version": "0.1.0", - "dependencies": { - "aws-cdk-lib": "^2.148.0", - "cdk-nag": "^2.28.157", - "dotenv": "^16.4.5" - }, - "bin": { - "sui": "app.ts" - }, - "devDependencies": { - "@types/node": "^20.14.10" - } - }, - "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.202", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz", - "integrity": "sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg==" - }, - "node_modules/@aws-cdk/asset-kubectl-v20": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", - "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==" - }, - "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.3.tgz", - "integrity": "sha512-twhuEG+JPOYCYPx/xy5uH2+VUsIEhPTzDY0F1KuB+ocjWWB/KEDiOVL19nHvbPCB6fhWnkykXEMJ4HHcKvjtvg==" - }, - "node_modules/@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/aws-cdk-lib": { - "version": "2.148.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.148.0.tgz", - "integrity": "sha512-Pa0pyIHlhnsqtMkPJS3tnptYhoOSNDOgoFurNB4Qfa0vnAkjYQ+JKQkR1tNNr8+UtO9jUfXRklQgjEqlFlrgBA==", - "bundleDependencies": [ - "@balena/dockerignore", - "case", - "fs-extra", - "ignore", - "jsonschema", - "minimatch", - "punycode", - "semver", - "table", - "yaml", - "mime-types" - ], - "dependencies": { - "@aws-cdk/asset-awscli-v1": "^2.2.202", - "@aws-cdk/asset-kubectl-v20": "^2.1.2", - "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.3", - "@balena/dockerignore": "^1.0.2", - "case": "1.6.3", - "fs-extra": "^11.2.0", - "ignore": "^5.3.1", - "jsonschema": "^1.4.1", - "mime-types": "^2.1.35", - "minimatch": "^3.1.2", - "punycode": "^2.3.1", - "semver": "^7.6.2", - "table": "^6.8.2", - "yaml": "1.10.2" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "constructs": "^10.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { - "version": "1.0.2", - "inBundle": true, - "license": "Apache-2.0" - }, - "node_modules/aws-cdk-lib/node_modules/ajv": { - "version": "8.16.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/aws-cdk-lib/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/ansi-styles": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/aws-cdk-lib/node_modules/astral-regex": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/balanced-match": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/aws-cdk-lib/node_modules/case": { - "version": "1.6.3", - "inBundle": true, - "license": "(MIT OR GPL-3.0-or-later)", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/color-convert": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/color-name": { - "version": "1.1.4", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/concat-map": { - "version": "0.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/emoji-regex": { - "version": "8.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { - "version": "3.1.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/fs-extra": { - "version": "11.2.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/aws-cdk-lib/node_modules/graceful-fs": { - "version": "4.2.11", - "inBundle": true, - "license": "ISC" - }, - "node_modules/aws-cdk-lib/node_modules/ignore": { - "version": "5.3.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/jsonfile": { - "version": "6.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/aws-cdk-lib/node_modules/jsonschema": { - "version": "1.4.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { - "version": "4.4.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/mime-db": { - "version": "1.52.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/aws-cdk-lib/node_modules/mime-types": { - "version": "2.1.35", - "inBundle": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/aws-cdk-lib/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/aws-cdk-lib/node_modules/punycode": { - "version": "2.3.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/aws-cdk-lib/node_modules/require-from-string": { - "version": "2.0.2", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/semver": { - "version": "7.6.2", - "inBundle": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/aws-cdk-lib/node_modules/slice-ansi": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/aws-cdk-lib/node_modules/string-width": { - "version": "4.2.3", - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/table": { - "version": "6.8.2", - "inBundle": true, - "license": "BSD-3-Clause", - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/universalify": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/uri-js": { - "version": "4.4.1", - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/yaml": { - "version": "1.10.2", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/cdk-nag": { - "version": "2.28.157", - "resolved": "https://registry.npmjs.org/cdk-nag/-/cdk-nag-2.28.157.tgz", - "integrity": "sha512-5nwOEq5bXMl1Hfe4ig1JNtECVM2jt6ISO8kZq5eq6YflLp2YLrhkAoDWSdyEDDo0l30uDLGSl9sxxkdR39l5gQ==", - "peerDependencies": { - "aws-cdk-lib": "^2.116.0", - "constructs": "^10.0.5" - } - }, - "node_modules/constructs": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.3.0.tgz", - "integrity": "sha512-vbK8i3rIb/xwZxSpTjz3SagHn1qq9BChLEfy5Hf6fB3/2eFbrwt2n9kHwQcS0CPTRBesreeAcsJfMq2229FnbQ==", - "peer": true, - "engines": { - "node": ">= 16.14.0" - } - }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - } - } -} diff --git a/lib/sui/package.json b/lib/sui/package.json deleted file mode 100644 index b2fb3780..00000000 --- a/lib/sui/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "aws-blockchain-node-runners-sui", - "version": "0.1.0", - "bin": { - "sui": "app.ts" - }, - "scripts": { - "build": "tsc", - "watch": "tsc -w", - "test": "jest", - "cdk": "cdk", - "cdk_deploy_common": "cdk deploy sui-common", - "cdk_synth_single_node": "cdk synth sui-single-node", - "cdk_deploy_single_node": "cdk deploy sui-single-node", - "cdk_destroy_single_node": "cdk destroy sui-single-node" - } -} diff --git a/lib/sui/sample-configs/.env-sample-full b/lib/sui/sample-configs/.env-sample-full deleted file mode 100644 index 9a9e23f1..00000000 --- a/lib/sui/sample-configs/.env-sample-full +++ /dev/null @@ -1,16 +0,0 @@ -############################################################# -# Example configuration for Sui Node Runner app on AWS # -############################################################# -## Set the AWS account is and region for your environment ## -AWS_ACCOUNT_ID="" -AWS_REGION="us-east-1" # your region of choice - -## Common configuration parameters # -SUI_NETWORK_ID="testnet" # All options: "mainnet", "testnet" , "devnet" -SUI_INSTANCE_TYPE="m6i.4xlarge" - -# Data volume configuration -SUI_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" -SUI_DATA_VOL_SIZE="4000" # Current required data size to keep both smapshot archive and unarchived version of it -SUI_DATA_VOL_IOPS="3000" # Max IOPS for EBS volumes -SUI_DATA_VOL_THROUGHPUT="700" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2") diff --git a/lib/sui/test/.env-test b/lib/sui/test/.env-test deleted file mode 100644 index 5ca8cf77..00000000 --- a/lib/sui/test/.env-test +++ /dev/null @@ -1,19 +0,0 @@ -############################################################# -# Example configuration for Starknet nodes runner app on AWS # -############################################################# - -## Set the AWS account is and region for your environment ## -AWS_ACCOUNT_ID="xxxxxxxxxxx" -AWS_REGION="us-east-1" # Regions supported by Amazon Managed Blockchain Access Ethereum: https://docs.aws.amazon.com/general/latest/gr/managedblockchain.html#managedblockchain-access - -## Common configuration parameters ## -STARKNET_L1_ENDPOINT="wss://ethereum-rpc.publicnode.com" -STARKNET_NETWORK_ID="mainnet" # All options: "mainnet", "sepolia", "sepolia-integration" -STARKNET_NODE_VERSION="v0.11.7" # Current required version of Starknet - -STARKNET_INSTANCE_TYPE="m6a.2xlarge" -# Data volume configuration -STARKNET_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families -STARKNET_DATA_VOL_SIZE="250" # Current required data size to keep both smapshot archive and unarchived version of it -STARKNET_DATA_VOL_IOPS="3000" # Max IOPS for EBS volumes (not applicable for "instance-store") -STARKNET_DATA_VOL_THROUGHPUT="700" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") diff --git a/lib/sui/tsconfig.json b/lib/sui/tsconfig.json deleted file mode 100644 index ae2453f2..00000000 --- a/lib/sui/tsconfig.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": [ - "es2020", - "dom" - ], - "types": ["node", "jest"], - "declaration": true, - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": false, - "noUnusedParameters": false, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": false, - "inlineSourceMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictPropertyInitialization": false, - "typeRoots": [ - "../../node_modules/@types" - ] - }, - "exclude": [ - "node_modules", - "cdk.out" - ] -} diff --git a/lib/tezos/.gitignore b/lib/tezos/.gitignore deleted file mode 100644 index e142d083..00000000 --- a/lib/tezos/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -*.js -!jest.config.js -*.d.ts -node_modules - -# CDK asset staging directory -.cdk.staging -cdk.out -package-lock.json \ No newline at end of file diff --git a/lib/tezos/README.md b/lib/tezos/README.md deleted file mode 100644 index 040c3d22..00000000 --- a/lib/tezos/README.md +++ /dev/null @@ -1,207 +0,0 @@ -# Sample AWS Blockchain Node Runner app for Tezos Nodes - -| Contributed by | -|:--------------------:| -| [@AhGhanima](https://github.com/AhGhanima), [@chrisdotn](https://github.com/chrisdotn) | - -## Architecture Overview - -This blueprint has two options for running nodes. You can set up a single JSON RPC node or multiple nodes in highly-available setup. The details are below. - -### Single RPC node setup -![SingleNodeSetup](./doc/assets/Architecture-Single.png) - -This setup is for small scale PoC or development environments. It deploys a single EC2 instance with the tezos client. The RPC port is exposed only to internal IP range of the VPC, while P2P ports allow external access to keep the client synced. - -### Highly available setup -![Architecture](./doc/assets/Architecture-HA.png) - -1. An ongoing data synchronization process is configured with nodes in the Tezos network with a sync node and RPC nodes. -2. The sync node is used to create a copy of node's state data in Amazon S3 bucket. -3. When new RPC nodes are provisioned, they copy state data from Amazon S3 bucket to speed up the initial sync process. -4. Applications and smart contract development tools access highly available RPC nodes behind the Application Load Balancer. - - -## Solution Walkthrough - -### Setup Cloud9 - -We will use AWS Cloud9 to execute the subsequent commands. Follow the instructions in [Cloud9 Setup](../../docs/setup-cloud9.md) - -### Clone this repository and install dependencies - -```bash - git clone https://github.com/aws-samples/aws-blockchain-node-runners.git - cd aws-blockchain-node-runners - npm install -``` - -**NOTE:** In this tutorial we will set all major configuration through environment variables, but you also can modify parameters in `config/config.ts`. - -### Prepare to deploy nodes - -1. Make sure you are in the root directory of the cloned repository - -2. If you have deleted or don't have the default VPC, create default VPC - -```bash - aws ec2 create-default-vpc - ``` - - **NOTE:** You may see the following error if the default VPC already exists: `An error occurred (DefaultVpcAlreadyExists) when calling the CreateDefaultVpc operation: A Default VPC already exists for this account in this region.`. That means you can just continue with the following steps. - - **NOTE:** The default VPC must have at least two public subnets in different Availability Zones, and public subnet must set `Auto-assign public IPv4 address` to `YES` - -3. Configure your setup - -Create your own copy of `.env` file and edit it: -```bash - # Make sure you are in aws-blockchain-node-runners/lib/tezos - cd lib/tezos - pwd - cp ./sample-configs/.env-sample-full .env - nano .env -``` - **NOTE:** You can find more examples inside the `sample-configs` directory. - - -4. Deploy common components such as IAM role, and Amazon S3 bucket to store data snapshots - -```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/tezos - npx cdk deploy tz-common -``` - -### Option 1: Single RPC Node - -1. Deploy Single RPC Node - -```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/tezos - npx cdk deploy tz-single-node --json --outputs-file single-node-deploy.json -``` - **NOTE:** The default VPC must have at least two public subnets in different Availability Zones, and public subnet must set `Auto-assign public IPv4 address` to `YES`. - - The EC2 instance will deploy, initialize the node and start the first sync. In Cloudformation the instance will show as successful once the node is running. From that point it still takes a while until the node is synced to the blockchain. You can check the sync status with the REST call below in step 4. If the `curl cannot connect to the node on port 8732, then the node is still importing. Once that's done, the curl command works. - -2. After starting the node you need to wait for the inital syncronization process to finish. It may take from an hour to half a day depending on the the state of the network. You can use Amazon CloudWatch to track the progress. To see them: - - - Navigate to [CloudWatch service](https://console.aws.amazon.com/cloudwatch/) (make sure you are in the region you have specified for `AWS_REGION`) - - Open `Dashboards` and select `tz-single-node--` from the list of dashboards. - -4. Once the initial synchronization is done, you should be able to access the RPC API of that node from within the same VPC. The RPC port is not exposed to the Internet. Run the following query against the private IP of the single RPC node you deployed: - -```bash - INSTANCE_ID=$(cat single-node-deploy.json | jq -r '..|.singleinstanceid? | select(. != null)') - NODE_INTERNAL_IP=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID --query 'Reservations[*].Instances[*].PrivateIpAddress' --output text) - - # We query if the node is synced to main - curl http://$NODE_INTERNAL_IP:8732/chains/main/is_bootstrapped -``` - -The result should be like this (the actual balance might change): - -```javascript - {"bootstrapped":true,"sync_state":"synced"} -``` - -### Option 2: Highly Available RPC Nodes - -1. Deploy Snapshot Node - -```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/tezos - npx cdk deploy snapshot-node --json --outputs-file sync-node-deploy.json -``` - **NOTE:** The default VPC must have at least two public subnets in different Availability Zones, and public subnet must set `Auto-assign public IPv4 address` to `YES` - -2. After starting the node you need to wait for the inital syncronization process to finish. It may take from an hour to half a day depending the state of the network. You can use Amazon CloudWatch to track the progress. To see them: - - - Navigate to [CloudWatch service](https://console.aws.amazon.com/cloudwatch/) (make sure you are in the region you have specified for `AWS_REGION`) - - Open `Dashboards` and select `tz-snapshot-node--` from the list of dashboards. - -Once synchronization process is over, the script will automatically stop the client and copy all the contents of the `/data` directory to your snapshot S3 bucket. That may take from 30 minutes to about 2 hours. During the process on the dashboard you will see lower CPU and RAM utilization but high data disc throughput and outbound network traffic. The script will automatically start the clients after the process is done. - -Note: the snapshot backup process will automatically run ever day at midnight time of the time zone were the sync node runs. To change the schedule, modify `crontab` of the root user on the node's EC2 instance. - -3. Configure and deploy 2 RPC Nodes - -```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/tezos - npx cdk deploy tz-ha-nodes --json --outputs-file rpc-node-deploy.json -``` - -4. Give the new RPC nodes about an hour to initialize and then run the following query against the load balancer behind the RPC node created - -```bash - export RPC_ABL_URL=$(cat rpc-node-deploy.json | jq -r '..|.alburl? | select(. != null)') - echo $RPC_ABL_URL - - curl http://$RPC_ABL_URL:8732/chains/main/is_bootstrapped -``` - -The result should be like this: - -```javascript - {"bootstrapped":true,"sync_state":"synced"} -``` - - If the nodes are still starting and catching up with the chain, you will see the following repsonse: - -```HTML - - 503 Service Temporarily Unavailable - -

503 Service Temporarily Unavailable

- -``` - -**NOTE:** By default and for security reasons the load balancer is available only from within the default VPC in the region where it is deployed. It is not available from the Internet and is not open for external connections. Before opening it up please make sure you protect your RPC APIs. - -### Clearing up and undeploying everything - -1. Undeploy RPC Nodes, Sync Nodes and Common components - -```bash - # Setting the AWS account id and region in case local .env file is lost - export AWS_ACCOUNT_ID= - export AWS_REGION= - - pwd - # Make sure you are in aws-blockchain-node-runners/lib/tezos - - # Undeploy Single RPC Node - cdk destroy tz-single-node - - # Undeploy RPC Nodes - cdk destroy tz-ha-nodes - - # Undeploy Sync Node - cdk destroy tz-snapshot-node - - # You need to manually delete an s3 bucket with a name similar to 'tz-snapshots-$accountid-tz-nodes-common' on the console,firstly empty the bucket,secondly delete the bucket,and then execute - # Delete all common components like IAM role and Security Group - cdk destroy tz-common -``` - -2. Follow steps to delete the Cloud9 instance in [Cloud9 Setup](../../doc/setup-cloud9.md) - -### FAQ - -1. How to check the logs from the EC2 user-data script? - - **Note:** In this tutorial we chose not to use SSH and use Session Manager instead. That allows you to log all sessions in AWS CloudTrail to see who logged into the server and when. If you receive an error similar to `SessionManagerPlugin is not found`, [install Session Manager plugin for AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) - -```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/tezos - - export INSTANCE_ID=$(cat single-node-deploy.json | jq -r '..|.single-node-instance-id? | select(. != null)') - echo "INSTANCE_ID=" $INSTANCE_ID - aws ssm start-session --target $INSTANCE_ID --region $AWS_REGION - sudo cat /var/log/cloud-init-output.log -``` diff --git a/lib/tezos/app.ts b/lib/tezos/app.ts deleted file mode 100644 index c56385f8..00000000 --- a/lib/tezos/app.ts +++ /dev/null @@ -1,76 +0,0 @@ -import 'dotenv/config' -import "source-map-support/register"; -import * as cdk from "aws-cdk-lib"; -import * as config from "./lib/config/tzConfig"; -import * as configTypes from "./lib/config/tzConfig.interface"; -import { TzCommonStack } from "./lib/common-stack"; -import { TzSingleNodeStack } from "./lib/single-node-stack"; -import { TzHANodesStack } from "./lib/ha-nodes-stack"; -import * as nag from "cdk-nag"; -import { TzSnapshotNodeStack } from './lib/snapshot-node-stack'; - -const app = new cdk.App(); -cdk.Tags.of(app).add("Project", "AWS_TZ"); - -const commonStack = new TzCommonStack(app, "tz-common", { - stackName: `tz-nodes-common`, - env: { account: config.baseConfig.accountId, region: config.baseConfig.region } -}); - -const singleNodeStack = new TzSingleNodeStack(app, "tz-single-node", { - stackName: `tz-single-node-${config.baseNodeConfig.historyMode}-${config.baseNodeConfig.tzNetwork}`, - - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - nodeRole: "single-node", - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - tzNetwork: config.baseNodeConfig.tzNetwork, - historyMode: config.baseNodeConfig.historyMode, - snapshotsUrl:config.baseNodeConfig.snapshotsUrl, - octezDownloadUri: config.baseNodeConfig.octezDownloadUri, - downloadSnapshot: config.baseNodeConfig.downloadSnapshot == "true", - dataVolume: config.baseNodeConfig.dataVolume, -}); -singleNodeStack.addDependency(commonStack); - -const snapshotNode = new TzSnapshotNodeStack(app, "tz-snapshot-node", { - stackName: `tz-snapshot-node-${config.baseNodeConfig.historyMode}-${config.baseNodeConfig.tzNetwork}`, - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - nodeRole: "rpc-node", - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - tzNetwork: config.baseNodeConfig.tzNetwork, - historyMode: config.baseNodeConfig.historyMode, - snapshotsUrl:config.baseNodeConfig.snapshotsUrl, - octezDownloadUri: config.baseNodeConfig.octezDownloadUri, - downloadSnapshot: config.baseNodeConfig.downloadSnapshot == "true" -}) - -const haNodeStack = new TzHANodesStack(app, "tz-ha-nodes", { - stackName: `tz-ha-nodes-${config.baseNodeConfig.historyMode}-${config.baseNodeConfig.tzNetwork}`, - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - nodeRole: "rpc-node", - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - tzNetwork: config.baseNodeConfig.tzNetwork, - historyMode: config.baseNodeConfig.historyMode, - snapshotsUrl:config.baseNodeConfig.snapshotsUrl, - dataVolume: config.baseNodeConfig.dataVolume, - octezDownloadUri: config.baseNodeConfig.octezDownloadUri, - - albHealthCheckGracePeriodMin: config.haNodeConfig.albHealthCheckGracePeriodMin, - heartBeatDelayMin: config.haNodeConfig.heartBeatDelayMin, - numberOfNodes: config.haNodeConfig.numberOfNodes, - downloadSnapshot: config.baseNodeConfig.downloadSnapshot == "true" -}); -haNodeStack.addDependency(commonStack); -haNodeStack.addDependency(snapshotNode); - -// Security Check -cdk.Aspects.of(app).add( - new nag.AwsSolutionsChecks({ - verbose: false, - reports: true, - logIgnores: false - }) -); diff --git a/lib/tezos/cdk.json b/lib/tezos/cdk.json deleted file mode 100644 index 7714e8c2..00000000 --- a/lib/tezos/cdk.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "app": "npx ts-node --prefer-ts-exts app.ts", - "watch": { - "include": [ - "**" - ], - "exclude": [ - "README.md", - "cdk*.json", - "**/*.d.ts", - "**/*.js", - "tsconfig.json", - "package*.json", - "yarn.lock", - "node_modules", - "test" - ] - }, - "context": { - "@aws-cdk/aws-lambda:recognizeLayerVersion": true, - "@aws-cdk/core:checkSecretUsage": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ], - "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, - "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, - "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, - "@aws-cdk/aws-iam:minimizePolicies": true, - "@aws-cdk/core:validateSnapshotRemovalPolicy": true, - "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, - "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, - "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, - "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, - "@aws-cdk/core:enablePartitionLiterals": true, - "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, - "@aws-cdk/aws-iam:standardizedServicePrincipals": true, - "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, - "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, - "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, - "@aws-cdk/aws-route53-patters:useCertificate": true, - "@aws-cdk/customresources:installLatestAwsSdkDefault": false, - "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, - "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, - "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, - "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, - "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, - "@aws-cdk/aws-redshift:columnId": true, - "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, - "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, - "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, - "@aws-cdk/aws-kms:aliasNameRef": true, - "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, - "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, - "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true - } -} diff --git a/lib/tezos/doc/assets/Architecture-HA.png b/lib/tezos/doc/assets/Architecture-HA.png deleted file mode 100644 index 475311e8e64fe9d6663b217e893e0ed775de9bd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 107961 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�k&2ko2D=$y<+sAzA+K58ALa|%+E(9LYML7G(i5C@`r#Q z0%U3^g+U{Vf9I`dLZ|{XD;x*!T{&N~<_2XvJ4M^n3}IQ26%li<3@If48`K|QN1o|e zEPEO`|E39F5}*8#WL<08>@pdMo;!XxBw(c!k~|FOmyG*q^zyRo($vm-QK-U2IoYFH zp$7Jw$4sD1&U9Kuky?uf!uT+6hkoSvQ_UR#?UwPl=L3TdilQeM*`g6c8M!Aq_R+sE z2sq0&bX^GaK${R`iv%j+_z1NS`hemdQ_6rEUH)ZPr3*d{YGyjBUN3sB9r(uBOJ(Hf z>idCWIxu1B`zla&1A5A4k=rS1awHHsM2_brTejzAC%(a63QdG{99@khL} z?zZ=>W^-ku8vjanVfGSE0sygS@~0&85LJ??_A50}%;kAt0SubX6~25v6iYLxej{^U zZLlMP7j>S(7wFu>RVpLy&m`)c=dWMb`>9iXfMBVzn*xPplA;HQ2>=k)~`eX z`iD#Rxx6spEQw&6FQoZ;R2o(Jy1fu2*Oy5cEeXKl9_lRgPE5YA`;jkyEzZ{F4R@N# zJaL8{;g!TjLl$~I3PCMYQh=}SSdJ>J=wOO>{F0wEZ1txPlEOALL0ErtTPdr%T@rvm z3$-8Ic^;{LGojawxv6-|*v274xgVaH7ud*=AH$F$0g{?x_OsqHZy`d(m#4+36#(Y{ z19r-+G5w_atI+@xl#CmVi>vUWzwG%lMHjpnv9pu_duU?0!o-bq)7THQDGTV8HOV$^ z(?SS*1ka;!MfDVcdb3Q7f|`qCp5d8BkgOD)(Z@Rq%MH8 z<~h9+9I;9)aC5cDl|l*BHc}h`QOiL$s_Bj84g?8X$rQ(LB|z>IBK<*J>H>1Es&V6O zoi~s0w|^!VKUm5m%@%o^4;l`b+st=MG;7mQ-DzX$*<~$azHX{L%gVi5JW$##x&o6} zffRw0hgP{q@+Pzu{|H=I;p3yrhDHG6s1HMFDxdtat=0upMpKUJ`2rsqUkT~3YdwqH zN(tyP^DIyY3Wx8lM$3s`HpQ*Y>q2$8<~Cc7eVa?phZQUj>cA96=V#K}361*CUQfR} z453EQd~-TH>pI*6aiVC};U{i9m%f3@W%cDJPlm@V?Is zh{N&$W2-eRI(5n@Ks8a$x1Y+y#-jm}QrN^7xHt00qOKU;AD`+7oS;pilsYu8#ZB+A zu|7hr58!NInxnADM8#-93**~giAA6V5kvE39tI1EQLf^woRG8ql&qS{bHT3&T*k6s z!}^s4`@b{qEQ|xbF#~6>QmQ)c3chv-_G5O7@t%5GX_!hYP%7{s=MEe#Q^y z(G@ITG!Gx4LO9A~ZYTD`m?%~YHflnQb1Fcb{=TAl&)_2w=KvJ(expevt7+`M*wWQ! zc~9~_Rxi~njw@93dzHTMu*~eIm?zJhHFw>`AYJIz$0iFSRNUjD{rA8G^oVP>Oe zv$(wbHQinBg7X4*niTda$G9nw@a5IRpPLmKnR$0+JmWgqTS6C7vjS!KgUP;`; z$BuiN$euB?TQ&m*xy%j2oMd7|R$Q0rH(s>sDW~mcbm! zQik2UhxI$*z>hc$qDHzY@aqYW+|A>?fb50vyUVuvUvC6S-z_#9 z7kX8kW9uGKYGmc6S}?boqyr4?w8h|3k1qRIyVvyjjnf7<3xa&AA>bCo8=3co1s=j~Ky|-4|I_85c(OY{_uGF}r5GvwzbT>J{Y(hnr<;p29TOG- z#dV#!SYJ;rIv}=`h>?oo0zx&7@0(djU+m1^@k=Nfxp~{9jwp3>^r@D7*C8JCtno#t zNSv}x5csA##TkXGOoT;PUkduZ%1TaI_EzWMzYbjLbkGOxLXK@1^EQ@q$Sh~AQYx@O zt+BaKZC5gAfruVz6&x=~z^`dQ1J_(qeVv)mGR){RVCF7~*bpZXEdrJs)J)k8G?Gdb z0wvVdC!gkV3A4z8s<#%!gTYNJU(Ne#Y!=;`RU|;dH)o5nFa(Ro28SGLlRst;gCbSi z($8Icc2-7f>I03q);Z%_iI^}(YutlIaUoKPl(7)!jf6H4K>fbi$S?KhdWZA>EuXlV zeyeWB$OfFvr0uY$gwU%~v6Z~1o%$Cb>PdCk8ke7eAtP21CVOhT1FDi+iR}0Io2{9R z#pi3d8wsM>-l*xD5_M^*osyVCkYz_PFWa?c-0|bt`9CY?k|A%8n2dm-NZxoP4>bCy z7ctEQi&ndc%JP@H!mP3b6HnBB>PfzJbYQkFcPB8NW@{F2J_o?2S|2x14^x{m20E|3 z*X|=O%ojTK3#d?LUg$^$_8593Mm9ldF_CjVvm!B+Cek0@&iSEhZ%_Jsr z1P8>}ONHDJ%8+`nd-eOiJa)oJ`Q>U^n2x;wn=p!C1Hy39s)ZbIH_a<;^zvxTZ)2PT z=5NfpDtcnF7dF0-r%)ki3{K(>=6Ov*OFL_pTRtxUJ$pn}6fE2dwstVA@DxB+#ryjB zG@;xC-<8UHi3%($`!$PgAC9}AsnH<^TkDiw}=I8%z6#h zNIdh1cQ(fpvVU()_UyIR(X1Uh5O1^Wz6!JZmK<8r<#%ef_&~#@*}Z@Z&*;kFE=Zq{ z+50$Y8Vf}j07Dc1ry>au^oJ?1b@u)hK%#QZ>|kIDT4^Fh;A_msm~JC5%ZE_1?Bt1k z`;>=Zq(La_cKg=ZdrykZ8nw%q^9g#yw*DL)i()Zl_HzVAhA)+%w6dqyn-86V^TMf& zQ_LD5Il%(Y*IKTH6wpFpk$s!4cAf3MTowSHxn&^|4E8W6F^IJ&Djw%~eo0AkojH~# zdV>aNQGp|GLvvfwc!(CBesQtaav)`K^!#a~Yy@uF>8vYlG}xsiZLHrFYaKS2B^y`# z`i_LiD{%txuh?`xOP9Sf-S4<3WOgjxnK(FjoH{#6e{=lcygLJJushKW{nd-9o54OJ zUW&wmpD~vysmqQJ{*JDfKG3WV&xwYZ5!ZAo*NfjbpYZH1LcS$J9_-_9%brr-w8ARq zF1Wt!<7AI4zF%<28d-kP=Zhl+A@0!ZrjNKRwh}YY6MnQ=gNL`j3o-;5^54oKYJJf_ zAoOo=O(J4MDI>l4m@in}K)4amIw7o6-V#6{P~6bq8}6=oI%uK3;`YVM@|~bxfuM!P zh5YMWz1{7;Mg9*H9?l*vvTPv3_SrZHlFeRsP6iiB7DGXk4Gs%VUv{y%Ke*<@sm3kk zLJ$B>CR;tWsbsmV<2hqh0nO=ynFNF76bvU$2;L2Bnj})30a8i`Qk&9A`_jN9lmhcA z!E5w24#;p0+t9e0ZWDh^8L54m0z*_b6ifn1gjMm#VEC+k%=?8eOyujZHw=tHTA+5d zD?L!xy#^?pr#R;4=2JBESQ<2d*84bnHTZhsQ>x8882QAzErU<)8>NdxQYm?3UM8o% z(%_S@Xh_+JYH=4^`(c{x=_z99TJYyG*n!;fi1$~w7!iG+h9b1EdM_?1pm`)JqEXl6 zd;iU#m?K$gZf2+wbBJujcLVP*{avTV}E>Np%x1(|OPD{Xdx42$d>K?|C=N=wpHkKvk)#Jtc# z`{>C{yCw_c>4B0k;Qzb+KEHRNe+rZ(8Kj1>?tu3w(SgqbdJeUmZ%KmGq1_QxjB2$< zT4UOoVWld{)Xd=t?UX6#P%=uF8X`;)G|UH2pE4kf?Y2Zfq^0kL*5Kx05+K84iGF^c zSD=ODU%I~^$F$Ou?fb}>*MSU``>v1I=Iq?Z(mR%#oB0)Pbp;oEXO?S!X?(k@oW7OS z7BV1lbUm|>fB4DVW5A$~jTk;o9J0Xxbt|%Z^*nHm9ulb@j$crG9DOd)w;xBRV_Fs- z9hvR=wlmN@YW+t4I2tlArEG4%)FyVcDbchg9pP^7VOgz<@H7zIA!BuZNb(;oK#F!4 zeBy?^2LV2*7^@W8J8HYUYw3dqa*{%WKZcLjCuPj38PmSAyL8ks<*n)?CMS1Ld$lM% zN!Y&fzA3rn_9{^t#c=?#J2U;dz!KjCZa2i7nHw3($lYH=yvbkzxX)-NC{4 z0xXq-Ujifs+B{tu0b)fxSOHA$7Gps0NNMn^Z#oR&`Ye3EPVBSfNcfHWr2bSAfG)8o zwS)0zSyze2&K6w+yQb^wB;_}lt|}uAQYM=oA!UrnX4AVTp$Kw3j^g7_`LrxA$=6kG z#)&`pfWvq{Xr&bzdjx{R&M5{ib`x$Jx?bP|Uj^k$%IEGD15)wkcC$GKV0`NH+n`;N zJCb_0J5z0@4h8mav>V4N(^))h#kpOlnwxP9fk#38lHVF7rpR{_%(s4}TaP#>!tv6>aq(K>*DQ($VQIF5!ZmQ zU9$6Rp6BPYJM=$13wruEkwXcf&cef^PvB8k_hr*_4S9q63zV+3P?ZPz70pqjk2eHF zSgTyj#!Qm8JLtOuXlLixWhyBvHyf{=-P)O^So-i#yVbly@c1Frbd1;YBuLFuZ5 znO&%0)!_RG+=(i1r9w-QQA6uq2@jvn;W35hLU@*N<3@8r9#4;HqrS*yWj2j_%R2M# zf!(sw-Nj}!{feiXR8J9&ULkV8`9=@%0;W$URc<}htP*73oB$qq`2n<0;pHw!B@BGi zBxM^MxVKe826y`WSjpmexLCuZcoa9s!Kolz?C~vMJ+okwEpk2QY&GZ`*1+6cQmVnb z5&Ihf=b{#v?U$~RonJF<>Z8t%ZGBppA;aM3hl87|(<#Z358&fvr|z_f3|ytq2)6W} zU}7j4JFs!}Y!}C0O!#34aySoAQm-jKJ+T)OVU@9$LhOY%j;yX)UUu3G2n(Lpjd~J? z=I+Yl8W!ORe2T}qK)VC}Ooax00A;LJb<>ao8Ge$0Aju57-i$n_sj;xyP^yZ1T+DqK zCZy;|jQn=TDiF|rqMsb=sKRu3t*tUm#@%@_5l-=6E); z8@qKhcBEES=MiwHF@ic;0BCW9Xz*oX=^ z$y^1iTx72q?a0u1T%1d36L)GshLs(5jUGFEJ8MP}UjnvxoSIyA?4zpV+jkm)QYa5z z=zUBPkucQRD4kZ=*DD%a{PteqD_FM}@2*s^npN?skc=;~rJfqkZp!wp-Ti?;qv?I~ z-LpVSx9S&V6@`voPY|W_U4aU~N`(G#b}6ar?igG?-rAH7_(*N4+^2|7z-`)orRJH| z=umyZAl-l$#x~u^m{%~H4E<9^#G`(O2|0;JNM#5z+3$t&!KAWs*POjwIM{(0shZio z%KL_A=iE5KlXjlwi%mUiKJDE7g}3=BZZFl1N<+ZvCB!s{U1#w{z|p(7BiVW#blb^Q zLG6Ng&d@?D7XtPZHC=Z&s9ge7;w$nR5?BKM@HjH*iG2_ALu0p9_M>gBcHhluiAYrd zoD7cTtA#2i46OD6H%_*xYd6+UoqroWZ+HE6XI!MehB4YJnw@#+W@inCXsp#Y_jDOa z89Q;0*zloBa4r0h<~%#lZi5czL7Rib7Y7o+65B1uH1(Fo4+I>mv2} z8~BDf8ZYE9(T7oMyCqtH?HhNMi%k}qsyVH4*wZ)rU49u}5}kn^VjFDM2{H&MaixJNpAHAC(=DW$Tj{~?J|8`nVtwdN@d+YUGm*GqjM{>YT?7G^@ z-fdS9DaQ6&qlBdbIEv?-gitZbwl^{ z>T1%Ib?2Q1ZtIIhwC^rXy3ZV*)zisf<*`|*2XO%XC z7S6iwz%}O_BkRGjm)&6nV118|T>B|bNxjbq(IFaZQqv_2=wqxaTKRZ|#)`iV9ifb* zdw>tvb;U07MgyHe?f@+MC8qpIP$DH%i99$7Q925GE)FW#eQf|*V9?V#l9YH*=RwWj zye;~}v*w3707^w}ox>z9unil>#;{M}ibh8emiP<@za+UBSJ z6?5Z~!|mtV^~aG$JaA1{Ud~R4WtXk}1SGcLMi1Ngx#;0kBg?%BcS0%7l(17T!h8E4 zq>V7X38BL^sWe{!@CPJ-!q^Tg-?(2-oOBnO&_WAi*b$|CtN_vm8Z4kLg2Lh9^a$gW zsZAZlh`~e^A64leKxw`RcCFn4$C8aYh@J1DaLt}6zxwey}K-m!~K+iq^^||idFka;o;NaIkI@;W->%H#r`FiMNcit4>Y8Gd4NVcx5i8(j3eVN!c zZIGHHwf`)jbQCMub{B0J{SNqr58CRXvFL{OZz{jVx={;gVa|hh@7Q<)K50rZoZf+M zU2mKNyZX>c3{iUOilza;nloM?`XAr*0jrx}KrN<86a4m;KWiealSHFoY;c|`mnMRW zl4Vf(gX?M>y0M(GLk4J~U0D+Z>4M|A=n&Z?crFX_9Ao;@Wf<6*<{j1?HAe9}x}=O%3?RnSvKgEdN<$fOjbDmb9nUHR*+yqKmyqQcyc-JuS5&=iD$s%!6B5-wtke)A9LSz!_c~ zeg^wG+bp|L@p?JviO#?ujJ=4S$_MiDvIlhlvyf=tyUcm@P@4j~%;|#-AFoMT2TZVy z;v9z3pl^n?XbT3z712_P1c?@v*On5!KZQ7Vd)W5=zHH{%Up zP&D8SYk>?lA|vHX-ymjg6qIRYL9Y2ZAkE6nW2fs%-7NYH7$RpRqg_LRuf0I(lF_M= z+_RVljO3eR7Po7{M{2-FB&>obl~D)ACM#6QJiyQTqA3D`WZ$flsB0c^#M<9X#9xm4 z!VIrDc^xN4KI3N{=BErl>s6S;G2Fp}^Q?RryZAmtyh~hhAN?uV6Ma7dIbHvf?_P#q zX^w8YO;)8f&3zKGTX#YYoHofOat*@p6362HI8AK3{i=s0d?yF#e&j-_$S-pJwUmmg zYpy*F7H!wx6x(ky<@mKpBaR_|fXiqddM+N2r;z};%!ZtC4$5s@&wg{E^~RQE0`PoN@tM<%}FK}asbq(|H1aW&7R-R zEc$IF*}&eMUpCmYz|v81tUma+h?`(L+js8{)$ZOR5;*!rQ6Ge|sM5H&3;K3D9{tEQ^4{_oB|A5d{s`=8a z*`U(gX4PSG0C|jT^8V27-aJ0zT?Ko%&Z`qc-Bj!wT#j;#DQe?pMvsk-z{s%1U4H0R zAo%2n@?jV_3?I31{96_fT93tuk=-`g`cUZcE^yzrbk%|Y%(c@E&=h9Zr{H50grl8j zpugI#b^T#!HxO9|SeTdNnx#|!=J0tCXrG1&@T0Gpu zuJ73VOi0$_CL1So{g1fNjNA5-1dQ$~0u4xm@4 z4&-5oZdZoTGTLc$2R4Z&70onNqc6kt$b3APqnNl4eJMb;5{G$r*FRHq5vP zxAfp8-b|oqnEVk82~~z4<3)kkCKI;tUtpM zlOAWm$|r-m;44^Gtq(ck>BI}Y~8qkMrXeBGW+W~CBR4=`B zEhUZgXD=8GmN=JF$UhFWEoCHMZc4hP&`AN?=|u%U1`p8ME?;=;zPR!OEJcM+vj z@Nw0Azi4|bAago)xu78n;tdmb^ZJbjWRS#txnSh2k3^@S0*O{F^Dwjq}L$0!9QD zA7W2Yb#rL3i`H!-DZ9#!#TR^n`2emt#0577monZF%zRO8v%e+SsY?Cj<5BU07h_^e zZ&k&u>p!=!eRS8!nO7o0h8eWRq#s}QcU~td*A! zQ4=l*eCT&QaSR2q<;II+F4{hq4YVqce^7yKfLUEzo8Q9PT zfo|blq8kAg`65o_skC9oYrE6W=DTVAD97fVnwqUYJ+$>q2)^`M zbuq$fgh|g1a83s|Gf}$;>wL|=pS|T*RdZgoyi}$)Q-M0_Wk`c?Hhf;5`5(s8Q$%$%HeXEAG7Z6PqOjBa=@M0K|W{&2WT+{xp^44udSP zbnd5K6h+IViM=^+9UasKdA;O!fLM{R5IMlZ2nfJi8BG%6UAp-DF8?j$Ax*$@ggXG@pR}N!@VNhVKF> zL;6qZcJP0tZsMQKX5>fn@t|9pEC91I{QFix);alYl|92|?0t+n z>|Dkq1N6WOL;ktLMyv0VFO)W0|GGm&=@Y4g98C|l1bk=yN&CL&K{MNlYkuUZn97>cmGy}5%1 zI4EJ-|8V!0=RO4w6#sJf_L7)+oO2kDE4k1C%x_RB(FwA$ce)t?&{jnVK$Q=lD2P}B zzg+;?(?D%c3O~K0=dx(!fftcym*h)N{%dYkBDGPe4pd=hJla@1=a0_k4911a_l*`u@ZHCOZ!sW#PBgHJ#}J$(QKpz}p{EF<86^~ZTN z%b-kROrviUpl6BaZ@z|ttRsRw8#72pjpX*(`Uv2QdKVS^$_{4jj4%kAmG|}B)Uo;`M?zR(%|lc zr=LN9T>(NSZTs%KK-6VG9kVK%3DQyv*LS1kz-M%lz%`Q|!=x*Bo|%sXfKQUZRig^u z-}gM72TTk|cS4=h6oIez_vMayw?74f&2r}Vs5ivB51>XCY=C7T1sMvvu^UPIgT3|w zz+7)L(jJ9l0#!B9xKr??~k_ARDq!6?RByE%8ROC=kJt+?Z_q;1NPow?g6 zvyt`RHZK<2kJEw)W&2wL!cA{{>?_>cMNJyNa&{`;dXUp8r)$u`U_PV8Q=(E~9jVtA)--EzW%d*On=wUo=b8$zVYJP-fZf_XS=5XxP|E&`ZDjoxQ;ZsdMa(#L60^Hf^t>b z@INdTYgFSe|BCt~8I~-Dg`1dZp~bE;kbb_lMw>EF(mb2}M&fXw@Jp-dX4N%^{iLnz z*CTn^WXAifQ6BF6N{f;sZa-V37k0~K*7^Zv85cZ&c!xdnt~J9k?*1HGeCrnVENqt{ z-Kf@L%0nb@I5iddaeiLLWx5HE-D)u;(rGSv`S-Ccp?1i=g_CDC%ZK~v@)?NczGtbb zfWn&3Tb%tXNCae#^y~c3!R7}x6&*hApo8(~-W_nD0&0ySfhFpyGI=M6DuN~g;WdZ^ zB6@M7Ey!b740_zFyvQIIUCm-!=c_X-vzdHm z`<-Eg+3*IespM4|brRoo)|tzqzt+dI4iY{RdO%vX5m{60P=q~PW@Kd6-ye|-^K43l zW80AkF}}*Qcuik$6!T)C>qj26rsP8!(@biuBuJw~eR8=HlOxsh#rBkA=+Dpu#VlX! zq3AvD-ZAGLaR}VjpzaDo@cNWwxhu~1!*q>=e8Op3A}`yJE5kF>nZdzhpmGP)4YH~q zJl~N1P6{nN_Ra}wl~Db+8ffVE9)12tW^g&(86XGZgAIx=))M=dU!+&B6ZC;G)K}J?9xo#4;h0#Lt<9yG=UC zOm{_`eYy}UyWgr0Et5!nvH&LcmGcJxxH3}ZH4u< z|4icC2tP%ljoBa&M8v}0;t>ZhT?G5_3Qm7Irj5MxR0JOXX@354>r_N<@MJ) zi$4iqThm+$7%)WCZh=)RIK)0rr@v-Zlwa+USkTW3p}ZepKQ?#N2uEayz9N~-2{ zKAiD4_@)WXS1W>XnrrG@3GI!wN)ZUzK`qCxBFgn!`9@5S`h|KG73f)}8y!=EH41kj z?bCTixsovlUzKchSemc!$>*wNhU|-D&gc9R{TNhk8fxEn)wPvX9RglL`?7`0g+b3O zUnMPc%>6g~mUiz{__n`<+e-CYW)Qt;AxcMabw^&aQ#d1$M;8?UC|N zpSyLcJQ!gJb`x1e%PBnk^cYoTYYsNzu*v1M8K`f)(hMJA8%xHggd=?A@8;@?z^+KJ z;uta80K+E8ka5h{A}vW|>G5`$CNcQ0|Dy$H-v4QS)F-T%s~nYlwDe1LvdR9=O!IAk z^rIHz?zQAsS+<|QR6o!;wyXeu7;!w7)LH#cukjU|$Qe^7#epAW&?bo&`Ja-l&8%GCcAeMI|T}`S#%@fbM zNEh50s^qYG6@c9kr8Cq(Q0unR&m})rtG;qb@=V@9#8f3GGt4rfZ4&qPLwxfq`+TKr zYNjOqiA^DlYvjhmseuv7V&&^eHPgwnh1i6hjnnG>@x6&&8)D}8@Hz(zn$e`LswB#x zjZ^L+&%PAZW7*|gt&GWNMpOK1X9^zM#JUp_1*3q|j9B@Nb>#eR^KF_NO+gSA?G>6t z)%4jh(Y|YANBudyL2}}^xV+J*KTZ&g+}FqF0N~Gj3Cam-0A=W;hp2dOuo^Gux?>)} z9-p=8bwl1JqKRK^$}R2I=(>upeS4z-4Sy2?es^ED(S-y^m_s&3io``l{oWtNJHt8p zk~tGci>5?VJRC&DPe=3glq-_>>}_567j+2m`645qxkr8FE|5qe<7ZIetT5L}@!{tf zWkm2cinsXGPJ6{N=!9+z7mK)GDyFlWG~q)8VxGsQ`UWV3?C;-kw$vWW$Qz0s8nwLi z3uRV%n*T6%q$wExq;l_c%#$HKyh|F|J+Mu-TpTTir5?0qT|hAAHqj8Shtqs|u=Gok zN~#ZnCHk)GYeX*zUi^ODbH7MnZt1|Od?S1>s^-+6@r?PZF>~Lz>qEN5hR}P~)NtXxc#vc3_m6ija zwYkkj`lw*J#?~yIk-Sv$PdEd)cE`nZ9JaDL31x?7ucd;9WJ>!N1J@`pBnM|Pq}tr3 zIIN^GTpUJ?$UuPse2DzYzKVO9zycO%jI^lPFK$vp1hapTQ`Sc5gg!5D7S~pW|4s)N6 zQSFNElG|cf{rw&@zunfLL%9G8`OpaJg&3B$iyn|A#NvGk`{RUA)e!X66g{>ao7tX=L4Jw2g9io z47hYH5zRrI-(kbKc@j$ugHANk-|^8vuyI#)WH304$t+S;_~Y(qFonQb)H6SpG^6%j zzRTaa9i@hPe$*dk(^>Rx&)>06+g5jo;k(%07knz#o2d1EZJs%r`d7loooVihj}wv?=Rf5; zW7u)j^6;r)VH!oVk~uIurB`Qhe?cc5g+q5zwK`dH$2Io{-J~a+g|cL2BlUfloymfa z1l=!QElo0OQ&ZZn^#=}S$uC!h27}c@{8uYzw|xTw1smB!gjW#xE7G0~Aw(mt_YcCzP>z4+Im#bKP0BnGMH43m297HP*5sM*hcH z>?X~b>PapkDdpBidEI^)HoSaM5>zfa zDqrq)wJ*<7TDUWco&Wms$bir3i25!e3+$wH69ebN$IIgIw&az7`xvePfgZ7iZYfPo zPG9QoRpYz&#aD+qe9#r;{bro)*forA-^5qeZuv48{9!PaXr|$u^!nqX#=doiLAb)v zDG*Op!&_kT_e0)#G*?(V)3rRb!6x$P*kAh9{Hp1tjM-F!_qZcjS}|xLt6(POA z$-3di;u|oUx5yuD&gyelj9AOQZWoj^hZjK0kILU*t{3Sz(n$no9u3N|$XeaKxvTU` z?F~)m=soJTKB2$?8q|s`N=)@5d?N`ZHB?nh6XJ7Xkvp6Vsw1;PH5BODgs1 zcQ?k%SUKOXNh`MCsRL!q|DII98nI8?@LuVLm?Nx@&9e0LWkoP9u!fr4+uwKBoUYlY~(U!7p|suivsb<-Zim=ZUEz&QTcW45RVgg^%9N(2< zqm_${&+gNab+5at8qU1ER)9mCr<|Y!T@uo5{GI`RN5S~O*@JOnMV?&D*f*HUSe&t- z`%SsUr);=%vA%$qwC32+JyjEy?6%z^VkT=$=CMU2WK5TbP$uq#NnG4%0NcLpG*NqL zEr|X1rY~g?@!;Y6Oz_Rt;o?%N7rQBZD?8=5ON@snko8ju@xq`LkLbgVgHE?MCA<{O zqEa08-m@q23+!0D7fzTMaprnTnMQ#jb)VT(H3uJ$#;K%OkFSd!VXonrX}<7^iF7tW zPNVonT7Jr&c+6VQW49D-rGiOJl6O{4H_p%64VaPmkLqc6-W0!!qRjq?L3u%j_FQ13 zx_djIG^2Sfu|PS;>smKU2jmKMNn(q z3N-wNZmwxf_ah$;*Q%;+^fy7>?e?GB@8C<;+L>fY;QRzZyY01}A4>TZsNmR%UoDPR+I3Ud`S>qY_C&T|w`A#6^B*%M@M~qNEdbN#rYYk}y#@2! z8BXU|2%v?aeXA1PEIxUDY_3J*Mf-EaxhPgn`gnsw#OM0fpD)vNs(1U3f?D%5&s};0 z*c(orn{yOmeEh?cWGO8s=ub?4rSA^*v#N@d3IM|lEhIZZ6RywxFQyO>Y!Ql}WUQZ3 zeZW;n+@Fs{fkx#}e5Bn_{pRM|!ZmLs=tn;J(yCOgdQ;#36G z9C*9)v}%6Xd%>iRdK1@q=py9MVZYa0c6={?b1*uAQ>spK&`W#;DKHIG z--<&Uc#E*5V3*G|s&*X|uC#$E3zWjT57E_WzALwoIsaWA6yK|>Pvo6DxA;mq6W6ml>;p3Mvb+78|{Ag08?rqOWpn`FYQ4oRV~Hdl3a$~bDVv`OB{l)+a`V0M=vus&k5 z8G86nahpuOBa})iJjj4IBoM0tAGi1JG z5g)0WBUbJGace0D9=zXv>FDXEDXxyYHX^a4YtK~7iwGfnK7XP;5^PLo$| z?@KGdE35}lw`r-f_!ju%3?9(lE`eX?s6$A;+IZ>GvOvEq!rB5r^UB=$I=*$Mlu%e- z0NxtZR;*;IMjlw9uCFrUlLwQ%Nqmyx)3(Ygu1=9R_1ulLh!;`;cF{b%+yyR5pRooYMKDhh!tgftntr-}9i+fMu%7Z-!irce>{3uK(t6 z?0gI{1%@r_j51{>RDTHhNE{4aX`z>p@CtnkGS3^*ls!O9YlRU z{jc|2vsmNI+t; ztOBpVJNvEMb!bezIU0WM?iihOEE}HsNxX)fxf&ll_>Pv`8wE8pOQtBxvlVX<_z-w` z2B)P-jHS)A8g$-jX!BRPv^?0GIgS;?hN&yVBwwJz_#sz4&xpm0^U2t8#9Ry5E@Svh zUXkkfB%JwWch~hJd+uVGdF+kmIh6CS2X)k5WK!!w)d-id!19<^q+m6-LrEA-(UFrj za|>s5udZ^M&1)RW@uibxFi-nYz@q5&L>VLV9E|i%UEK8tdt6cJx z%VF>M9*3Fe^nN(;wsF?I=bz>&Be~i%3MJDpZC&<=Xw^$yIr|Ma)70w1qOA;+%>6l3 z%BW@Uni?kYSjc?8_>__8Q|t7=`;)8(5m=Lpve@A9hdKOkE>B=4mkd;Jv8ioG=%N`! zoqU3DU)z0~BrMvj+_F5}RKpn=Dsr0=_)zp*L9w@mP&wTtKXkhb_$pS`l@8&e=7U>O z85dbiz8ZA$W6q5kkEN_B&idP#GwSfQbSN&M_Yu_+53%E7lkFmVhd*>5LpyZVNohv= zW+qZV;mK9_a^rZNgdvnE1U~P0Z+P8KF@ej|ZlzFU#%y`uV|bg>9-CKC{LOmhYHK$2 z0;3@Pjk<>T`GpXE`HE0yp#tn5xU{NYG8->seZBBe5Z3UF&6EM88MP5g}u>Ob|ZEVucAZ-(cg-9A|wXPd{9yQn- zszMgdkNVK8>1Q~@EobYmM6yfM`uqCAaT4;px>Zffeeu3Ve6f{ocI9FatJ{FhBIl^* zUFs=Mn6)|b8G7E8B`B0eD=Hy=`}w9$`F3}^5X@rm0{%{tf>=RO0x_Te%c5nmm^??O zmlOK^pBrTS&W&@bM~h|}%76d(a&tCoR8y2zLKJ3EaX7Bqy*)ya$wq)*yk+npWt?4F z)YwQyw06&~%(RGcR%C{yJQ`$B<@Lvy*IrE=as;2V+FP97$n?d-Zt;&Zo4~tHq?gYV zxjWTfoh6l@B1C4PyH{ys%&B)jxsAq?p!Fr$>+uHf_M5?XXN0fLGOE+;7U<8^qV<~< zv1NYHA1MnN&*m}mYv+sH!T}k~lt4oOKSXgy(23su7Yo@k2kQy|6_!j!-JYCh8U~|p zGZcx~vb4P(IB_ibNyf($Mjlmp%?qgi3G^2z74G=+G{**o zRYx(K&xBb`YBI8~704oS*+zc6**UkXMHT44d)pEaJRlggaz9G1an9I4X`Wgh>CB^^9mnU47)yt9j^MoNBt z)f?~}-d@N9@Q_tQ2n^E<(1C!()bBFd>;G|@CzVF=riFh$rB5%XOXmV8raAUF)o-Q< ztrx;)&gF5IM>m56?icYcHEK}tiylP7#;LWP4Hl(fICoa@%#*Ki%*}z%ti&)9%Gzx_ z2_X06AGC{QH|nQam~BgpD)%W#6Y^U3Kj!D!Cal~U#W!m*9ViqPt;+c)Y9LJS%eHTonRJkOk>Y5X>}8rf`Xbx;Gs&YRqQn-vhh;{?%mH94JH;ax zIQs=Bin<;L6p(`1?ENW_$OA`Cfe_#?{5o3mWRBb509ahgA># za=@Mxd$H*y8)a+k9*H#3SPVYH=r~?2uR};GRAt}W5bLTqzrndtxUQ>KW}$~uSgP1? zQ|O+>m|?Fm0Zwf~-lLX&yPh;NE3<;$HZ7m$U%P_ZJW5K8j$$*UwFSE~F5+|P$_D~M zo)MAZCLTYO!MSA%NZzNY0o54Sw;`cW8oA`h!<5&x%X8=FI{*k|18fH=Sqxr-^;+{JGn=08;4Rjl=G6Hy@c!AP98)l`fMQi1Qr?f=51se)!m8Z6 zRaaBgzJ^yxXq5c872UbFK5bf6FT^SAT~uT%(IPz0$-iEMO#J-B4VTqmQ*)uSlC9WC zekk;yGOTti6y0vXk5DLf2R?i3G!@iWmvyqg7}glp8aAEh(A7+{x1~q))n#Rr6l9dI zPx3zLo0LWXKdkiv?4V8&AcK$d6eXx|==nc!VTPw3S*koC z+tpWX#`EN*moX=T6Mecrj5*7$+FLU4QQeZV!3Q|ayWK`e}BpwN%RbixB zN5>|-t0e+^PllCTsMKTF|B9CPV1_mO-Boi9 z7KPpGx5=5fS=3=;rDg#y*;gpH(fRMek_$z6KFVQKqm_FX!4I1I^csRaSnZk=ni6Pu zb01!jVXE4FZ9D!6n@@8rpm0i#q&5(?D8}|CX3G|g@QM6Jv=dF168Y?B(t+)B*(9E( zWVttIP#`SO{$<l7n zU;~Sj*Q{{%pzV7hD!tH`CXtHPtU38kX3c+VemX|`HQ2>|8LV%sM4LbM;LnoY@P4?X zH{+G;O}JZet$XY|~AMjxa` z;?P9C^!#Rm#V&~N>pq$PtkGen5DgZ4-5VJPPb}5tvnrM93!~_Y!2}xvqQ_*h}nAm-$)i0;@X_7O1%7h0JC5GkoH zul#&ppNsVReYuGlRuXJ|W1xI&EB8sS@l~7B2V*LnN{tF*SQL$-XYH*^9X&TQh2l7$ zvs@y@mn=4jR;+wrN1Ch25@-IsF)cepAbSacIJ+aKs!IZG<8?St=z5LP{JLkmd$*)O zDH`4Da9ZJmnBGg4=bpRel9j!iQ?mSq__h3+6*9_;A5y%n-gHNr30I0W3&l3a2@gGb zk*b>mhtlq?Q=9fWceQP2mSTF|D4HyEGsN0kn!WWf;&NWNV?cV>)q8tU3Z@w^X({wbAD_%(?=oB{ z9#b51C$!H|^$ZIzKO#e9+{b1axx1I7CG}-Eh*}K8g>q)<1*sT@?sUpIe&(wk{INHs zAKoWj=2>bXZtZ-pn8}dZ5OR|8kawuVH|#x)SvW_+qiD*G_T=jOpS`WqX%u4=wwL03 zQ+|VEl;9VKOQXw#-UPU+RP21VrNHeNl#O9Jr;0Dj&AKT(&&esZe?) zV7$bWb-_c!T%#`N?)M1%@i9 z57f@EVP*?d@vwNV`e@j6uCrYiJvy|r=zPVel`SIQP=-g8eiv-?aPZ`d9}%h_j@oC! zkGGqYF_PbfR+XBDfkKqWzV(WT=x8g!FEUjw-^3G& z>J*cFN7>>x4_=L?5Q|{Y6(0k% z4$4tX{D-*(Y<~IabwND#tXohGy!_~Z_!{qX{6tO*nbj<{Vw6h@xI$1u{2?d~5GL&{ zUwoa6vhqCxqgX>OwVaLGaVeCO!}BdX_4< zqi?i=lh3Fm@zTD3jqpvTp^@b42eDSVG>t;W$76en!BSoJsQiAh9^J6!*U>w~q$Q1o z+p80ISRL!`ucZm5pbdCsYh1WFF z{-P-=Gai7Rv^m)cIy&hz+?5R9m8J!ioLxTQeap>_Anh-G^qgu2(b=OSu%RvhQ&{T` zM)C*Mq)d>^pJwj|xPbYts1+K!!ZgdBQKz+QF0Degdkfh5U4iF?hO4#*bA*}-H(O#j zZb>J;7b$8#3L+d0kyLuaRQ1fvezT5n&1&;_SC>5`Kx%p6%JbP#;Z1;8votutFE?E) z^z9GgOHQ}Ar6{*Xp;RQK6|$^-bbQTg(=09gv0(F3>BYH$IQJpehy^B<;J5C`1TqI5 zZ`7RLd@J=(O*K-x?bma)#d~)2gM;`r&PT6xKJ~qW=P9II{ASoW5O!>QFv!W_F$zGF zoHF0DAZM=(VT0Klf=4-Y?vxFJ3_q_W$lp3WyGAV7)ajilT$X1AUg)ve^M0g_>pMgl&5q5+B zaKcAmxNR|?8>{cV;+hy>WRE|;l@v<{h1IASZXwvjsm4dO`XI;9lP1WJ2{#6bx9OH7 zuloB*(i<5;>GKvbcfCwOTHMQ4v)YmU3Y}(Xbnc))#4|=bE|amZ5sHnEqMPaCxb%=S z8Fd7&D;2`FNZCPhTc3Ktx%34`YeF@TSk>^5%?|~Dzw=^!cv>_SCw91KjxnKifaT{q zW?%fKYoNWo+qD2)=)OyVIZ0VY;J7tsc--u#TD$L_gmql@Doja&>HY-Pj49#0?&qzO zJFqtEujhAbyYQx67RB^)R)uY-Mz_S#&$K?CAo`M$sGd{Oj-8?gV5yois|QQNDm||s zGm)DUv|PP~>R@zqg-Ic_mmyx-hmT7_%l=Y2R_;r=VgS;y^C6+=yVE}Lwff|8W|z*_ zqrG=pmdA@GBMM@xs18^}q7K`nIQi9k^^5O}H4xkB3*_RwfH_>0h04*eeCG)L-C{+LGX*4k)A(E|9> zcTMh5QrTUtG|F$D6aj!`Sg_le6aZi1ka{T50bQGC!7V)>_4vtM>;l0gsopAG!)#<@ z@TZ7Z$$u#XP^h>^MBL>vME5>ek6@VeXqj;bqu=}Qz1v&%QUb2|Pg+|jeMMdI(OVAdp3pZqb-L>)5s6Jve-zfC_ z-q-^QEpTi^~*(O-yCU`#))SQ!BGm<`#cS5D0%665%#Rsw*0dp zs=M1HJl+?!u~I^@7(~aQiZ;eXd-cA0qZXBYG}Gn{&l88^gnHRHDNxZgTLcf)njuhI zC*gIjR}1Nu48&Yy>Jlk{|M*tSyR|SVm=>Bf^br?;KI7}Xw^Uwe z>_*3par=}d%S<5lh8P!S?~wuFMiufm+vqQsF7;#l;vN9~2OyYmWmMMfw%FVJ$5cUh zwUSCp3sDo13`0Y#_AQ$G>p3RRw_BL)50)(-@g#8Uj;AQLokQcQ-rU2C5lveesn~hG zII~w*=B)wE>B`#27YE2@s+vRWTOfP%(9&2JP^!bNXWm9p zOo9M@VtG=?Apfur3$~#m-loU&?E5ugs-a!y$_t(BuKd6;k6nkTmAYR znoO5(fpH|a$^#s>jg^SZNb<+(IOzO3a;1&$az&)jnhqqbNzN89hFD2S|89$;sw zdfCg6w{QikX)+~nB^5q()({!8zg&BO0{VL8?R5t#N*7lsTYRSQW=5ojE&U*MZ_rI0re@M zN{zcg$xN?X|ApKLqh|MxJjn%npWv?5Pqh)Pq>Z9uMr#cCj^#fHqJbrBE+GTPlepS@amYp_J)Q z9CjMVvzEj0_uLjOajaBfAy-F(7C4p|6+}$mV6??o+KjPu-psU;&zoHtV~Eudfm!I1 z9|FWO&iR(Ea!^DnGXM4fN4f>AFa3U83r#dOv8rCY?77m~ozn&5ClL%RsWqs4Tx&Gv zZAhtaFo?e|7lXZr&ouVlTrT&!r_Bfq`8;Z-`#4cmLcJ@cshVV-CMPJN;+G_>d{FMT zFEU$XT)T!jA+!8av9*5ri=iWYP`m=$m#20kGbGg@s_8C)C#3^sR5q0Yxv3{JX-B=C ze-u%z@zU2t z`q~4>nqI4}54C*H7t~IF7+dK8m|ewBhj#ki3tkkn7Eo=)6{@W~siX*G0=ni{=>fTC zyrn6Ub0GOv03mT>@Dcea)0)xC%Qek8yE0(5;#6gV^r(+k={?gL6gH`lxJ)NV)MaW#cUcrg=DJ&LIYJIcO`q<5mMm$=63reD?zNGR4Y zJ)l-p5XoBq^laj>PsjGi!Sng1oy2((Rgn!{3#O`7_v`z)q|3PTIHliP6NT^TF*;@u zVUB;IycMv*^CxXM~9GJ!aHS>ZdgSL7Pn#`R?LEcT{jWHZY|y} zXonPl2)Qc{t7B-Co1Pdy9(Oh#DHoOq|42S_rE)dFWp9f;T_SNfu6?&L><%lvQ3kw? zK51E%%yYSPA@&Xwb!vUZdyC z(p%MI)y!-WJd1RF+YvP7NL? zE~yY70Y!tWVxZhl%y!1JYh*F(Uv7>JCOahV55lhuA1@Rx_J zJlAKNCB6jAW)f%z-i5bCN~a2E5@}h^tv;t!;3Q3#m?DWw72b$`UVDmQ)YH*<1*NT! zP*`B5>b^rCR^+y5rv=5)P@w$O0hXhXAXR{`5{?)_FyCn92`q~CVeF`6y!so5pAa5}c12kkGCl)c7%Z6tGQ!nld^#G8GD(=Nh1?3?GJu?*@VtRu6 zy#U?sE-{3G;Wqu>Oiyn>^bEfAw(}Hwf06!C?4_Ui`+lGr2<7jA0#oHSHdrf5K<(mf z)i6j^8j*Rvu!^sFq6OTgFl_3QE(tQo%-E%hwx8ZQ_$!8};7qyyzbZI4_b(uCpvQ&@ z_aCY~Mturo`q&-Lckusg!!8G+K2qS4&mjg;k+FYm565lqGmOUk-XS`WyR(j(@UrS4 zgBlsWY8n#JYefC79jVTn2J{3pZ(LveB(B1Ahm45tKeiETn6C0s*%tKPVP4y&Xu4r zo{60TaHK8v!6U#E18NHWD*B2*zOR7j79=KvI8Vi8z>b60T91&&a)72qK<5wzr})2- zzrVu}cx~_CJ2y7N16i+_B!h3 zHn^AdC3#$sBzp9lAoX;z2$U}D=YWjUewzqtcHD~k<_kQq?Ml6uN~-dcYbr?et1;!M zPY@Dwo&{DrC}LP(#L>WytprZz+_wR;1|yv@Yw|RrUF9#r^pN3+P6?R~9iotX>9mmh z-~;;oUN0n1eoy96_zVtU62^0=Zz57ikU8nFB5arurmLoq1&lz4>*t;Y%8|H17673j z*)5*EHUXMrNanEk>I>FH$esnnD#R9mhl-dZb}04-z%AHqX8|6z4$Chy(z#>y+lHR~ z^3`Cf%BOXP&_QMmxXXa9{L`>?w9bgj{pAkGlxTqF>H&Uym+TY|ykHdin7zQH0)8C4 zsCO=j0Qdqf$WRpf+yN2E7UFHaY#Qx`U!vb5W@E#i$RwO1|}Gdw*cmoYVfIx$N)t2 z`>(7ollZ>6ZIPK`0xuvz<{lx<{c!gx%TJdFCQN^XE3~rqCmq1vUqIGCNU(5OLuKJI z7&x}^o&JoBFfd166zg(+brUP*M=B_`<9i3%v@kk$*xTbPtw@Tkueg z5RUpQIginfb&V$|CEoxCQln#h`!%D$n^UzXG*ohWDU2=fe|qs#t4D}X%W5 zb$4*UR6|@7e}wdE5C>j8^E_Z?Flu-5RP%6UK#+2YxFK;pO;m8~cU9K^4A?t5 zWS-SJ=hGG=As@g`y)e9Xdw@HZ2G$Sx`~C1C30nc!(2v{@bPphq)<1uIl}WC&-x6ha zr*)I62I*(}YnTxh36*~f(uKvr7@n@52>6#+dw2Zt(hVNC;B+h2CpI!2ZD`yFZKBRS z23ja??}B5`|C~FGP#fU2w^`-UKzi#HR6m(AI4pZYb? z$K7si18Ee5o9g%Zwca~zV?D({DFK!Jw^iTaBqa{qI<=rp;o!hXR4;=+XSz2+KlR-I zT<6g!vRa0pgTa0)UL&(3zL<+8_RF(@nR??DM6E5E<*R)wScCt^!oldC=!rI~`u(;& zYiE{xaK75*jh$UCP96SnC!7sF=9!ZfY)6l)(ttTy>vV2MR_f3}cV-0-npCGgre^2Y zQnJ!_kr&)ztOykQeFWZLVhDQ#XcVeO0=vc6;=K6lAb@R(JzLzd##`O9-CRfhsKcB3 ztQOSL>IQ=Qe+sWT>b$rx2iChYpC&-Yj&Lpe#-~*8keDxVh zDgXY)f{xMePSlb8Ls|2LzEr_%u&h%;mQo#Q8N=Cp% zJirt6{(+I9_VkI{1e+UISAJa3(qt91$3SZ1>}ZF}9YdfVJ!C@ z{#sXEU3sA;5h42L@Po4D8mf^X6hOrRyIjMsxdH6*BN=d`1RbX6zrC5Ot1O}qA*sM* zMuKZ#&0t1EJBWyh-J-3~N!YeAT!N9XzJkDhaa%`F3e_{$+9py}dZJGzXI3aLe$4bAU29rpzg0yJPb`$G%qff`NV zpOfyakpcoiXEV3{AsFgEGMk(_ZjT=Q>V!Yvc%OEyH`ArNs91LBqY>6 z2@yf1Q=I}aGXaQJ_xG!p>MT;otsr1F2jCQt0fRjb_-T88jtcOXiDxscWIwoO7qmy| zw{xWb)8hX)ZcY@;VgcBqDA6)FscJNIio{n4=qM!q^T(*1y>~196)iW266FJ{QjG@w zP%j4S9B>_fAK;5%C%Xv(trdh1X-2F_vK=5^(!ip=@W*QN`~Gz}PxqA*QodN=J^~S{ zOBPG3m#&`rk-xe+@aF2s_}J8v0tC_Gl!^RVthC3}%`vYj{pZl2B)dXdfu`dt=Ud>Y zMhLAnKEd}4_wN7GQfgL!2GGILWnEX$%ql44a^l4*0ur$Q8aMDg1Ps;H&PN@D{Am#A zpyzX)f5ec#N6$S7cJG zE7FjA`}+l{z9M#7n0FXY*Eis*Ki-7|VgLTIYZ59VEZs^AE&+`>L|C?y`;1D&XPv+2 zHuOmi*cUOEK+ue(@e#5%r#u_y&UB$QP_sGMbpHJsGfTM7Mg~cNgYyDp0{^+P(Ht@shha?vv zRVY5N&zKU%?)&F2Q7MptpqHj{3-o9Jpng~{_OE^c*RuisxgQ5iBZ%w5sS|^b`%k-D z4F_{5X55qz^z{iy!)%|h)x!Qcu>-*aZj9(nHi77t4jE|fIo(vcfA)_QXjVzxZQW3C z@lo)8+xuGHKfNpL3239+$MV?gu>J(3v2nS7PX;8^Ve~`Ndp%a+!~R#Ckib6)Q5lgu IAszSs1G9x0-~a#s diff --git a/lib/tezos/doc/assets/Architecture-Single.png b/lib/tezos/doc/assets/Architecture-Single.png deleted file mode 100644 index 934a72bbb4e22c45a010dcfa4294b088ac96ab4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40531 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�h;7-=pO zHrP%#=w*qwc64xk1`KlnXU8%a9M@i#nh%JREPZ#PD0)%%2fNF^0z+Jm`K-Q&PKE1z68(f^2S~g|CJZ9hD%a%kcnYVmciA>a>`$)V; ziu#RPT264uoB%VZ02f^CN|eRv;A}C5HTF!8?IuU>tK=I&$)GIgAIsN3|Co~Vah^tw z`q#Htf=%O1s{s+O=QUzD$Z!S$WXsqe?kv-+B_PK zW@SmP#a863ww#TjjIt`40w5g%P8FYH4*+a&Z*oaLHJolE;b2Fh;4FqwE0XKL&xK%t z?4}PMTXE?hT=xkZz$>+^4Cm4*E*!WGN+GheSz_LYd?$cMvs$ZnUQXABW0x`_?kR&# zN+Yq6L@bJIHb~=mylB(u7u9w}^cEtHb3vS-*h2sD;j4hI z<@q$BHYW4l*!=ue5ATZPm$TF}EeZ6|YJs!a6ZEdL7+%Zx?kawE1s{V{Vp45!-gJ_RZqJPM6t3Iub9L)}>UoeJGlbWsrA#eR%TUk2|7w!)rV9|UZ-P^aU59Ox1n+lJ!n4ZQ$2Q%v#stD8|m8Wu-;hHvEoR^m(@>=yP1|jaCSPRQK?%Bqd*{ZxygEp9)@m zob)JGp-!&0EJoQ1x~nJe*PjDmoEw#4{MXo5%K*iF-|y3DtOG`gY#q^eKe*$UzWH@W zwXX2Ic!P(Dqn90|y#EhG;8a5y&**2t?@=9>NhaYJi{YC5k?wF)(*u0LME?NIdu zm_Yk3;q#E~^MsWvW#e)+i|quyPx}u)3Bd))&e+Qwe!e>+T<~FWFc`duhYwarbkt&- zDt+yM$kxP5sc@qW48i!REAI&bnowt7=G&OKhBDrpA~NuO5(RN|cfZDTG?#g(@8`1O zt0nvG{nPgH@Gw`eYPJtPm#y~q_$Bg<39EiyTbj0(lEB-$d=YgyT?t-`h(!I&_Pn=6 zGagl{14hm)0_}@7rXzgYjN^Aj_q0cL@=B?%B!5#xRhg)n?F?wR5b{kT z>*$&Ca!qHZQX1BgNK2IXsB)kf_`9iFd|YmBY(rT@klNcx#FNF*wz6jg1KdSw9~nyL zq^jiSs0QcIR}M1h)V4wnE%Sd;w#1+;;X9F{+ujFme&Sk2V7w-Xof+|eIQ7b3r&pI? zl&&xKrf9&lcvo+K<)E7k1D)+`Lw2?E6iT2t8ItdRoP9No+cf~>V|Tyj470Eyzl!R% zq3butYy^Jr`Y9wfy-dFrTQo#kjot9#w~lV|O4vWG{UfMz`NNg1K}bV541L&hD5PJI z^lhWWckIQD^g?d$l3nW`#5(oR-R{7noS50}JYrs-)8X9I#hSeRD*wlZsIbup%| zU4Lw$FHN;4NkFv<))B#wLuush2TIaF} z$HjvS$HF@bRZb~JBowhBN(x{|{_tB2i6(Q8k2cdb@^P=olUu#A-1{)t5x9|q zFa^Q35vLSi>C79o(Z2(QUthb!poIPE;aVl>*Zn-gZujVEC))Y~hQb(@xP1#cZ4S!(cX;8>a*CaRfK=Y-dFamA+tnw-+HUoLY)9ZUWzU?%5caR@l`dMU|YfqPpXRYUR zVPF_plz-1R!m*)7N4sj~cfZNQc6*^`uo?#4-3MBW z`~)@Uwb3Z-{4E&r;4a32{pknEG~ZL9BK_)cPOW?yP?coZ-9)8<6i6DEb^%EUN9}?@ z3r767ss_a5-0C`q)`W?jK@HWs5h|4DY7tOvbViy#9F`-dO38A22W~JuK=TJzqe!7C zI=5*W-GX7R5*VX&n<9LVAegHFpD*SGEb7Ph=>0Dw@^IDDN5a2P<@{pX{VS^LsCvY< zT>F{75<*QTMng!;CNK_m zMPFSlikh@Oe(YE7)LI?Kg|1(u3dfdjO&!7emIBw(!yx2{`y9ic!FYDn#MVtZzs6p}xy4-n<*w+$Qnn}7HF2|d? z0~N+j8znUN9ybiubW#N*hd10dCiPBi8W!BUVmWp?St5Q0GWaNXAJ$MbOW6OukRs>+ zfaH94LJpI3z;ht0n0#~!QoTISnsvBnI5yi^dQc$ia#3{?NH_PNpF8Qrkdn*4()s)uB#hXSo5*b2=bLvG$YFBz69xQl%{Y158qLa{5oE%pfrIn2D51j@He zmgFJ}Df{E%^EUG53S=l_aWzj+L}x=R0*OShtm9a@DHvmNb;CT(TaHaB_IZ@?wAqgj zpHKVpZrl%^7D6_9?Q4^b0?|nS&hoDzfAE%jG-5kHr8zmf^R8>;sAK_Mg&Kl_ThkDD zXLU=8Yu^K!d>F+2;sf7O52+dg<1b1iV2>LZ`z~Pp75heCWxexcp7b;q`QQM7=tl zAeu(P&G~~7^a%(=XVX>*OEr{LsHZLp%d31L&+OxIjQ!WiH2;*5pgnx$AL8?4Wtm%F z01NWbL;>{HdbxDShLckp(AP|gP@7oitkK*RJ%yljiyD*46N z-9HcY;EfEa+Z0f{5g=92(r4voSwY;UXSI++5(9?hG!xg+v~hJ@8t#O>t)3uR$DC7t zbO2dYWkfs+YGOjZVg?gutMzq)YJEX21h^QJY|uv#wNV@HIKyOoUy&VSW&~K7w-hL2 zRZbXYUIEN1nFOiKM*nL}iU2IN>+5{y{j($iABC`OCmKGBPt29mWKbh?s0MXtm`8$mW4&L-{)&EZOesqvT|)1r>$<|`inMahq zEpdnTjvIGE(ppw$4tdy5j$*6ytHmZI{&I^3rTa_9;ecuih;QtAp@&oFKS97A^c&H; zf?+C3k{+mO73MQ)g6-R0|XWbQO>MKOF!55_zlw! zC+@j-J`0TY?gFl3;eBy{OYRl)Wt65bfopMNrWfi+c<2 zNs04R?`1ZYxk<+jf1)B8C}atAL3h%VVA#EWMO92#L`2Ton4zk$lM)_;#w-Y;%({qm zBrYl|zqGjQ19+j0$9O`9GdXEK>PLTC9Fj?nf$S;o76A`VLNwmwD{-60KsRnCa4s~C z0sFERa7NJi3p6y!5QYlluWwsn7QFNg+Xn2T3NEAdZYB8%Iv&QB3l>{VXQ#>_)mOpX zkgt;tA$yUHcYf_~qQEPV^P-!ZIrNO)v9$|)kVe^Slret38 z-pI@t9lr;xK%UhQ{{iR-lyCSeumFeuuax1iID)}$VoV?hMT<%NDdFAWS zK+8?dS-|NyoGUO&rSn_D*@e(aA>^XNuO9gHj+v3p*N|c69orune~;T|wO;djIq|5j z#@pk$6Cc}q-uQg^M5PfOt=2xOnB4rE5>9htZoZdx`;Io**oC;xNkI>ZDDsThA5Ye- z=`ZGC;sC&*CtyPcJyn`@8!}&b-<3>}p?zMv`=|cH+f9%5;>EMt=?-#Vu;Kj)jJ>YD zsQ$X0A5x<3ie^Hhb1j|f;bxkfum4Hy94oF!rs4OaAx$ATYR5pv`_bjh_L1`zupe+m zHEuJbT=l^RYqaMrk$+3Llr?m%Zbvsyn+_vhVQgo+FwcsUn+D%}7}(8g){t01lXG{d zEnw3+1`cc;SNA;kD+XVKvZlY;!ikJhsMI>m9&Z9@g!qf3z)3A6uJ3MZZ1Ez+>?N4^3@|0EF zIsPzb`?FM8(Zw+Sb$4c-enMWQXX3@OUF%eZJwW_^857OztGHYcNMjwVwIg5xeX2=OXY@^YhJeKJ+rtXUB;%lWh&j zP__|mwGR&we>^ugOXz3!ADXVHJyFF>t$1DtKkIGk)6Awf9IZ_t>`iwbwx| z4_9tl55cB>5Q`7*Q=!J}j)?mTbx)t{Os7SFN`_cHIbbaU+KId{i({I_B;PnD&u6%N z`oxS}`S#uI0rNL5R}Dd4QP)6hS6QetkhY*$7|#oP#CgQ}M_ARDokJVh*t*wOjaordqvxA z$|b>b<`(Q&Di?^)2AulD%vadvyCF@PuR6l}fn;}PTM!`m43Gf)>`f6FLg{sau>KSP zcNMjI*Ujl?z%6AS?yhQtQ9oAKOjzt4*i{NE0VUTb2*?0k>q4Oy6>qT`di6?e{O9cF z=S|apgWTjgBT}D!KB&anP=E8xDU-iAMHwE$wFo=2IP)HJOTLY9iN@FGtbCmzJ*g`3DDm6HbN zWp%cq&vy3*!T(Hmm~B@(J3Wyuy8GQ{VE4tj>O49ngzXg67g^da(n`;o zw>AC`l`RG^anR;7OqfAy0!v?m17~Y(QFqH8evM?*O8KVXxg*mR1C+vGG@G(I&~#e> z^`g~jPpVjw_`%#wD-==;$E}ZlvCcX50hlIEdo24IP2FOumqqiNi_S|wnda$8ME_h! z5pfF#V)4Sg#ld$#huy#3C!q?Ie4nNpy$Z@{i%e(W*qK1fwb3%?g&$Nd$yB^%te(6! z04ok|%0ORy8f1kuMAu2vu0tC~Vvb8|>Yv}Uh+k3)aynUk5 zfGXCEGAlL*L}VQ1ew{H#Z7?XUxin}iB<8fGhw@pv5^BcFOD2i}W?N5=dBSD9c zUd|l`@b~?hVdAsXMk$|{t#K+ns{ZmY>|SqHc0rWB^$){SA-AK88$p{3(>$2nV=qCU z1yR(u6W`~7Qv%2g@NtciY5U5KinIA?7XU|%Mhbb5I>iQ`fi#`%6D|t?JZ}t6M}7Lr z!<6Gks#v{LG`lMO?Q8s1Qtz+V{$dHI7gGYYmWN_TYZXfPLJa#j6?-7PXHfRDBsn?f zkx{pA@2>5j5s>^@aqMsZov#E8tmIG%Rh35F3Ns?D5~C^aAJ!wqwG4;opg-VuH0y;- z-%gm(MojcYy`0vyueO`YpF065%JYudD$J><`2DZ6^Zfj$6Ije_TywSUL_CnoXYVcg zl)BS&%=>I18_EQ?QDUp>;44xg+2pMP7sO}hq>5qo+o2MC4@6Fz>b_qUeL`hG_4jZxqxQTvvk4CBTs?y znlq)jGix5@5F67}*B8f?Kn{)Xz|lYoT#HlGY9nXXo1)Ey_XZvxLvL5y{35Bs3_p#Dk#S%-Cb#f zhKYGC*yCIE!p-aIj#d|EE}G@n7fk2>I@v>=yF&2y%A>b;);_2EeP4GwdnZnd~xJ>mBU?iZD%(!@fovT$GQ48ep6 zf;(AZw%guzqRPcZa)NYp93)%GnaoO|d?AyXfk5Ragpr-|0AvskXK3fcl^_L(55Mc0 zF6CJJ8BnN7u(tx0w5u8lIJn7q1=yD%?ZI0i&)X8{E^u<9X~M30_xGGRUg>XpBm3eK zzt{t8g|fP0Fl=Vp1liR_6&<>E_lu$ENGtlgM_BirW}dAMt3=KCgw>uQ5rVNS=2vAS=~ecuBYkvhEtd$wBAWaKmRof4{GdFzRhRyc%}f^2d! z2r@_Ei2RCAQ{&0(NQlg3;mnA|b@*d>fusodX+4~@=)TpG18i!ZbgSq`m zWYDKcRSO@j>$VG(;ACaqzx@adVjo|BZs^qWm><-M6Z{d*mxAm7qiG8&yc_Ono~_{U z%{oioV^6WO5j>C)d3Xk-hl!HPOCh2zO$nX* zBXx&tOs4YYzHbn%427CuCLcBka+#Vu#6UNb{x`xx0A#?NZIm3B`XtU8x+B&HvXl?% zE*pn&3^T@z+DFI@QuG_l5wq-5!i>^ASzPcqZV2`Q|GJ8dq?pH2kV7+H(fVL#RQ51e zMAiZ|0~O#*y^%(9m!S@Aw_P`C7lrAA?n{3IS?FeZ|A!ar;UXKQV9$o<_oU!X5rmkM zUW$Bd`6bC)n4UQ3@q|D+mA7PuUjU?w&7s>?-sgm^UU97-y?P(R{UiKsjIyJ)~ z+uLMySh?t6+F_9XPY_*FU_j2ferHRl*|`z~c}HBbMfP5nk6&WwA{sHN_ha}C(LB?a zQO5j0I@b>|9pJW`xdkm;Z}<3PS~p3w>*p@Nj2~%q^>J?m(c3TuyB#l?+^CTL(w8k; z)u?`ljG#?Gv8}1IMb`Pnh9V%y(Mb6l7};ri)`<`AO#qj)AqqNE>^*)2E#Ru$^A*Z6 zF0x9O$KUdWPtKaiAy#ZYhd@Lw$lQcB_fkqKFq~Eb@rM+2PF_9(de|#+5P^(W@!s?6)TZ&;Wzj42Wk54)^wJ-odmy2oE@kNJbv-{_H3j`=r)?r_QR2&oBotTWAky< zBgib_{@T&gpJm>J7EreGRxJ%xsf(jYlP9Tlk$5=*lyt+ zOi$nL)Na7p(_eGlWt#KPM~)9=7dc}y3@ki(7fzqqb!xXJjFr^#@|0bA;GWb?%3|i_ za-ZJsISX>_0_kIPQiR0E?~D?t$02x&KKEh-OKdsP3O%ItT&HueRTIR9E+bCvhEZNE1UaUYi8yP?$7Lw&9fchi7=V;HyRxH z2xer~C+gmZY{-W@vg+A#-Eqz+$X44Mjhs-w3&6G2J|jncq`LKCXr^al_Xg?(m#D<1 zFLE~EEGVvta_-g4|{_ z&G)Zh(bQfo`ooWhbu-|c5BD&M9WD1sK9soEuUAzZv%NpsUDXj!a5xnz6Q;?2%w^VM z1#?>XQ3fzpZ_mA%`uCh*=bzi75yaOYZ*eW}o?u^B)1Eu+HFgJ7s+oKxh&Qn0LQMh` zCJI`-%sobqG7%cu@res2%f8h@TlXP=kn2<9+3Ar=L+Q(5{2xBelhLfmNYx($q})Qu z-50kH(A_5=6!DHrXPEG4D2ju$f&QXiyXeIjpzGL>ac>CK;nK0F$z-D}0nOc4A5yk# zzDB4FTC0Vv3$*ytqz-*~Fhz+DFNP`#Rp3zfnJd-8)K#G0uO_ZN{fI4o zw+*bt+6{2puG?eu_RSZE3v?j*l@?Jqn}E+A>sd$K{#APU!2%kG-ln9;BNstNa%Z8w zhZ1_9J5T+hQBb8A=ce^LdBDb@Z4L}9 zhKOEtv^1TXVq|5wD9QTfN`Thn166y#y-o_1qN&G2ZV!=Krtn!tBOG7(4rYK<$WFzm z6Egnpz-wFJX-R-(?{j>O3R#-?hseW~f~gf}K<8J0idAhhBY));o6-+g7H+n8*#$9X zL^!rl77Tw*)wLx~ejxB*rF15MrdTuL++v=WqLUy^ZG~5LhDcmY=h+(XM=wi+w^rLT zQ~+nfgg){VkcA^oS~q>Hf%jPJlP{apsfKESg1y&<146yhdZbrzy=ua4mBOc4>SmCf zNlS9ej7QHQh`Q-xAc&46Fd!ws7{DYUaFZi3E zZrar?-(3Z{%oR){=nGA$k=}b4i18;8U(ycr=Uaq*4%u93H`VfGsKll}R2#o^YWOQk zJClEor%LVDVu_V`KK%Id=znZJ5wI@at`)pa4O1|~LOnXXwe|cU2y?V9^PQ4avL_$` zD?lhQABMnE%;RTi67Ny&6XBEl&LM(49rCk%EG|| zp0kfwFs#rN2^07TPJ`gJmX6?Yq$RWL82C=Q1=?%N-eQ@r!$BKhjRo)cHV|&rImfbu z3reABew!CIC>?B*$G?!=ey`I_94IRMEuRM1z2XVZqd^p0erk*1LE-xzyTqi;(>?4P zcALa{<>Mbqoqp?hePbrnl!Y^uks95X4n1MCI-p^02-?{w>!9aRB6N87hl%-Gl(g`Z z(*T7HK!5A-AW1-Ly%vt#Uvkf*#C4lw#h9GN0W6U==+x8EG5a z1Er^WbZzEc&;kskSM}DHS}6+(imInQ=hdtWE33xLl5VphBQ<0`MWwz8^jms@hZj-D z5<*|(iWr)+8p@x~57?W}7B;pt7riReeczvpi78b9B6*7f zdid>8i!Oy=h?!i;js*7iC4D5k_Ax-h^C&4hCTHaY9v72ZfEVKtkn|?qZOIWhUabv9@I(akR#p015pE#x}mcA-zB*Cj0vKu750A5a|5e(1=d%%eT(b zfQ(b$q(&DbTg=ysa1H|sDrTpJfdrt%0&yA?yNWrY+<3 zaDEvQl7nvUz>h1<#246<;Fy=iHd@p&eg&Lg`$HUSmv8i6$rx0dS2z6NJn4R|v@PKz zlSZdCfv!Iq+b+`U;wpiMM6bG4VwH}3Y$3MhfBNA-lH(c6Wy&xDk#Gl)dzT%Q2_6vmY5=LL+?vpzrw$U zcqJ=@#7fbn*O4k^8vZ^L$7OH>bc~y%o>!N$5{#@!ohxrftpmSrH&w@Tf>$1Vi^N^& zRQul0MH9rW>k9no4HhE3SNa37Xr_yz1Vq%YL3ywVag~HlcyNeI2ab6!`KP_*%7Dn9 z`Nn%^f=%?~D6GV*g2E@2!bF$9{G^ce%V=^+c-9M*5*mkc5c#=z;^lWxKwV?lkT&VU zsaG{s7_HGXQ9+I`$k^Hn2}Vh6>BKxMl`WeCI@vB$R1h0Wi$9pNMYjfn{E&!DZ*n$D z89}sf@niBr;*XRLO1u!rz?1T_nPWi)BaHkqVPxade&lxodiUmed}%n-T7>vv?h%!l zZ>K5%{mfmVjAOtl=$x|EEOo!`cZ(wZZe(5brHcg#c!shVx1fj2MR7%{&lTWN{QW>j z%U;wUunp?o2OCXlv7(1`j+s%X&w=_4JqI$B zRKkZL#XtExKcWq%syHknv}Vu^db& zQ2uJ!s_hb5(sVlRtw_wl1Ts&b_vs4NLC8Y<>;e(uE_o8>_g#+;DLcZrn$Z)Kd8DYW z*LNg3=6;SkLLsG&Z@rlc3|z1Ny^&yox?83K+*>ivAo()9Eg|K)W=!{7&hV;bosAw&pA1`r{%9p_EmjIwWEmsqV`$& zrTe&FsP>$d@j^&m!2|e2V!0fmfSl^X{pKzk0Hx%AZleTOb>p1C0y!5o$3C(&uR!;_ z<&KC_Xd zIGfe`N3XrZ0kcSEWEsh0EyicKQ}c>B;S~5zhT<{ot0Iv&92~E-d$|~)Fq~iuh#DG^ zl@H=qL=K`61mjEuo7O1r>K}KFM5Z>A-B-?@3h(D1 zb7!%3ad#3~`s8hs;xerAE35fSo6b4X^m`qwmba2Ks~vHnz<2g~DYnY!d`o4T&$n5% z4s5e1X+5*zZPfnsS!-R2XiY<6L|=W|u;GZW5-U_Zi*tWS=d3DYBGn4qgH$@tPULNs zU&ZU6t5%CeCJtCS#u_{bZ5C6iz5uFnJVv$g^%=HZtnTr$@k{#Dv}u{L zmS}EN)v+i6Bcj=`SR`70m1LtnVdP=@`B~#B&(Tp%ntn!r$oNd4fE(GIzFlJi$E-_)$l zF?J&PMxO^3fBz_+z|Cbh(V^oGYD4v@P%zA-PU-f^S)7CPZD95a=zda@q1LmOZ&nFx+z7OCz|95)NgKGv>8#Uo65RiT%ZHf z&>^N_C7%E!P?2>))o}d?#JX6-iMd3!+;#)LA_=}~tWbfu_ zcdvLsU{B(9rJ*}UMMG{5Jd(0r5pM30Zlqo$=4lC>NL->U3qzUFB@b)%=wXh~~q zJ>6P1O?Q0avtJ8;1NNPAoL~6mP$2(isA=s2p~wYROwqJp|1|US^E^$p4wt0v<5000 zoXla}d*v#7J*D9&>cyAk<#)UN@^+O(0tQV6a0vAnO4e_JZe--z5_E$ehJxsCsI$JP z#QGD7*C&WQIC7Y@7Exo;W`(DBPPXpU18$YzMP74)YJ4VQeX&Mvao70HbFBDO$!)UgQF?U+5P&)u-CHkxUrl)CY7j||o22&d#eZ1&vYylFD` z$F8y;f3JmvtFQ%QJ{odcV(f&sLT)ylAD*O^YM*on#BC5E@2SX-=HQ2wsxnkW;T(SJi zd<+3;oJs%ELF*kP(nq0hV>V%1AD1$|4;P5}7R*j-sw#F;uKZB2cA{)}Ha1*~0-7ni zR%@PdeQIc{Sz`R5JcEsYZ{4Jk`^=ZELu%{w)jYOex(BVlMe<S6J8rRfDvW8 zxi%7eSH4&KkVB#BoC$aFEGeKnH`Cwa+Ch9@RM{-osnj;jgd2C-4Z^Sjhefy4y!N~U z=q{*(d72ONKe9!>0Te+_84d#`HIvBW7p$Fo(PtUqTRslc3dL4+#KNeRYceEkS*eIb zZe#&WZ%9c2UeK|+09s7|F*X^owpE}MwVwEEe3y#DcAZ7iUTd)i}Bx>^+T(fZJrqlDAtT3%tRK<4yw zL#vPrN8&BS7Zl;vR;){+iHsnBob17QA4(L+Q2RW0sq9Yrmdw~w?Oz|1ws@$EcpZWOo(+ z#W>y)xLdSSaoSGf>$H6>&Bcsl^(RtmrQ{kC-F*trq%Wkt!-sP<;0~fb1x9ct@cj># zJ!$cYOLE49vEJrN94e}}thE=%U~K_){DpRSIw#y7@zlV3$V0`938~9@r(d1Db-%|2 z@kBCtqEh^4-M-*RFlJ3J#!&d8I=#vgU*~t2ohIecLI~tk!ljFC`1MGN4mpaztgo7i zm)?1vMqv8;;qJPP*ZNRv%2{3xv^>eKZX_OZ_LW$)G%?9vkQ{nHDOAWGWq?h6Y*pjG zyK%x^ePbK+p;@hq>{X=s9FEb$jYBs7DB1`?G0$u+X*rPHlY9f(g|Sv2f+d@AH`I>v z=K&!eF1u@bI%Qh78&_P&wl!87(@-G{{*i@b(i zSD2BiqL{ZtbmHxFgoIWNIN*nJ>%C)5Gcx^ioOZ1Hgn@&qKb?vc<#(xWtZe;kVu-Ha ztdKQuy_o714oM_HdO}@ahNJjk^ZRhnvl8SFevf@>!y%&OvnBelL8mxM4-E-f@Stdd1+Q00;-Z z;sZZ4Z3)wG{+>G?(6}q^iFcbRpnEZPOCa_68uR)bEe=xDX#pJ193%y>$Ja>eqPwSyNtCPzXC?3_<;DUh&r^tF= zOZf3+sir;!xCZawU?eFnNllr!1(MWil9>C*&Bn7}7ZHzArk>wh-Cgf^57qTzXAznK z!}2?h%Xm(UlXF`Z+fHyNJ?THmEAeSBp(iT#9{@&re?5FzXTo*D*3xM~bbWwB*ww;zcg1!=oq`7-R$>tw z--J!C4G*2#Ac)KzUhPi?N3$=+`p%A;s^DWJRUmHFjInHdcsS9n`oRnreCeEaz9J1% zI~~bUGOkIShR^WUj&7WzW-|h7H?mGP95Y4_m@)8fK)})X#p+ju=tq48IEJ_LA#3gP za2Qip=QW?1_al>(lf+`LpN^aW`;ojNlC`^7|e6d{e2v{6RO8 z@xYbvNXss|jq(RRaoyne-!a=c;6hm6P99<+*OPhgW?ha*>Yya|Y#CH0G=bLb(A2{R zZ+RUqCv^KpRKA;$R&{!mTrazgW$vIPgWB|Kz5;Jza=fUfvDRPP7r#k8zk?7l6&u&)&AZ8GWNW%N5BrucUX}e!q#CN?21__3}G9XdzK?u~pp$NsFb~ zrz+D35KUMxJ=1s85jSOUcp$q1`r_%^x@0#XB#`Rl5IrCcxu<^~c`x`n`avnSj-L)GHsf zYe&jPws8w>yCsShX}S6GO9|Rx-!P?V-cv0PlN+_XW0* z9(G1E&-kbBt#y%GE#kle!RHm>cUNyc1FM$R$&98uC(n$s{~;4`)Xg7OLwLW6jP%*z zid6NQ{YEGu@qvDGXMzDpXXo}(tc*neu09$|fvO(YxU9{LyR7%XWo1Ji{$cZb1OB80gQ9VU zaCQRj&)^W9XGbb7SvbepW0b7W)^pSa-QuKVzX-*IRfG#yUoE{>3IGYzm1NnSS&|jF zER&(h&;Oxo@(ey+Re=8*OtL~i{@uNiYIZG^%c9`l38hV%y0ltHUOmWHSo_N&1vSp( zOP=otkp{Z^ZvL$4N8D=8i4!DZpnlOPo4nL701qyXy{NH&O)RKyXlUQJ8Pm&In-!4CxQDnfz@zETH;KyCHDX8{#gKDmUYgT zLHB5)1Sble5Ryy#RKX?fxql!2k5lDhMItjbc~rnN1eaGQ9mtGvbyIMX1}xHx&)6;o zm#EXJ3zZJ#rUL>K{2cPFAO(*N92$=Npjm2G`x32q4zUe!Z<6qLmH)%k3(E5JD^HzFl^dAbRqvF%OEey3Vzcn@T@Vf3QPX?*|KnNFZFrAe*yUXfOW6HKo%~B zpApikf;+>sh*6s2>#xs`_`&~=HT)Nmc_PQ!%fC9wOnM0T!GedJ=MVoE{az0)fpEkj zV!*Ri4u#zeE|2b0oxjpZ=T8p}MM-plW}qgq&Ex!gfTRaMr~k3ak;EMu-Gyi@zOcP! z(QA++K#mj>z?-bIov+gSd%-z{D$pyuGRfS0w;6o40SIz3XM-=`(z0MJr$7^6Xshh! zXJZM>>6r#BpBwqlv&o>SR81kWKRC2|Blv_U=AYZpy+xM2fC9Yp2+#jC z7?PA5o^XEl63YPq%+raUrzqz}2Eu4?MGvsEcQ~HTDm3K$;h=<$GxB<=|Ke-G%w)=c zO}zc_Bj;&Nz|(RLF~wcd59160=FK_u`Ch{~$Mb*vz8^8I7FXim#KaW*HLt&`{J-Y? zf6e>l5qo;<9y-Y|GKUiF?!9TSWr=c&`|!(@^C)$=0!46zt=ba z_0=(ANX?^W5SrswNJk?zUC1RR^N6o4v1;`v0iV~Xx~BIWy?RZlO{O{Uf5P&eg3zm( zVQ;2`GK=IZrc%!d3Y!SkFR4#)v1Bq^5TX!d6-KLvPwNY{e=^pz>*Vm${lU?$&Q_a3 z;7z{|xV^{KiUhL#KL4*>UIr?}=l)$4*4e<((ZKOT5a?h~6Z`9H_o+gR`k=xKJdYF? z5aPk&;=tmfU<`_u&iu5tKB-xt8Y$H{A%4P*ua`f9WF1&0L4#4`qwlvLL2sFr6+ZH= zaSo9c6aaZ$3S9d=MX!4_FE_jQih~r02ypxbwl{qbDg+B3QUs+8P!nTGK~<2cAW)uB zP@@etywUwK^sO?}RDlUUvNC{92cndjur|ds_vN?zKnd&HWWC=_F6=9qyTB?`W`XNd z-!BCLPcQoc!z(B54Y$zlI$)~;WQ4;eQ1c2rrFBv+epPJ(u<6oR+ObL*k|Gy_O7C@h zj!gFrIer>EN-@6#I3Su@^PGf z1vw~u0k*NsS(|-)Il$=+T3P)!9Q6w%U>z{sW#L=AM4FNCzmGe3kAc;juVbvr^ zSSx*yW(3bNEmQ@~GQ}(T$a;ZoQ~1mRZlyhIpTb}Oa>w5#r*l^KsX~ohss?OBm?p}` zv#fFirjV;Ey2?bs;VaO1L^y%H0hqG?ObB3h0yaT-SDJ^2gX4x#aD@jjGz1kP4JG@R zLbF#nL-JII!y{l07rvgls^A1PP0XD8N)2k;Pq16oADaPeS$foT8kA~4;`$88DwTaR z<%5_GfZF-MguHpJ)iiK2ZQz(P3)sH9(o$L@xPlE>o`o&ex)llyNg;noNPcv7;^hD} z4?%;1kR;%;Knd6;b!&k%V*f7j&RN9=^K~aMqx!~7UwKFooD8>Xrj)Ik4YlnF#4S?E zz*fJ)N7X}D7DCK30$DZbM_Qc+C&Oe&+??6=Dgqk(0a?Iyaggw}kFE=(fc}o|U6m0I z4J!{DP|OJ4Kf=#xs-VKOR3N_Qh5PyX`=-So4qD%|WPkhO-`e@z&BvPU#CP$%u$s{N zn&GpZ{>RiyS2h~?FA#Y&yK)5+I6nccQUOM-kcX@rivuwH3t^%UfvvH&+vckxnSh}x zd?Rz!CP)TP14f1M<*Qj4zQA(h8cXV2XK1i>0sXJrH~X7VV-qmbOG!XIA)p5wVc$`^ z7(5_t-Z*!eRyQP3wgE%dICgE@H5Q-(-+*hDupqVo`ufq1OnpJ%{-|SMwZM`^1(X*P zWtY8b^K}rszu~w0R!F`$VFuK_?RWfI;1%&dCN;h)fLe/test'], - testMatch: ['**/*.test.ts'], - transform: { - '^.+\\.tsx?$': 'ts-jest' - } -}; diff --git a/lib/tezos/lib/assets/cfn-hup/cfn-auto-reloader.conf b/lib/tezos/lib/assets/cfn-hup/cfn-auto-reloader.conf deleted file mode 100644 index 3cd32a0a..00000000 --- a/lib/tezos/lib/assets/cfn-hup/cfn-auto-reloader.conf +++ /dev/null @@ -1,4 +0,0 @@ -[cfn-auto-reloader-hook] -triggers=post.update -path=Resources.WebServerHost.Metadata.AWS::CloudFormation::Init -action=/opt/aws/bin/cfn-init -v --stack __AWS_STACK_NAME__ --resource WebServerHost --region __AWS_REGION__ diff --git a/lib/tezos/lib/assets/cfn-hup/cfn-hup.conf b/lib/tezos/lib/assets/cfn-hup/cfn-hup.conf deleted file mode 100644 index 2163b37a..00000000 --- a/lib/tezos/lib/assets/cfn-hup/cfn-hup.conf +++ /dev/null @@ -1,5 +0,0 @@ -[main] -stack=__AWS_STACK_ID__ -region=__AWS_REGION__ -# The interval used to check for changes to the resource metadata in minutes. Default is 15 -interval=2 diff --git a/lib/tezos/lib/assets/cfn-hup/cfn-hup.service b/lib/tezos/lib/assets/cfn-hup/cfn-hup.service deleted file mode 100644 index 2660ea46..00000000 --- a/lib/tezos/lib/assets/cfn-hup/cfn-hup.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=cfn-hup daemon -[Service] -Type=simple -ExecStart=/usr/local/bin/cfn-hup -Restart=always -[Install] -WantedBy=multi-user.target diff --git a/lib/tezos/lib/assets/copy-data-to-s3.sh b/lib/tezos/lib/assets/copy-data-to-s3.sh deleted file mode 100644 index 6a9052e2..00000000 --- a/lib/tezos/lib/assets/copy-data-to-s3.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set +e - -systemctl stop node.service - -source /etc/environment -# aws s3 sync ~/.tezos-node/ s3://$S3_SYNC_BUCKET/ -s5cmd sync /home/tezos/.tezos-node/ s3://$S3_SYNC_BUCKET/node/ - -echo "Synced node to S3" - -systemctl start node.service \ No newline at end of file diff --git a/lib/tezos/lib/assets/cw-agent.json b/lib/tezos/lib/assets/cw-agent.json deleted file mode 100644 index 28833017..00000000 --- a/lib/tezos/lib/assets/cw-agent.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "agent": { - "metrics_collection_interval": 60, - "run_as_user": "root" - }, - "metrics": { - "aggregation_dimensions": [ - [ - "InstanceId" - ] - ], - "append_dimensions": { - "InstanceId": "${aws:InstanceId}" - }, - "metrics_collected": { - "cpu": { - "measurement": [ - "cpu_usage_idle", - "cpu_usage_iowait", - "cpu_usage_user", - "cpu_usage_system" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ], - "totalcpu": false - }, - "disk": { - "measurement": [ - "used_percent" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ] - }, - "diskio": { - "measurement": [ - "io_time", - "write_bytes", - "read_bytes", - "writes", - "reads", - "write_time", - "read_time", - "iops_in_progress" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ] - }, - "mem": { - "measurement": [ - "mem_used_percent", - "mem_cached" - ], - "metrics_collection_interval": 60 - }, - "netstat": { - "measurement": [ - "tcp_established", - "tcp_time_wait" - ], - "metrics_collection_interval": 60 - }, - "swap": { - "measurement": [ - "swap_used_percent" - ], - "metrics_collection_interval": 60 - } - } - } -} diff --git a/lib/tezos/lib/assets/download-snapshot.sh b/lib/tezos/lib/assets/download-snapshot.sh deleted file mode 100644 index b5326ce2..00000000 --- a/lib/tezos/lib/assets/download-snapshot.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set +e - -source /etc/environment - -echo "Downloading TZ snapshot from $TZ_SNAPSHOTS_URI." - - -TZ_SNAPSHOTS_FILE_NAME=snapshot - -mkdir ~/tezos-snapshots -wget -O ~/tezos-snapshots/$TZ_SNAPSHOTS_FILE_NAME $TZ_SNAPSHOTS_URI -octez-node snapshot import ~/tezos-snapshots/$TZ_SNAPSHOTS_FILE_NAME -rm ~/tezos-snapshots/$TZ_SNAPSHOTS_FILE_NAME - - -echo "TZ snapshot is ready !!!" diff --git a/lib/tezos/lib/assets/node-cw-dashboard.ts b/lib/tezos/lib/assets/node-cw-dashboard.ts deleted file mode 100644 index 3153f429..00000000 --- a/lib/tezos/lib/assets/node-cw-dashboard.ts +++ /dev/null @@ -1,216 +0,0 @@ -export const SyncNodeCWDashboardJSON = { - "widgets": [ - { - "height": 5, - "width": 6, - "y": 0, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "CPUUtilization", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "CPU utilization (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 18, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "NetworkIn", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Network in (bytes)" - } - }, - { - "height": 5, - "width": 6, - "y": 0, - "x": 18, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "NetworkOut", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Network out (bytes)" - } - }, - { - "height": 5, - "width": 6, - "y": 10, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 300, - "metrics": [ - [ "CWAgent", "mem_used_percent", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Mem Used (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 300, - "metrics": [ - [ "CWAgent", "cpu_usage_iowait", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "CPU Usage IO wait (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 0, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ { "expression": "m7/PERIOD(m7)", "label": "Read", "id": "e7" } ], - [ "CWAgent", "diskio_reads", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7", "visible": false, "stat": "Sum", "period": 60 } ], - [ { "expression": "m8/PERIOD(m8)", "label": "Write", "id": "e8" } ], - [ "CWAgent", "diskio_writes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m8", "visible": false, "stat": "Sum", "period": 60 } ] - ], - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Sum", - "period": 60, - "title": "nvme1n1 Volume Read/Write (IO/sec)" - } - }, - { - "height": 4, - "width": 6, - "y": 0, - "x": 12, - "type": "metric", - "properties": { - "metrics": [ - [ "CWAgent", "tz_current_block", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "sparkline": true, - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Maximum", - "period": 60, - "title": "Tezos Client Block Height" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 6, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Sum", - "period": 60, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ { "expression": "IF(m7_2 !=0, (m7_1 / m7_2), 0)", "label": "Read", "id": "e7" } ], - [ "CWAgent", "diskio_read_time", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_1", "visible": false, "stat": "Sum", "period": 60 } ], - [ "CWAgent", "diskio_reads", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_2", "visible": false, "stat": "Sum", "period": 60 } ], - [ { "expression": "IF(m7_4 !=0, (m7_3 / m7_4), 0)", "label": "Write", "id": "e8" } ], - [ "CWAgent", "diskio_write_time", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_3", "visible": false, "stat": "Sum", "period": 60 } ], - [ "CWAgent", "diskio_writes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m7_4", "visible": false, "stat": "Sum", "period": 60 } ] - ], - "title": "nvme1n1 Volume Read/Write latency (ms/op)" - } - }, - { - "height": 5, - "width": 6, - "y": 10, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ { "expression": "(m2/1048576)/PERIOD(m2)", "label": "Read", "id": "e2", "period": 60, "region": "${REGION}" } ], - [ "CWAgent", "diskio_read_bytes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m2", "stat": "Sum", "visible": false, "period": 60 } ], - [ { "expression": "(m3/1048576)/PERIOD(m3)", "label": "Write", "id": "e3", "period": 60, "region": "${REGION}" } ], - [ "CWAgent", "diskio_write_bytes", "InstanceId", "${INSTANCE_ID}", "name", "nvme1n1", { "id": "m3", "stat": "Sum", "visible": false, "period": 60 } ] - ], - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 60, - "title": "nvme1n1 Volume Read/Write throughput (MiB/sec)" - } - }, - { - "height": 3, - "width": 6, - "y": 15, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ "CWAgent", "disk_used_percent", "path", "/data", "InstanceId", "${INSTANCE_ID}", "device", "nvme1n1", "fstype", "xfs", { "region": "${REGION}", "label": "/data" } ] - ], - "sparkline": true, - "view": "singleValue", - "region": "${REGION}", - "title": "nvme1n1 Disk Used (%)", - "period": 60, - "stat": "Average" - } - } - ] -} diff --git a/lib/tezos/lib/assets/setup-s3-sync-service.sh b/lib/tezos/lib/assets/setup-s3-sync-service.sh deleted file mode 100644 index e20cb6ad..00000000 --- a/lib/tezos/lib/assets/setup-s3-sync-service.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -set +e - -source /etc/environment - -echo "Setting up s3-sync as service" -cat >/etc/systemd/system/s3-sync.service </etc/systemd/system/s3-sync.timer <> /etc/environment -echo "ASSETS_S3_PATH=${_ASSETS_S3_PATH_}" >> /etc/environment -echo "TZ_SNAPSHOTS_URI=${_TZ_SNAPSHOTS_URI_}" >> /etc/environment -echo "STACK_NAME=${_STACK_NAME_}" >> /etc/environment -echo "STACK_ID=${_STACK_ID_}" >> /etc/environment -echo "RESOURCE_ID=${_NODE_CF_LOGICAL_ID_}" >> /etc/environment -echo "TZ_HISTORY_MODE=${_TZ_HISTORY_MODE_}" >> /etc/environment -echo "TZ_NETWORK=${_TZ_NETWORK_}" >> /etc/environment -echo "TZ_DOWNLOAD_SNAPSHOT=${_TZ_DOWNLOAD_SNAPSHOT_}" >> /etc/environment -echo "TZ_OCTEZ_DOWNLOAD_URI=${_TZ_OCTEZ_DOWNLOAD_URI_}" >> /etc/environment -echo "LIFECYCLE_HOOK_NAME=${_LIFECYCLE_HOOK_NAME_}" >> /etc/environment -echo "AUTOSCALING_GROUP_NAME=${_AUTOSCALING_GROUP_NAME_}" >> /etc/environment -echo "INSTANCE_TYPE=${_INSTANCE_TYPE_}" >> /etc/environment -echo "S3_SYNC_BUCKET=${_S3_SYNC_BUCKET_}" >> /etc/environment -source /etc/environment - -arch=$(uname -m) - -echo "Architecture detected: $arch" - -if [ "$arch" == "x86_64" ]; then - SSM_AGENT_BINARY_URI=https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm - AWS_CLI_BINARY_URI=https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip - S5CMD_URI=https://github.com/peak/s5cmd/releases/download/v2.1.0/s5cmd_2.1.0_Linux-64bit.tar.gz -else - SSM_AGENT_BINARY_URI=https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_arm64/amazon-ssm-agent.rpm - AWS_CLI_BINARY_URI=https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip - S5CMD_URI=https://github.com/peak/s5cmd/releases/download/v2.1.0/s5cmd_2.1.0_Linux-arm64.tar.gz -fi - -echo "Updating and installing required system packages" -yum update -y -amazon-linux-extras install epel -y -yum groupinstall "Development Tools" -y -yum -y install amazon-cloudwatch-agent collectd jq gcc ncurses-devel telnet aws-cfn-bootstrap - -cd /opt - -# echo 'Installing AWS CLI v2' -# curl $AWS_CLI_BINARY_URI -o "awscliv2.zip" -# unzip -q awscliv2.zip -# ./aws/install -# rm /usr/bin/aws -# ln /usr/local/bin/aws /usr/bin/aws - -echo "Downloading assets zip file" -aws s3 cp $ASSETS_S3_PATH ./assets.zip --region $AWS_REGION -unzip -q assets.zip - -echo 'Configuring CloudWatch Agent' -cp /opt/cw-agent.json /opt/aws/amazon-cloudwatch-agent/etc/custom-amazon-cloudwatch-agent.json - -echo "Starting CloudWatch Agent" -/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ --a fetch-config -c file:/opt/aws/amazon-cloudwatch-agent/etc/custom-amazon-cloudwatch-agent.json -m ec2 -s -systemctl status amazon-cloudwatch-agent - -aws configure set default.s3.max_concurrent_requests 50 -aws configure set default.s3.multipart_chunksize 256MB - -echo 'Installing SSM Agent' -yum install -y $SSM_AGENT_BINARY_URI - -echo "Installing s5cmd" -cd /opt -wget -q $S5CMD_URI -O s5cmd.tar.gz -tar -xf s5cmd.tar.gz -chmod +x s5cmd -mv s5cmd /usr/bin -s5cmd version - -if [[ "$STACK_ID" != "none" ]]; then - echo "Install CloudFormation helper scripts" - mkdir -p /opt/aws/ - pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz - sudo ln -s /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup - - echo "Configuring CloudFormation helper scripts" - mkdir -p /etc/cfn/ - mv /opt/cfn-hup/cfn-hup.conf /etc/cfn/cfn-hup.conf - sed -i "s;__AWS_STACK_ID__;\"$STACK_ID\";g" /etc/cfn/cfn-hup.conf - sed -i "s;__AWS_REGION__;\"$AWS_REGION\";g" /etc/cfn/cfn-hup.conf - - mkdir -p /etc/cfn/hooks.d/ - mv /opt/cfn-hup/cfn-auto-reloader.conf /etc/cfn/hooks.d/cfn-auto-reloader.conf - sed -i "s;__AWS_STACK_NAME__;\"$STACK_NAME\";g" /etc/cfn/hooks.d/cfn-auto-reloader.conf - sed -i "s;__AWS_REGION__;\"$AWS_REGION\";g" /etc/cfn/hooks.d/cfn-auto-reloader.conf - - echo "Starting CloudFormation helper scripts as a service" - mv /opt/cfn-hup/cfn-hup.service /etc/systemd/system/cfn-hup.service - - systemctl daemon-reload - systemctl enable --now cfn-hup - systemctl start cfn-hup.service - -fi - -echo "Install Octez-node and its dependencies" - -if [ "$arch" == "x86_64" ]; then - # curl -o octez-binaries.tar.gz https://gitlab.com/tezos/tezos/-/package_files/133747462/download - curl -o octez-binaries.tar.gz $TZ_OCTEZ_DOWNLOAD_URI - tar xf octez-binaries.tar.gz - mv ./octez-x86_64/* /usr/local/bin/ - else - # curl -o octez-binaries.tar.gz https://gitlab.com/tezos/tezos/-/package_files/133748628/download - curl -o octez-binaries.tar.gz $TZ_OCTEZ_DOWNLOAD_URI - tar xf octez-binaries.tar.gz - mv ./octez-arm64/* /usr/local/bin/ -fi - - - -find /usr/local/bin/ -name "octez-*" -exec chmod +x {} \; -groupadd tezos -adduser -g tezos tezos -#mkdir -p /var/tezos/node -#chown -R tezos:tezos /var/tezos - -echo "Changing user to tezos" - - -echo "Installing zcash dependency" -curl -o /tmp/fetch-params.sh https://raw.githubusercontent.com/zcash/zcash/713fc761dd9cf4c9087c37b078bdeab98697bad2/zcutil/fetch-params.sh -chmod +x /tmp/fetch-params.sh -su tezos -c "/tmp/fetch-params.sh" - -echo "Configuring node" -su tezos -c "octez-node config init --network=$TZ_NETWORK --history-mode=$TZ_HISTORY_MODE --net-addr='[::]:9732' --rpc-addr='[::]:8732' --allow-all-rpc [::]:8732" - -# Signal completion to CFN -echo "Signaling completion to CloudFormation. The node will still sync/import data" -cfn-signal --stack $STACK_NAME --resource $RESOURCE_ID --region $AWS_REGION - - -# download snapshot if network is mainnet -if [ "$INSTANCE_TYPE" == "SNAPSHOT" ] || [ "$INSTANCE_TYPE" == "SINGLE" ]; then - if [ "$TZ_NETWORK" == "mainnet" ] && [ "$TZ_DOWNLOAD_SNAPSHOT" == "true" ]; then - echo "Downloading Tezos snapshot and importing" - chmod +x /opt/download-snapshot.sh - su tezos -c "/opt/download-snapshot.sh" - fi -fi - - -if [[ "$INSTANCE_TYPE" == "HA" ]]; then - # su tezos -c "aws s3 sync s3://$S3_SYNC_BUCKET/node ~/.tezos-node/node" - su tezos -c "s5cmd sync 's3://$S3_SYNC_BUCKET/node/*' /home/tezos/.tezos-node/" -fi - -if [[ "$INSTANCE_TYPE" == "SNAPSHOT" ]]; then - chmod +x /opt/copy-data-to-s3.sh - su tezos -c "/opt/copy-data-to-s3.sh" - chmod +x /opt/setup-s3-sync-service.sh - /opt/setup-s3-sync-service.sh -fi - - -echo "Setting up node as service" -cat >/etc/systemd/system/node.service </etc/systemd/system/sync-checker.service </etc/systemd/system/sync-checker.timer < { - switch (dataVolumeType) { - case "gp3": - return ec2.EbsDeviceVolumeType.GP3; - case "io2": - return ec2.EbsDeviceVolumeType.IO2; - case "io1": - return ec2.EbsDeviceVolumeType.IO1; - case "instance-store": - return constants.InstanceStoreageDeviceVolumeType; - default: - return ec2.EbsDeviceVolumeType.GP3; - } -}; - -export const baseConfig: configTypes.TzBaseConfig = { - accountId: process.env.AWS_ACCOUNT_ID || "xxxxxxxxxxx", - region: process.env.AWS_REGION || "us-east-1" -}; - -const getArch = (cpuType: string | undefined) => { - switch (cpuType?.toLowerCase()) { - case "x86_64": - return ec2.AmazonLinuxCpuType.X86_64; - case "arm64": - return ec2.AmazonLinuxCpuType.ARM_64; - default: - return ec2.AmazonLinuxCpuType.X86_64; - } -} - -const getDownloadUri = (arch: ec2.AmazonLinuxCpuType) => { - // defaults to v0.20 - switch (arch) { - case ec2.AmazonLinuxCpuType.X86_64: - return process.env.TZ_X86_OCTEZ_URI || "https://gitlab.com/tezos/tezos/-/package_files/133747462/download"; - case ec2.AmazonLinuxCpuType.ARM_64: - return process.env.TZ_ARM64_OCTEZ_URI || "https://gitlab.com/tezos/tezos/-/package_files/133748628/download"; - default: - return process.env.TZ_X86_OCTEZ_URI || "https://gitlab.com/tezos/tezos/-/package_files/133747462/download"; - } -} - -const getSnapshotUri = (network: configTypes.TzNetwork, historyMode: configTypes.TzNodeHistoryMode, snapshotRegion: configTypes.TzSnapshotRegion, snapshotsUrl: string) => { - if (snapshotsUrl !== constants.NoneValue) { - return snapshotsUrl; - } - - return `https://snapshots.${snapshotRegion}.tzinit.org/${network}/${historyMode}` -} - -export const baseNodeConfig: configTypes.TzBaseNodeConfig = { - instanceType: new ec2.InstanceType(process.env.TZ_INSTANCE_TYPE ? process.env.TZ_INSTANCE_TYPE : "c7g.xlarge"), - instanceCpuType: getArch(process.env.TZ_CPU_TYPE), - tzNetwork: process.env.TZ_NETWORK || "mainnet", - historyMode: process.env.TZ_HISTORY_MODE || "full", - snapshotRegion: process.env.TZ_SNAPSHOT_REGION || "us", - octezDownloadUri: getDownloadUri(getArch(process.env.TZ_CPU_TYPE)), - snapshotsUrl: getSnapshotUri( - process.env.TZ_NETWORK || "mainnet", - process.env.TZ_HISTORY_MODE || "full", - process.env.TZ_SNAPSHOT_REGION || "us", - process.env.TZ_SNAPSHOTS_URL || constants.NoneValue - ), - dataVolume: { - sizeGiB: process.env.TZ_DATA_VOL_SIZE ? parseInt(process.env.TZ_DATA_VOL_SIZE) : 2000, - type: parseDataVolumeType(process.env.TZ_DATA_VOL_TYPE?.toLowerCase() ? process.env.TZ_DATA_VOL_TYPE?.toLowerCase() : "gp3"), - iops: process.env.TZ_DATA_VOL_IOPS ? parseInt(process.env.TZ_DATA_VOL_IOPS) : 10000, - throughput: process.env.TZ_DATA_VOL_THROUGHPUT ? parseInt(process.env.TZ_DATA_VOL_THROUGHPUT) : 700 - }, - downloadSnapshot: process.env.TZ_DOWNLOAD_SNAPSHOT ? process.env.TZ_DOWNLOAD_SNAPSHOT : "true" -}; - -export const haNodeConfig: configTypes.TzHAConfig = { - albHealthCheckGracePeriodMin: process.env.TZ_HA_ALB_HEALTHCHECK_GRACE_PERIOD_MIN ? parseInt(process.env.TZ_HA_ALB_HEALTHCHECK_GRACE_PERIOD_MIN) : 15, - heartBeatDelayMin: process.env.TZ_HA_NODES_HEARTBEAT_DELAY_MIN ? parseInt(process.env.TZ_HA_NODES_HEARTBEAT_DELAY_MIN) : 40, - numberOfNodes: process.env.TZ_HA_NUMBER_OF_NODES ? parseInt(process.env.TZ_HA_NUMBER_OF_NODES) : 2 -}; diff --git a/lib/tezos/lib/constructs/tz-node-security-group.ts b/lib/tezos/lib/constructs/tz-node-security-group.ts deleted file mode 100644 index 76c8548f..00000000 --- a/lib/tezos/lib/constructs/tz-node-security-group.ts +++ /dev/null @@ -1,47 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as nag from "cdk-nag"; - -export interface TzNodeSecurityGroupConstructsProps { - vpc: cdk.aws_ec2.IVpc; -} - -export class TzNodeSecurityGroupConstructs extends cdkConstructs.Construct { - public securityGroup: cdk.aws_ec2.SecurityGroup; - - constructor(scope: cdkConstructs.Construct, id: string, props: TzNodeSecurityGroupConstructsProps) { - super(scope, id); - - const { - vpc - } = props; - - const sg = new ec2.SecurityGroup(this, `rpc-node-security-group`, { - vpc, - description: "Security Group for Blockchain nodes", - allowAllOutbound: true - }); - - - // ports - sg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(9732), "Peer connection port"); - sg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.udp(9732), "Peer connection port"); - sg.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(8732), "RPC Port"); - sg.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.udp(8732), "RPC Port"); - - this.securityGroup = sg; - - // cdk-nag suppressions - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-EC23", - reason: "Need to use wildcard for P2P ports" - } - ], - true - ); - } -} diff --git a/lib/tezos/lib/ha-nodes-stack.ts b/lib/tezos/lib/ha-nodes-stack.ts deleted file mode 100644 index 2a585d70..00000000 --- a/lib/tezos/lib/ha-nodes-stack.ts +++ /dev/null @@ -1,152 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as iam from "aws-cdk-lib/aws-iam"; -import * as s3 from "aws-cdk-lib/aws-s3"; -import { AmazonLinuxGeneration, AmazonLinuxImage } from "aws-cdk-lib/aws-ec2"; -import * as s3Assets from "aws-cdk-lib/aws-s3-assets"; -import * as configTypes from "./config/tzConfig.interface"; -import { TzNodeSecurityGroupConstructs } from "./constructs/tz-node-security-group"; -import * as fs from "fs"; -import * as path from "path"; -import * as constants from "../../constructs/constants"; -import { HANodesConstruct } from "../../constructs/ha-rpc-nodes-with-alb"; -import * as nag from "cdk-nag"; - -export interface TzHANodesStackProps extends cdk.StackProps { - nodeRole: configTypes.TzNodeRole; - instanceType: ec2.InstanceType; - instanceCpuType: ec2.AmazonLinuxCpuType; - tzNetwork: configTypes.TzNetwork; - historyMode: configTypes.TzNodeHistoryMode; - downloadSnapshot: boolean; - octezDownloadUri: string; - snapshotsUrl: string; - dataVolume: configTypes.TzDataVolumeConfig; - albHealthCheckGracePeriodMin: number; - heartBeatDelayMin: number; - numberOfNodes: number; -} - -export class TzHANodesStack extends cdk.Stack { - constructor(scope: cdkConstructs.Construct, id: string, props: TzHANodesStackProps) { - super(scope, id, props); - - const REGION = cdk.Stack.of(this).region; - const STACK_NAME = cdk.Stack.of(this).stackName; - const AWS_ACCOUNT_ID = cdk.Stack.of(this).account - const lifecycleHookName = STACK_NAME; - const autoScalingGroupName = STACK_NAME; - - const { - instanceType, - nodeRole, - instanceCpuType, - tzNetwork, - historyMode, - downloadSnapshot, - snapshotsUrl, - dataVolume, - numberOfNodes, - albHealthCheckGracePeriodMin, - heartBeatDelayMin, - } = props; - - // using default vpc - const vpc = ec2.Vpc.fromLookup(this, "vpc", { isDefault: true }); - - // setting up the security group for the node from TZ-specific construct - const instanceSG = new TzNodeSecurityGroupConstructs(this, "security-group", { vpc: vpc }); - - // getting the IAM Role ARM from the common stack - const importedInstanceRoleArn = cdk.Fn.importValue("TzNodeInstanceRoleArn"); - - const instanceRole = iam.Role.fromRoleArn(this, "iam-role", importedInstanceRoleArn); - - // making our scripts and configs from the local "assets" directory available for instance to download - const asset = new s3Assets.Asset(this, "assets", { - path: path.join(__dirname, "assets") - }); - - const snapshotBucket = s3.Bucket.fromBucketName(this, "snapshots-s3-bucket", cdk.Fn.importValue('TezosSnapshotBucket')) - - asset.bucket.grantRead(instanceRole); - snapshotBucket.grantRead(instanceRole); - - // parsing user data script and injecting necessary variables - const nodeScript = fs.readFileSync(path.join(__dirname, "assets", "user-data", "node.sh")).toString(); - - const modifiedInitNodeScript = cdk.Fn.sub(nodeScript, { - _AWS_REGION_: REGION, - _STACK_NAME_: STACK_NAME, - _TZ_SNAPSHOTS_URI_: snapshotsUrl, - _STACK_ID_: constants.NoneValue, - _NODE_CF_LOGICAL_ID_: constants.NoneValue, - _TZ_HISTORY_MODE_: historyMode, - _TZ_DOWNLOAD_SNAPSHOT_ : String(downloadSnapshot), - _TZ_OCTEZ_DOWNLOAD_URI_ : props.octezDownloadUri, - _TZ_NETWORK_: tzNetwork, - _LIFECYCLE_HOOK_NAME_: lifecycleHookName, - _AUTOSCALING_GROUP_NAME_: autoScalingGroupName, - _ASSETS_S3_PATH_: `s3://${asset.s3BucketName}/${asset.s3ObjectKey}`, - _S3_SYNC_BUCKET_: cdk.Fn.importValue('TezosSnapshotBucket'), - _INSTANCE_TYPE_: "HA" - }); - - const rpcNodes = new HANodesConstruct(this, "rpc-nodes", { - instanceType, - dataVolumes: [dataVolume], - machineImage: new ec2.AmazonLinux2023ImageSsmParameter({ - kernel: ec2.AmazonLinux2023Kernel.KERNEL_6_1, - cpuType: instanceCpuType, - }), - role: instanceRole, - vpc, - securityGroup: instanceSG.securityGroup, - userData: modifiedInitNodeScript, - numberOfNodes, - rpcPortForALB: 8732, - albHealthCheckGracePeriodMin, - heartBeatDelayMin, - lifecycleHookName: lifecycleHookName, - autoScalingGroupName: autoScalingGroupName, - healthCheckPath: "/monitor/bootstrapped" - }); - - - - new cdk.CfnOutput(this, "alb-url", { value: rpcNodes.loadBalancerDnsName }); - - // Adding suppressions to the stack - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-AS3", - reason: "No notifications needed" - }, - { - id: "AwsSolutions-S1", - reason: "No access log needed for ALB logs bucket" - }, - { - id: "AwsSolutions-EC28", - reason: "Using basic monitoring to save costs" - }, - { - id: "AwsSolutions-IAM5", - reason: "Need read access to the S3 bucket with assets" - }, - { - id: "AwsSolutions-IAM4", - reason: "AmazonSSMManagedInstanceCore and CloudWatchAgentServerPolicy are restrictive enough" - }, - { - id: "AwsSolutions-EC29", - reason: "We do not need to have termination protection for sync nodes" - } - ], - true - ); - } -} diff --git a/lib/tezos/lib/single-node-stack.ts b/lib/tezos/lib/single-node-stack.ts deleted file mode 100644 index 0df0ac3c..00000000 --- a/lib/tezos/lib/single-node-stack.ts +++ /dev/null @@ -1,141 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as iam from "aws-cdk-lib/aws-iam"; -import * as s3Assets from "aws-cdk-lib/aws-s3-assets"; -import * as path from "path"; -import * as fs from "fs"; -import * as nodeCwDashboard from "./assets/node-cw-dashboard" -import * as cw from 'aws-cdk-lib/aws-cloudwatch'; -import * as constants from "../../constructs/constants"; -import { SingleNodeConstruct } from "../../constructs/single-node" -import * as configTypes from "./config/tzConfig.interface"; -import { TzNodeSecurityGroupConstructs } from "./constructs/tz-node-security-group" -import * as nag from "cdk-nag"; - -export interface TzSingleNodeStackProps extends cdk.StackProps { - nodeRole: configTypes.TzNodeRole; - instanceType: ec2.InstanceType; - instanceCpuType: ec2.AmazonLinuxCpuType; - tzNetwork: configTypes.TzNetwork; - historyMode: configTypes.TzNodeHistoryMode; - downloadSnapshot: boolean; - snapshotsUrl: string; - octezDownloadUri: string; - dataVolume: configTypes.TzDataVolumeConfig; -} - -export class TzSingleNodeStack extends cdk.Stack { - constructor(scope: cdkConstructs.Construct, id: string, props: TzSingleNodeStackProps) { - super(scope, id, props); - - // Setting up necessary environment variables - const REGION = cdk.Stack.of(this).region; - const STACK_NAME = cdk.Stack.of(this).stackName; - const STACK_ID = cdk.Stack.of(this).stackId; - const availabilityZones = cdk.Stack.of(this).availabilityZones; - const chosenAvailabilityZone = availabilityZones.slice(0, 1)[0]; - - // Getting our config from initialization properties - const { - instanceType, - nodeRole, - instanceCpuType, - tzNetwork, - historyMode, - downloadSnapshot, - snapshotsUrl, - octezDownloadUri, - dataVolume, - } = props; - - // Using default VPC - const vpc = ec2.Vpc.fromLookup(this, "vpc", { isDefault: true }); - - // Setting up the security group for the node from Ethereum-specific construct - const instanceSG = new TzNodeSecurityGroupConstructs (this, "security-group", { - vpc: vpc, - }) - - // Making our scripts and configis from the local "assets" directory available for instance to download - const asset = new s3Assets.Asset(this, "assets", { - path: path.join(__dirname, "assets"), - }); - - // Getting the snapshot bucket name and IAM role ARN from the common stack - const importedInstanceRoleArn = cdk.Fn.importValue("TzNodeInstanceRoleArn"); - - const instanceRole = iam.Role.fromRoleArn(this, "iam-role", importedInstanceRoleArn); - - // Making sure our instance will be able to read the assets - asset.bucket.grantRead(instanceRole); - - // Setting up the node using generic Single Node constract - const node = new SingleNodeConstruct(this, "single-node", { - instanceName: STACK_NAME, - instanceType, - dataVolumes: [dataVolume], - machineImage: new ec2.AmazonLinux2023ImageSsmParameter({ - kernel: ec2.AmazonLinux2023Kernel.KERNEL_6_1, - cpuType: instanceCpuType, - }), - vpc, - availabilityZone: chosenAvailabilityZone, - role: instanceRole, - securityGroup: instanceSG.securityGroup, - vpcSubnets: { - subnetType: ec2.SubnetType.PUBLIC, - }, - }); - - // Parsing user data script and injecting necessary variables - const userData = fs.readFileSync(path.join(__dirname, "assets", "user-data", "node.sh")).toString(); - const modifiedUserData = cdk.Fn.sub(userData, { - _AWS_REGION_: REGION, - _ASSETS_S3_PATH_: `s3://${asset.s3BucketName}/${asset.s3ObjectKey}`, - _STACK_NAME_: STACK_NAME, - _TZ_SNAPSHOTS_URI_: snapshotsUrl, - _STACK_ID_: STACK_ID, - _NODE_CF_LOGICAL_ID_: node.nodeCFLogicalId, - _TZ_HISTORY_MODE_: historyMode, - _TZ_DOWNLOAD_SNAPSHOT_ : String(downloadSnapshot), - _TZ_OCTEZ_DOWNLOAD_URI_ : octezDownloadUri, - _TZ_NETWORK_: tzNetwork, - _LIFECYCLE_HOOK_NAME_: constants.NoneValue, - _AUTOSCALING_GROUP_NAME_: constants.NoneValue, - _INSTANCE_TYPE_: "SINGLE", - _S3_SYNC_BUCKET_: constants.NoneValue - }); - - // Adding modified userdata script to the instance prepared fro us by Single Node constract - node.instance.addUserData(modifiedUserData); - - // Adding CloudWatch dashboard to the node - const dashboardString = cdk.Fn.sub(JSON.stringify(nodeCwDashboard.SyncNodeCWDashboardJSON), { - INSTANCE_ID:node.instanceId, - INSTANCE_NAME: STACK_NAME, - REGION: REGION, - }) - - new cw.CfnDashboard(this, 'single-cw-dashboard', { - dashboardName: `${STACK_NAME}-${node.instanceId}`, - dashboardBody: dashboardString, - }); - - new cdk.CfnOutput(this, "single-instance-id", { - value: node.instanceId, - }); - - // Adding suppressions to the stack - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-IAM5", - reason: "Need read and write access to the S3 bucket", - }, - ], - true - ); - } -} diff --git a/lib/tezos/lib/snapshot-node-stack.ts b/lib/tezos/lib/snapshot-node-stack.ts deleted file mode 100644 index 9ad0b649..00000000 --- a/lib/tezos/lib/snapshot-node-stack.ts +++ /dev/null @@ -1,215 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as iam from "aws-cdk-lib/aws-iam"; -import * as s3 from "aws-cdk-lib/aws-s3"; -import { AmazonLinuxGeneration, AmazonLinuxImage } from "aws-cdk-lib/aws-ec2"; -import * as s3Assets from "aws-cdk-lib/aws-s3-assets"; -import * as configTypes from "./config/tzConfig.interface"; -import { TzNodeSecurityGroupConstructs } from "./constructs/tz-node-security-group"; -import * as fs from "fs"; -import * as path from "path"; -import * as constants from "../../constructs/constants"; -import { HANodesConstruct } from "../../constructs/ha-rpc-nodes-with-alb"; -import * as nag from "cdk-nag"; -import { SnapshotsS3BucketConstruct } from "../../constructs/snapshots-bucket"; - -export interface TzSnapshotNodeStackProps extends cdk.StackProps { - nodeRole: configTypes.TzNodeRole; - instanceType: ec2.InstanceType; - instanceCpuType: ec2.AmazonLinuxCpuType; - tzNetwork: configTypes.TzNetwork; - historyMode: configTypes.TzNodeHistoryMode; - downloadSnapshot: boolean; - snapshotsUrl: string; - octezDownloadUri: string; -} - -export class TzSnapshotNodeStack extends cdk.Stack { - constructor(scope: cdkConstructs.Construct, id: string, props: TzSnapshotNodeStackProps) { - super(scope, id, props); - - const REGION = cdk.Stack.of(this).region; - const STACK_NAME = cdk.Stack.of(this).stackName; - const STACK_ID = cdk.Stack.of(this).stackId; - const AWS_ACCOUNT_ID = cdk.Stack.of(this).account - const lifecycleHookName = STACK_NAME; - const autoScalingGroupName = STACK_NAME; - - const { - instanceType, - nodeRole, - instanceCpuType, - tzNetwork, - historyMode, - downloadSnapshot, - snapshotsUrl, - octezDownloadUri, - } = props; - - // using default vpc - const vpc = ec2.Vpc.fromLookup(this, "vpc", { isDefault: true }); - - // setting up the security group for the node from TZ-specific construct - const instanceSG = new TzNodeSecurityGroupConstructs(this, "security-group", { vpc: vpc }); - - // getting the IAM Role ARM from the common stack - const importedInstanceRoleArn = cdk.Fn.importValue("TzNodeInstanceRoleArn"); - - const instanceRole = iam.Role.fromRoleArn(this, "iam-role", importedInstanceRoleArn); - - // making our scripts and configs from the local "assets" directory available for instance to download - const asset = new s3Assets.Asset(this, "assets", { - path: path.join(__dirname, "assets") - }); - - asset.bucket.grantRead(instanceRole); - - - const snapshotsBucket = new SnapshotsS3BucketConstruct(this, "snapshots-s3-bucket", { - bucketName: `${STACK_NAME}-${AWS_ACCOUNT_ID}-${REGION}`, - }); - - const s3VPCEndpoint = vpc.addGatewayEndpoint("s3-vpc-endpoint", { - service: ec2.GatewayVpcEndpointAwsService.S3, - }); - - const snapshotInstanceRole = new iam.Role(this, `snapshot-instance-role`, { - assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com"), - managedPolicies: [ - iam.ManagedPolicy.fromAwsManagedPolicyName("CloudWatchAgentServerPolicy"), - ], - }); - - asset.bucket.grantRead(snapshotInstanceRole); - snapshotInstanceRole.addToPolicy( - new iam.PolicyStatement({ - resources: [ - snapshotsBucket.bucketArn, - snapshotsBucket.arnForObjects("*"), - "arn:aws:s3:::lambsonacid-octez-*" - ], - actions: ["s3:ListBucket", "s3:*Object"], - }) - ); - - - // Limiting access through this VPC endpoint only to our sync bucket and Amazon linux repo bucket - s3VPCEndpoint.addToPolicy( - new iam.PolicyStatement({ - principals: [new iam.AnyPrincipal()], - resources: [ - snapshotsBucket.bucketArn, - snapshotsBucket.arnForObjects("*"), - `arn:aws:s3:::al2023-repos-${REGION}*`, - `arn:aws:s3:::al2023-repos-${REGION}/*`, - `arn:aws:s3:::${asset.s3BucketName}`, - `arn:aws:s3:::${asset.s3BucketName}/*`, - "arn:aws:s3:::amazoncloudwatch-agent", - "arn:aws:s3:::amazoncloudwatch-agent/*", - "arn:aws:s3:::lambsonacid-octez-*" - ], - actions: ["s3:ListBucket", "s3:*Object", "s3:GetBucket*"], - }) - ); - const snapshotNodeScript = fs.readFileSync(path.join(__dirname, "assets", "user-data", "node.sh")).toString(); - - - const snapshotNode = new ec2.Instance(this, "tz-snapshot-node", { - instanceName: "tz-snapshot-node", - instanceType: instanceType, - machineImage: new ec2.AmazonLinux2023ImageSsmParameter({ - kernel: ec2.AmazonLinux2023Kernel.KERNEL_6_1, - cpuType: instanceCpuType, - }), - vpc: vpc, - blockDevices: [ - { - // ROOT VOLUME - deviceName: "/dev/xvda", - volume: ec2.BlockDeviceVolume.ebs(46, { - deleteOnTermination: true, - encrypted: true, - iops: 3000, - volumeType: ec2.EbsDeviceVolumeType.GP3, - }), - }, - ], - detailedMonitoring: true, - propagateTagsToVolumeOnCreation: true, - role: snapshotInstanceRole, - securityGroup: instanceSG.securityGroup, - }); - - const modifiedSnapshotNodeScript = cdk.Fn.sub(snapshotNodeScript, { - _AWS_REGION_: REGION, - _STACK_NAME_: STACK_NAME, - _TZ_SNAPSHOTS_URI_: snapshotsUrl, - _STACK_ID_: STACK_ID, - _TZ_HISTORY_MODE_: historyMode, - _TZ_DOWNLOAD_SNAPSHOT_ : String(downloadSnapshot), - _TZ_OCTEZ_DOWNLOAD_URI_ : octezDownloadUri, - _TZ_NETWORK_: tzNetwork, - _S3_SYNC_BUCKET_: snapshotsBucket.bucketName, - _NODE_CF_LOGICAL_ID_: snapshotNode.instance.logicalId, - _LIFECYCLE_HOOK_NAME_: lifecycleHookName, - _AUTOSCALING_GROUP_NAME_: autoScalingGroupName, - _ASSETS_S3_PATH_: `s3://${asset.s3BucketName}/${asset.s3ObjectKey}`, - _INSTANCE_TYPE_: "SNAPSHOT", - }); - - snapshotNode.addUserData(modifiedSnapshotNodeScript); - - - // Getting logical ID of the instance to send ready signal later once the instance is initialized - const snapshotNodeCfn = snapshotNode.node.defaultChild as ec2.CfnInstance; - - // CloudFormation Config: wait for 15 min for the node to start - const creationPolicy: cdk.CfnCreationPolicy = { - resourceSignal: { - count: 1, - timeout: "PT90M", - }, - }; - - - snapshotNodeCfn.cfnOptions.creationPolicy = creationPolicy; - - new cdk.CfnOutput(this, "TezosSnapshotBucket", { - value: snapshotsBucket.bucketName, - exportName: "TezosSnapshotBucket", - }); - - // Adding suppressions to the stack - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-AS3", - reason: "No notifications needed" - }, - { - id: "AwsSolutions-S1", - reason: "No access log needed for ALB logs bucket" - }, - { - id: "AwsSolutions-EC28", - reason: "Using basic monitoring to save costs" - }, - { - id: "AwsSolutions-IAM5", - reason: "Need read access to the S3 bucket with assets" - }, - { - id: "AwsSolutions-IAM4", - reason: "AmazonSSMManagedInstanceCore and CloudWatchAgentServerPolicy are restrictive enough" - }, - { - id: "AwsSolutions-EC29", - reason: "We do not need to have termination protection for snapshot nodes" - } - ], - true - ); - } -} diff --git a/lib/tezos/package.json b/lib/tezos/package.json deleted file mode 100644 index db927af4..00000000 --- a/lib/tezos/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "aws-blockchain-node-runners-tz", - "version": "0.1.0", - "bin": { - "scroll": "bin/tz.js" - }, - "scripts": { - "build": "tsc", - "watch": "tsc -w", - "test": "jest", - "cdk": "cdk", - "cdk_deploy_common": "cdk deploy tz-common", - "cdk_deploy_ha_nodes": "cdk deploy tz-ha-nodes", - "cdk_destroy_common": "cdk destroy tz-common", - "cdk_destroy_ha_nodes": "cdk destroy tz-ha-nodes" - } -} diff --git a/lib/tezos/sample-configs/.env-sample-full b/lib/tezos/sample-configs/.env-sample-full deleted file mode 100644 index 946aed9a..00000000 --- a/lib/tezos/sample-configs/.env-sample-full +++ /dev/null @@ -1,38 +0,0 @@ -############################################################# -# Example configuration for TZ nodes runner app on AWS # -############################################################# - -## Set the AWS account is and region for your environment ## -AWS_ACCOUNT_ID="..." -AWS_REGION="..." - -## Common configuration parameters ## -TZ_NETWORK="mainnet" # All options: "mainnet", "sandbox", "ghostnet" -TZ_HISTORY_MODE="rolling" # All options: "full", "rolling", "archive" -TZ_SNAPSHOT_REGION="us" # All options "eu", "asia" or "us" -TZ_DOWNLOAD_SNAPSHOT="true" # All options: "true", "false" - -# TZ snapshots download link -# ONLY define if it does not follow the pattern below, which is the default -# https://snapshots..tzinit.org// -# TZ_SNAPSHOT_URI="..." - -# Binary download links (here using v0.20) -TZ_ARM64_OCTEZ_URI="https://gitlab.com/tezos/tezos/-/package_files/133748628/download" -TZ_X86_OCTEZ_URI="https://gitlab.com/tezos/tezos/-/package_files/133747462/download" - -## Instance Nodes -# IMPORTANT: Make sure the CPU type (architecture) and the instance type match -TZ_INSTANCE_TYPE="c7g.xlarge" -TZ_CPU_TYPE="arm64" # All options: "x86_64", "arm64" - -# Data volume configuration -TZ_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families -TZ_DATA_VOL_SIZE="900" # Current required data size to keep both snapshot archive and unarchived version of it -TZ_DATA_VOL_IOPS="10000" # Max IOPS for EBS volumes (not applicable for "instance-store") -TZ_DATA_VOL_THROUGHPUT="700" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") - -## HA nodes configuration ## -TZ_HA_NUMBER_OF_NODES="2" # Total number of RPC nodes to be provisioned. Default: 2 -TZ_HA_ALB_HEALTHCHECK_GRACE_PERIOD_MIN="50" # Time enough to initialize the instance -TZ_HA_NODES_HEARTBEAT_DELAY_MIN="50" # Time sufficient enough for a node do sync diff --git a/lib/tezos/test/.env-test b/lib/tezos/test/.env-test deleted file mode 100644 index d8ee5c89..00000000 --- a/lib/tezos/test/.env-test +++ /dev/null @@ -1,30 +0,0 @@ -############################################################# -# Example configuration for TZ nodes runner app on AWS # -############################################################# - -## Set the AWS account is and region for your environment ## -AWS_ACCOUNT_ID="xxxxxxxxxxx" -AWS_REGION="us-east-2" - -## TZ snapshots download link # IMPORTANT !!! Make sure the url is valid before you use it! -TZ_SNAPSHOT_URI="https://snapshots.eu.tzinit.org/mainnet/rolling" - -## Common configuration parameters ## -TZ_NETWORK="mainnet" # All options: "mainnet", "sandbox", "ghostnet" -TZ_HISTORY_MODE="rolling" # All options: "full", "rolling", "archive" -TZ_DOWNLOAD_SNAPSHOT="true" # All options: "true", "false" - -## Instance Nodes -TZ_INSTANCE_TYPE="m6gd.xlarge" -TZ_CPU_TYPE="arm64" # All options: "x86_64", "arm64". IMPORTANT: Make sure the CPU type matches the instance type used - -# Data volume configuration -TZ_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: Use "instance-store" option only with instance types that support that feature, like popular for node im4gn, d3, i3en, and i4i instance families -TZ_DATA_VOL_SIZE="1000" # Current required data size to keep both snapshot archive and unarchived version of it -TZ_DATA_VOL_IOPS="10000" # Max IOPS for EBS volumes (not applicable for "instance-store") -TZ_DATA_VOL_THROUGHPUT="700" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") - -## HA nodes configuration ## -TZ_HA_NUMBER_OF_NODES="2" # Total number of RPC nodes to be provisioned. Default: 2 -TZ_HA_ALB_HEALTHCHECK_GRACE_PERIOD_MIN="50" # Time enough to initialize the instance -TZ_HA_NODES_HEARTBEAT_DELAY_MIN="50" # Time sufficient enough for a node do sync diff --git a/lib/tezos/test/common-stack.test.ts b/lib/tezos/test/common-stack.test.ts deleted file mode 100644 index c70f80e3..00000000 --- a/lib/tezos/test/common-stack.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Match, Template } from "aws-cdk-lib/assertions"; -import * as cdk from "aws-cdk-lib"; -import * as dotenv from 'dotenv'; -dotenv.config({ path: './test/.env-test' }); -import * as config from "../lib/config/tzConfig"; -import { TzCommonStack } from "../lib/common-stack"; - -describe("TZCommonStack", () => { - test("synthesizes the way we expect", () => { - const app = new cdk.App(); - - // Create the TzCommonStack. - const tzCommonStack = new TzCommonStack(app, "tz-common", { - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - stackName: `tz-nodes-common`, - }); - - // Prepare the stack for assertions. - const template = Template.fromStack(tzCommonStack); - - // Has EC2 instance role. - template.hasResourceProperties("AWS::IAM::Role", { - AssumeRolePolicyDocument: { - Statement: [ - { - Action: "sts:AssumeRole", - Effect: "Allow", - Principal: { - Service: "ec2.amazonaws.com" - } - } - ] - }, - ManagedPolicyArns: [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - Ref: "AWS::Partition" - }, - ":iam::aws:policy/AmazonSSMManagedInstanceCore" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/CloudWatchAgentServerPolicy" - ] - ] - } - ] - }) - - }); -}); diff --git a/lib/tezos/test/ha-nodes-stack.test.ts b/lib/tezos/test/ha-nodes-stack.test.ts deleted file mode 100644 index b4cd6e60..00000000 --- a/lib/tezos/test/ha-nodes-stack.test.ts +++ /dev/null @@ -1,253 +0,0 @@ -import { Match, Template } from "aws-cdk-lib/assertions"; -import * as cdk from "aws-cdk-lib"; -import * as dotenv from 'dotenv'; -dotenv.config({ path: './test/.env-test' }); -import * as config from "../lib/config/tzConfig"; -import * as configTypes from "../lib/config/tzConfig.interface"; -import { TzHANodesStack } from "../lib/ha-nodes-stack"; - -describe("TzHANodesStack", () => { - test("synthesizes the way we expect", () => { - const app = new cdk.App(); - - // Create the TzHANodesStack. - const tzHANodesStack = new TzHANodesStack(app, "tz-snapshot-node", { - stackName: `tz-ha-nodes-${config.baseNodeConfig.historyMode}`, - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - nodeRole: "rpc-node", - - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - tzNetwork: config.baseNodeConfig.tzNetwork, - historyMode: config.baseNodeConfig.historyMode, - snapshotsUrl:config.baseNodeConfig.snapshotsUrl, - dataVolume: config.baseNodeConfig.dataVolume, - downloadSnapshot: Boolean(config.baseNodeConfig.downloadSnapshot), - - albHealthCheckGracePeriodMin: config.haNodeConfig.albHealthCheckGracePeriodMin, - heartBeatDelayMin: config.haNodeConfig.heartBeatDelayMin, - numberOfNodes: config.haNodeConfig.numberOfNodes, - }); - - // Prepare the stack for assertions. - const template = Template.fromStack(tzHANodesStack); - - // Has EC2 instance security group. - template.hasResourceProperties("AWS::EC2::SecurityGroup", { - GroupDescription: Match.anyValue(), - VpcId: Match.anyValue(), - SecurityGroupEgress: [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" - } - ], - SecurityGroupIngress: [ - { - "CidrIp": "1.2.3.4/5", - "Description": "Blockchain Node RPC", - "FromPort": 8732, - "IpProtocol": "tcp", - "ToPort": 8732 - } - ] - }) - - // Has security group from ALB to EC2. - template.hasResourceProperties("AWS::EC2::SecurityGroupIngress", { - Description: "Load balancer to target", - FromPort: 8732, - GroupId: Match.anyValue(), - IpProtocol: "tcp", - SourceSecurityGroupId: Match.anyValue(), - ToPort: 8732, - }) - - // Has launch template profile for EC2 instances. - template.hasResourceProperties("AWS::IAM::InstanceProfile", { - Roles: [Match.anyValue()] - }); - - // Has EC2 launch template. - template.hasResourceProperties("AWS::EC2::LaunchTemplate", { - LaunchTemplateData: { - BlockDeviceMappings: [ - { - "DeviceName": "/dev/xvda", - "Ebs": { - "DeleteOnTermination": true, - "Encrypted": true, - "Iops": 3000, - "Throughput": 125, - "VolumeSize": 46, - "VolumeType": "gp3" - } - }, - { - "DeviceName": "/dev/sdf", - "Ebs": { - "DeleteOnTermination": true, - "Encrypted": true, - "Iops": 10000, - "Throughput": 700, - "VolumeSize": 1000, - "VolumeType": "gp3" - } - } - ], - EbsOptimized: true, - IamInstanceProfile: Match.anyValue(), - ImageId: Match.anyValue(), - InstanceType: "m6gd.xlarge", - SecurityGroupIds: [Match.anyValue()], - UserData: Match.anyValue(), - TagSpecifications: Match.anyValue(), - } - }) - - // Has Auto Scaling Group. - template.hasResourceProperties("AWS::AutoScaling::AutoScalingGroup", { - AutoScalingGroupName: `tz-ha-nodes-${config.baseNodeConfig.historyMode}`, - HealthCheckGracePeriod: config.haNodeConfig.albHealthCheckGracePeriodMin * 60, - HealthCheckType: "ELB", - DefaultInstanceWarmup: 60, - MinSize: "0", - MaxSize: "4", - DesiredCapacity: config.haNodeConfig.numberOfNodes.toString(), - VPCZoneIdentifier: Match.anyValue(), - TargetGroupARNs: Match.anyValue(), - }); - - // Has Auto Scaling Lifecycle Hook. - template.hasResourceProperties("AWS::AutoScaling::LifecycleHook", { - DefaultResult: "ABANDON", - HeartbeatTimeout: config.haNodeConfig.heartBeatDelayMin * 60, - LifecycleHookName: `tz-ha-nodes-${config.baseNodeConfig.historyMode}`, - LifecycleTransition: "autoscaling:EC2_INSTANCE_LAUNCHING", - }); - - // Has Auto Scaling Security Group. - template.hasResourceProperties("AWS::EC2::SecurityGroup", { - GroupDescription: Match.anyValue(), - SecurityGroupEgress: [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" - } - ], - SecurityGroupIngress: [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Peer connection port", - "FromPort": 9732, - "IpProtocol": "tcp", - "ToPort": 9732 - }, - { - "CidrIp": "0.0.0.0/0", - "Description": "Peer connection port", - "FromPort": 9732, - "IpProtocol": "udp", - "ToPort": 9732 - }, - { - "CidrIp": "1.2.3.4/5", - "Description": "RPC Port", - "FromPort": 8732, - "IpProtocol": "tcp", - "ToPort": 8732 - }, - { - "CidrIp": "1.2.3.4/5", - "Description": "RPC Port", - "FromPort": 8732, - "IpProtocol": "udp", - "ToPort": 8732 - }, - { - "Description": "Allow access from ALB to Blockchain Node", - "FromPort": 0, - "IpProtocol": "tcp", - "SourceSecurityGroupId": Match.anyValue(), - "ToPort": 65535, - }, - ], - VpcId: Match.anyValue(), - }); - - - - // Has ALB. - template.hasResourceProperties("AWS::ElasticLoadBalancingV2::LoadBalancer", { - LoadBalancerAttributes: [ - { - Key: "deletion_protection.enabled", - Value: "false" - }, - { - Key: "access_logs.s3.enabled", - Value: "true" - }, - { - Key: "access_logs.s3.bucket", - Value: Match.anyValue(), - }, - { - Key: "access_logs.s3.prefix", - Value: `tz-ha-nodes-${config.baseNodeConfig.historyMode}` - } - ], - Scheme: "internal", - SecurityGroups: [ - Match.anyValue() - ], - "Subnets": [ - Match.anyValue(), - Match.anyValue() - ], - Type: "application", - }); - - // Has ALB listener. - template.hasResourceProperties("AWS::ElasticLoadBalancingV2::Listener", { - "DefaultActions": [ - { - "TargetGroupArn": Match.anyValue(), - Type: "forward" - } - ], - LoadBalancerArn: Match.anyValue(), - Port: 8732, - Protocol: "HTTP" - }) - - // Has ALB target group. - template.hasResourceProperties("AWS::ElasticLoadBalancingV2::TargetGroup", { - HealthCheckEnabled: true, - HealthCheckIntervalSeconds: 30, - HealthCheckPath: "/monitor/bootstrapped", - HealthCheckPort: "8732", - HealthyThresholdCount: 3, - Matcher: { - HttpCode: "200-299" - }, - Port: 8732, - Protocol: "HTTP", - TargetGroupAttributes: [ - { - Key: "deregistration_delay.timeout_seconds", - Value: "30" - }, - { - Key: "stickiness.enabled", - Value: "false" - } - ], - TargetType: "instance", - UnhealthyThresholdCount: 2, - VpcId: Match.anyValue(), - }) - }); -}); diff --git a/lib/tezos/test/single-node-stack.test.ts b/lib/tezos/test/single-node-stack.test.ts deleted file mode 100644 index 8a19e403..00000000 --- a/lib/tezos/test/single-node-stack.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { Match, Template } from "aws-cdk-lib/assertions"; -import * as cdk from "aws-cdk-lib"; -import * as dotenv from 'dotenv'; -dotenv.config({ path: './test/.env-test' }); -import * as config from "../lib/config/tzConfig"; -import * as configTypes from "../lib/config/tzConfig.interface"; -import { TzSingleNodeStack } from "../lib/single-node-stack"; - -describe("TZSingleNodeStack", () => { - test("synthesizes the way we expect", () => { - const app = new cdk.App(); - - // Create the EthSingleNodeStack. - const tzSingleNodeStack = new TzSingleNodeStack(app, "tz-single-node", { - stackName: `tz-single-node`, - - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - nodeRole: "single-node", - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - tzNetwork: config.baseNodeConfig.tzNetwork, - historyMode: config.baseNodeConfig.historyMode, - snapshotsUrl:config.baseNodeConfig.snapshotsUrl, - dataVolume: config.baseNodeConfig.dataVolume, - downloadSnapshot: true - }); - - // Prepare the stack for assertions. - const template = Template.fromStack(tzSingleNodeStack); - - // Has EC2 instance security group. - template.hasResourceProperties("AWS::EC2::SecurityGroup", { - GroupDescription: Match.anyValue(), - VpcId: Match.anyValue(), - SecurityGroupEgress: [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" - } - ], - SecurityGroupIngress: [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Peer connection port", - "FromPort": 9732, - "IpProtocol": "tcp", - "ToPort": 9732 - }, - { - "CidrIp": "0.0.0.0/0", - "Description": "Peer connection port", - "FromPort": 9732, - "IpProtocol": "udp", - "ToPort": 9732 - }, - { - "CidrIp": "1.2.3.4/5", - "Description": "RPC Port", - "FromPort": 8732, - "IpProtocol": "tcp", - "ToPort": 8732 - }, - { - "CidrIp": "1.2.3.4/5", - "Description": "RPC Port", - "FromPort": 8732, - "IpProtocol": "udp", - "ToPort": 8732 - } - ] - }) - - // Has EC2 instance with node configuration - template.hasResourceProperties("AWS::EC2::Instance", { - AvailabilityZone: Match.anyValue(), - UserData: Match.anyValue(), - BlockDeviceMappings: [ - { - DeviceName: "/dev/xvda", - Ebs: { - DeleteOnTermination: true, - Encrypted: true, - Iops: 3000, - VolumeSize: 46, - VolumeType: "gp3" - } - } - ], - IamInstanceProfile: Match.anyValue(), - ImageId: Match.anyValue(), - InstanceType: "m6gd.xlarge", - Monitoring: true, - PropagateTagsToVolumeOnCreation: true, - SecurityGroupIds: Match.anyValue(), - SubnetId: Match.anyValue(), - }) - - // Has EBS data volume. - template.hasResourceProperties("AWS::EC2::Volume", { - AvailabilityZone: Match.anyValue(), - Encrypted: true, - Iops: 10000, - MultiAttachEnabled: false, - Size: 1000, - Throughput: 700, - VolumeType: "gp3" - }) - - // Has EBS data volume attachment. - template.hasResourceProperties("AWS::EC2::VolumeAttachment", { - Device: "/dev/sdf", - InstanceId: Match.anyValue(), - VolumeId: Match.anyValue(), - }) - - // Has CloudWatch dashboard. - template.hasResourceProperties("AWS::CloudWatch::Dashboard", { - DashboardBody: Match.anyValue(), - DashboardName: { - "Fn::Join": ["", ["tz-single-node-",{ "Ref": Match.anyValue() }]] - } - }) - - }); -}); diff --git a/lib/tezos/tsconfig.json b/lib/tezos/tsconfig.json deleted file mode 100644 index ae2453f2..00000000 --- a/lib/tezos/tsconfig.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": [ - "es2020", - "dom" - ], - "types": ["node", "jest"], - "declaration": true, - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": false, - "noUnusedParameters": false, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": false, - "inlineSourceMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictPropertyInitialization": false, - "typeRoots": [ - "../../node_modules/@types" - ] - }, - "exclude": [ - "node_modules", - "cdk.out" - ] -} diff --git a/lib/theta/.gitignore b/lib/theta/.gitignore deleted file mode 100644 index a6e176a9..00000000 --- a/lib/theta/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -*.js -!jest.config.js -*.d.ts -node_modules -*.lock - -# CDK asset staging directory -.cdk.staging -cdk.out diff --git a/lib/theta/README.md b/lib/theta/README.md deleted file mode 100644 index b448d38e..00000000 --- a/lib/theta/README.md +++ /dev/null @@ -1,249 +0,0 @@ -# Sample AWS Blockchain Node Runner app for Theta Edge Nodes - -| Contributed by | -|:--------------------:| -| [@jonathan-eid](https://github.com/jonathan-eid), [@onching](https://github.com/onching) | - -The Edge Node turns your computer into an edge computing node in the Theta EdgeCloud. The Edge Node can execute various types of jobs including AI/deep learning model training and inference, as well as video transcoding and relaying. By running the edge node, you can contribute unused computational and bandwidth resources and earn token rewards. - -This blueprint is designed to assist in deploying a single node [Theta Edge Network Edge Node](https://docs.thetatoken.org/docs/setup-theta-edge-node) on AWS. It is intended for research and develoment use purposes. - -## Overview of Deployment Architectures - -### Single Node setup -![Single Nodes Deployment](./doc/assets/Architecture-Theta-Edge-Single-Node.drawio.png) - -1. A single RPC Theta Edge Node is deployed within in the [Default VPC](https://docs.aws.amazon.com/vpc/latest/userguide/default-vpc.html) and continuously synchronizes with the rest of nodes on Theta Blockchain Network through [Internet Gateway](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html). The Theta node is accessed by the user internally. JSON RPC API is not exposed to the Internet to protect the node from unauthorized access. User can get console acess to EC2 Instance using Session Manager to interact with the RPC API from CLI. -2. The AWS Cloud Development Kit (CDK) is used to deploy a single node. The CDK application stores assets like scripts and config files in S3 bucket to copy them to the EC2 instance when launching an Edge Node. -3. The Theta node pulls password from Secrets Manager during startup only. -4. The Theta node sends various monitoring metrics for both the EC2 Instance and Edge Node to Amazon CloudWatch. - - -## Additional materials - -
- -Well-Architected Checklist - -This is the Well-Architected checklist for Edge nodes implementation of the AWS Blockchain Node Runner app. This checklist takes into account questions from the [AWS Well-Architected Framework](https://aws.amazon.com/architecture/well-architected/) which are relevant to this workload. Please feel free to add more checks from the framework if required for your workload. - -| Pillar | Control | Question/Check | Remarks | -|:------------------------|:----------------------------------|:---------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| | | Traffic inspection | Traffic protection is not used in the solution. [AWS Web Applications Firewall (WAF)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html) could be implemented for traffic over HTTP(S), [AWS Shield](https://docs.aws.amazon.com/waf/latest/developerguide/shield-chapter.html) provides Distributed Denial of Service (DDoS) protection. Additional charges will apply. | -| | Compute protection | Reduce attack surface | This solution uses Amazon Linux2 AMI(`Amazon Linux2 AMI(HVM)-Kernel 5.10`). You may choose to run hardening scripts on it. | -| | | Enable people to perform actions at a distance | This solution uses [AWS Systems Manager for terminal session](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-sessions-start.html#start-sys-console), not ssh ports. | -| | Data protection at rest | Use encrypted Amazon Elastic Block Store (Amazon EBS) volumes | This solution uses encrypted Amazon EBS volumes. | -| | Authorization and access control | Use instance profile with Amazon Elastic Compute Cloud (Amazon EC2) instances | This solution uses AWS Identity and Access Management (AWS IAM) role instead of IAM user. | -| | | Following principle of least privilege access | In all node types, root user is not used (using special user "bcuser" instead). | -| | Application security | Security focused development practices | cdk-nag is being used with appropriate suppressions. | -| Cost optimization | Service selection | Use cost effective resources | Although we can run the Edge Node without a GPU, we choose to use a `g4dn.xlarge` w/ a T4 GPU & 256gb GP3 EBS Storage to take advantage of the jobs the Theta Edge Network has to offer| -| | Cost awareness | Estimate costs | Single RPC node with `g4dn.xlarge` EBS gp3 volumes about 256 GB(10000 IOPS, 125 MBps/s throughput) with On-Demand pricing will cost around US$439.46 per month in the US East (N. Virginia) region. More cost-optimal option with 3 year EC2 Instance Savings plan the cost goes down to $254.77 USD. To create your own estimate use [AWS Pricing Calculator](https://calculator.aws/#/) | -| Reliability | Resiliency implementation | Withstand component failures | Docker service and Edge Node container is set to always restart in the case of an EC2 outage. There is no restart mechanism on the single node instance as of now. | -| | Data backup | How is data backed up? | Considering blockchain data is replicated by nodes automatically and Theta nodes sync on run, we don't use any additional mechanisms to backup the data. | -| | Resource monitoring | How are workload resources monitored? | Resources are being monitored using Amazon CloudWatch dashboards. Amazon CloudWatch custom metrics are being pushed via CloudWatch Agent. | -| Performance efficiency | Compute selection | How is compute solution selected? | Compute solution is selected based on best price-performance, i.e. AWS lower end GPU Amazon EC2 instances. | -| | Storage selection | How is storage solution selected? | Storage solution is selected based on best price-performance, i.e. gp3 Amazon EBS volumes with optimal IOPS and throughput. | -| | Architecture selection | How is the best performance architecture selected? | We used a combination of recommendations from the Theta community. | -| Operational excellence | Workload health | How is health of workload determined? | Health of the workload is based on EC2 status checks. | -| Sustainability | Hardware & services | Select most efficient hardware for your workload | The solution uses T4-powered instances. This is a lower end, data-center GPU that will be sufficient for getting the average user up and running on the Theta Edge Network. | -
- -### Recommended Infrastructure - - -| Usage pattern | Ideal configuration | Primary option on AWS | Config reference | -|---------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------|-------------------------------------------------------| -| Edge Node | 4 vCPU, 16 GB RAM, Data volume: EBS gp3 256GB, 3K IOPS, 125 MB/s throughput | `g4dn.2xlarge` EBS gp3 volumes about 256 GB(10000 IOPS, 125 MBps/s throughput) | [.env-sample-full](./sample-configs/.env-sample-full) | - -## Setup Instructions - -### Setup Cloud9 - -We will use AWS Cloud9 to execute the subsequent commands. Follow the instructions in [Cloud9 Setup](../../docs/setup-cloud9.md) - -### Clone this repository and install dependencies - -```bash -git clone https://github.com/aws-samples/aws-blockchain-node-runners.git -cd aws-blockchain-node-runners -npm install -``` - -### Deploy Commons - -1. Make sure you are in the root directory of the cloned repository - -2. If you have deleted or don't have the default VPC, create default VPC - - ```bash - aws ec2 create-default-vpc - ``` - - > **NOTE**: - > You may see the following error if the default VPC already exists: `An error occurred (DefaultVpcAlreadyExists) when calling the CreateDefaultVpc operation: A Default VPC already exists for this account in this region.`. That means you can just continue with the following steps. - - -3. Configure the CDK app - - Create your own copy of `.env` file and edit it to update with your AWS Account ID, AWS Region: - - ```bash - # Make sure you are in aws-blockchain-node-runners/lib/theta - cd lib/theta - pwd - cp ./sample-configs/.env-sample-full .env - nano .env - ``` - - -4. Deploy common components such as IAM role - - > **IMPORTANT**: - > All AWS CDK v2 deployments use dedicated AWS resources to hold data during deployment. Therefore, your AWS account and Region must be [bootstrapped](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html) to create these resources before you can deploy. If you haven't already bootstrapped, issue the following command: - > ```bash - > npx cdk bootstrap aws://ACCOUNT-NUMBER/REGION - >``` - - ```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/theta - - # Set a password for your theta edge node wallet client - EDGE_NODE_PASSWORD=letscompute # please change 'letscompute' to something more secure - npx cdk deploy theta-edge-common --parameters edgeNodePassword=$EDGE_NODE_PASSWORD - ``` - - -### Single RPC Edge Node - -1. Deploy the stack - ```bash - pwd - # Make sure you are in aws-blockchain-node-runners/lib/theta - npx cdk deploy theta-edge-single-node --json --outputs-file single-node-deploy.json - ``` - -2. After the node is initialised you need to wait another 10 minutes for the inital edge node startup process to complete. You can use Amazon CloudWatch to track the progress. There is a script that publishes CloudWatch metrics every minute, where you can watch `theta_current_block_height`. When the node is fully launched you should see the `Theta Client Block Height` & `Theta Client Peer Count` dashboards populate. To see them: - - - Navigate to [CloudWatch service](https://console.aws.amazon.com/cloudwatch/) (make sure you are in the region you have specified for `AWS_REGION`) - - Open `Dashboards` and select `edge-single-node---` from the list of dashboards. - -Alternatively, you can manually check. Run the following query from within a Session Manager Session inside the instance: - - ```bash - curl -X POST -H 'Content-Type: application/json' \ - --data '{"jsonrpc":"2.0","method":"edgecore.GetStatus","params":[],"id":1}' http://localhost:17888/rpc - ``` - - You should get a response similar to: - - ```javascript - { - "jsonrpc": "2.0", - "id": 1, - "result": { - "address": "0x095f9CF353a4e2B8CD1f876cd8990d42bEcf6455", - "chain_id": "mainnet", - "peer_id": "0x095f9CF353a4e2B8CD1f876cd8990d42bEcf6455", - "current_height": "25223860", - "current_time": "1715637778" - } - } - ``` - - -3. Once the startup is done, you should be able to access the RPC API of that node from within the same VPC. The RPC port is not exposed to the Internet. - - - -### Clearing up and undeploy everything - -1. Destroy Single Nodes and Common stacks - -```bash -export AWS_ACCOUNT_ID= -export AWS_REGION= - -pwd -# Make sure you are in aws-blockchain-node-runners/lib/theta - -# Destroy Single Node -npx cdk destroy theta-edge-single-node - - - # Delete all common components like IAM role and Security Group -npx cdk destroy theta-edge-common -``` -2. Follow steps to delete the Cloud9 instance in [Cloud9 Setup](../../doc/setup-cloud9.md) - - -### FAQ - -1. How to check the logs of the clients running on my edge node? - -Please enter the [AWS Management Console - EC2 Instances](https://us-east-2.console.aws.amazon.com/ec2/home?region=us-east-2#Instances:instanceState=running), choose the correct region, copy the instance ID you need to query. - - **Note:** In this tutorial we chose not to use SSH and use Session Manager instead. That allows you to log all sessions in AWS CloudTrail to see who logged into the server and when. If you receive an error similar to `SessionManagerPlugin is not found`, [install Session Manager plugin for AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) - -```bash -pwd -# Make sure you are in aws-blockchain-node-runners/lib/theta - -export INSTANCE_ID="i-**************" -echo "INSTANCE_ID=" $INSTANCE_ID -aws ssm start-session --target $INSTANCE_ID --region $AWS_REGION -sudo docker logs $(sudo docker ps -q) - -``` -2. How to check the logs from the EC2 user-data script? - -Please enter the [AWS Management Console - EC2 Instances](https://us-east-2.console.aws.amazon.com/ec2/home?region=us-east-2#Instances:instanceState=running), choose the correct region, copy the instance ID you need to query. - -```bash -pwd -# Make sure you are in aws-blockchain-node-runners/lib/theta - -export INSTANCE_ID="i-**************" -echo "INSTANCE_ID=" $INSTANCE_ID -aws ssm start-session --target $INSTANCE_ID --region $AWS_REGION -sudo cat /var/log/cloud-init-output.log -``` - - - -3. How can I restart the Edge Node? - -Please enter the [AWS Management Console - EC2 Instances](https://us-east-2.console.aws.amazon.com/ec2/home?region=us-east-2#Instances:instanceState=running), choose the correct region, copy the instance ID you need to query. - -```bash -pwd -# Make sure you are in aws-blockchain-node-runners/lib/theta - -export INSTANCE_ID="i-**************" -echo "INSTANCE_ID=" $INSTANCE_ID - -aws ssm start-session --target $INSTANCE_ID --region $AWS_REGION -sudo docker restart $(sudo docker ps -q) -``` - - -5. Where can I find more infromation about EDGE RPC API? - -Please refer to more [JSON-RPC API METHODS](https://docs.thetatoken.org/docs/edge-node-api). The following are some commonly used API methods: - -```bash -# Query Status -curl -X POST -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","method":"edgecore.GetStatus","params":[],"id":1}' http://localhost:17888/rpc - -# Query Peers -curl -X POST -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","method":"edgecore.GetPeers","params":[],"id":1}' http://localhost:17888/rpc - -# Query Past Jobs -curl -X POST -H 'Content-Type: application/json' http://localhost:15888/rpc -d '{"jsonrpc": "2.0", "method": "edgelauncher.GetPastJobs", "params": [{"type": "lavita", "page": 0, "num": 10}], "id": 1}' - -``` - - -## Upgrades - -When nodes need to be upgraded or downgraded, CDK does not automatically attach old volumes to new EC2 instances. This is not yet automated and contributions are welcome! diff --git a/lib/theta/app.ts b/lib/theta/app.ts deleted file mode 100644 index a17e09b7..00000000 --- a/lib/theta/app.ts +++ /dev/null @@ -1,39 +0,0 @@ -import 'dotenv/config' -import "source-map-support/register"; -import * as cdk from "aws-cdk-lib"; -import * as config from "./lib/config/edgeConfig"; -import * as configTypes from "./lib/config/edgeConfig.interface"; -import { EdgeCommonStack } from "./lib/common-stack"; -import { EdgeSingleNodeStack } from "./lib/single-node-stack"; -import * as nag from "cdk-nag"; - -const app = new cdk.App(); -cdk.Tags.of(app).add("Project", "AWS_THETA_EDGE"); - -new EdgeCommonStack(app, "theta-edge-common", { - stackName: `theta-edge-nodes-common`, - env: { account: config.baseConfig.accountId, region: config.baseConfig.region } -}); - -new EdgeSingleNodeStack(app, "theta-edge-single-node", { - stackName: `theta-edge-single-node-${config.baseNodeConfig.edgeNetwork}`, - - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - nodeRole: "single-node", - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - edgeNetwork: config.baseNodeConfig.edgeNetwork, - edgeNodeGpu: config.baseNodeConfig.edgeNodeGpu, - edgeLauncherVersion: config.baseNodeConfig.edgeLauncherVersion, - dataVolume: config.baseNodeConfig.dataVolume -}); - - -// Security Check -cdk.Aspects.of(app).add( - new nag.AwsSolutionsChecks({ - verbose: false, - reports: true, - logIgnores: false - }) -); diff --git a/lib/theta/cdk.json b/lib/theta/cdk.json deleted file mode 100644 index 7714e8c2..00000000 --- a/lib/theta/cdk.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "app": "npx ts-node --prefer-ts-exts app.ts", - "watch": { - "include": [ - "**" - ], - "exclude": [ - "README.md", - "cdk*.json", - "**/*.d.ts", - "**/*.js", - "tsconfig.json", - "package*.json", - "yarn.lock", - "node_modules", - "test" - ] - }, - "context": { - "@aws-cdk/aws-lambda:recognizeLayerVersion": true, - "@aws-cdk/core:checkSecretUsage": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ], - "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, - "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, - "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, - "@aws-cdk/aws-iam:minimizePolicies": true, - "@aws-cdk/core:validateSnapshotRemovalPolicy": true, - "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, - "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, - "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, - "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, - "@aws-cdk/core:enablePartitionLiterals": true, - "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, - "@aws-cdk/aws-iam:standardizedServicePrincipals": true, - "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, - "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, - "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, - "@aws-cdk/aws-route53-patters:useCertificate": true, - "@aws-cdk/customresources:installLatestAwsSdkDefault": false, - "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, - "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, - "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, - "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, - "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, - "@aws-cdk/aws-redshift:columnId": true, - "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, - "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, - "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, - "@aws-cdk/aws-kms:aliasNameRef": true, - "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, - "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, - "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true - } -} diff --git a/lib/theta/doc/assets/Architecture-Theta-Edge-Single-Node.drawio b/lib/theta/doc/assets/Architecture-Theta-Edge-Single-Node.drawio deleted file mode 100644 index 7e39adc9..00000000 --- a/lib/theta/doc/assets/Architecture-Theta-Edge-Single-Node.drawio +++ /dev/null @@ -1,330 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/theta/doc/assets/Architecture-Theta-Edge-Single-Node.drawio.png b/lib/theta/doc/assets/Architecture-Theta-Edge-Single-Node.drawio.png deleted file mode 100644 index 3260b72edd96155fe391e67b1d7646a59f084067..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116339 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 z 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 bG+f}~-w7djw1SR;fIoMX)D-jOAN&75CZ=E# diff --git a/lib/theta/jest.config.js b/lib/theta/jest.config.js deleted file mode 100644 index 08263b89..00000000 --- a/lib/theta/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - testEnvironment: 'node', - roots: ['/test'], - testMatch: ['**/*.test.ts'], - transform: { - '^.+\\.tsx?$': 'ts-jest' - } -}; diff --git a/lib/theta/lib/assets/cfn-hup/cfn-auto-reloader.conf b/lib/theta/lib/assets/cfn-hup/cfn-auto-reloader.conf deleted file mode 100644 index 3cd32a0a..00000000 --- a/lib/theta/lib/assets/cfn-hup/cfn-auto-reloader.conf +++ /dev/null @@ -1,4 +0,0 @@ -[cfn-auto-reloader-hook] -triggers=post.update -path=Resources.WebServerHost.Metadata.AWS::CloudFormation::Init -action=/opt/aws/bin/cfn-init -v --stack __AWS_STACK_NAME__ --resource WebServerHost --region __AWS_REGION__ diff --git a/lib/theta/lib/assets/cfn-hup/cfn-hup.conf b/lib/theta/lib/assets/cfn-hup/cfn-hup.conf deleted file mode 100644 index 2163b37a..00000000 --- a/lib/theta/lib/assets/cfn-hup/cfn-hup.conf +++ /dev/null @@ -1,5 +0,0 @@ -[main] -stack=__AWS_STACK_ID__ -region=__AWS_REGION__ -# The interval used to check for changes to the resource metadata in minutes. Default is 15 -interval=2 diff --git a/lib/theta/lib/assets/cfn-hup/cfn-hup.service b/lib/theta/lib/assets/cfn-hup/cfn-hup.service deleted file mode 100644 index 2660ea46..00000000 --- a/lib/theta/lib/assets/cfn-hup/cfn-hup.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=cfn-hup daemon -[Service] -Type=simple -ExecStart=/usr/local/bin/cfn-hup -Restart=always -[Install] -WantedBy=multi-user.target diff --git a/lib/theta/lib/assets/cw-agent.json b/lib/theta/lib/assets/cw-agent.json deleted file mode 100644 index 28833017..00000000 --- a/lib/theta/lib/assets/cw-agent.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "agent": { - "metrics_collection_interval": 60, - "run_as_user": "root" - }, - "metrics": { - "aggregation_dimensions": [ - [ - "InstanceId" - ] - ], - "append_dimensions": { - "InstanceId": "${aws:InstanceId}" - }, - "metrics_collected": { - "cpu": { - "measurement": [ - "cpu_usage_idle", - "cpu_usage_iowait", - "cpu_usage_user", - "cpu_usage_system" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ], - "totalcpu": false - }, - "disk": { - "measurement": [ - "used_percent" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ] - }, - "diskio": { - "measurement": [ - "io_time", - "write_bytes", - "read_bytes", - "writes", - "reads", - "write_time", - "read_time", - "iops_in_progress" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ] - }, - "mem": { - "measurement": [ - "mem_used_percent", - "mem_cached" - ], - "metrics_collection_interval": 60 - }, - "netstat": { - "measurement": [ - "tcp_established", - "tcp_time_wait" - ], - "metrics_collection_interval": 60 - }, - "swap": { - "measurement": [ - "swap_used_percent" - ], - "metrics_collection_interval": 60 - } - } - } -} diff --git a/lib/theta/lib/assets/setup-instance-store-volumes.sh b/lib/theta/lib/assets/setup-instance-store-volumes.sh deleted file mode 100644 index e55c630c..00000000 --- a/lib/theta/lib/assets/setup-instance-store-volumes.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -source /etc/environment - -if [[ "$DATA_VOLUME_TYPE" == "instance-store" ]]; then - echo "Data volume type is instance store" - export DATA_VOLUME_ID=/dev/$(lsblk -lnb | awk 'max < $4 {max = $4; vol = $1} END {print vol}') -fi - -if [ -n "$DATA_VOLUME_ID" ]; then - if [ $(df --output=target | grep -c "/data") -lt 1 ]; then - echo "Checking fstab for Data volume" - - mkfs.ext4 $DATA_VOLUME_ID - echo "Data volume formatted. Mounting..." - echo "waiting for volume to get UUID" - OUTPUT=0; - while [ "$OUTPUT" = 0 ]; do - DATA_VOLUME_UUID=$(lsblk -fn -o UUID $DATA_VOLUME_ID) - OUTPUT=$(echo $DATA_VOLUME_UUID | grep -c - $2) - echo $OUTPUT - done - DATA_VOLUME_UUID=$(lsblk -fn -o UUID $DATA_VOLUME_ID) - DATA_VOLUME_FSTAB_CONF="UUID=$DATA_VOLUME_UUID /data ext4 defaults 0 2" - echo "DATA_VOLUME_ID="$DATA_VOLUME_ID - echo "DATA_VOLUME_UUID="$DATA_VOLUME_UUID - echo "DATA_VOLUME_FSTAB_CONF="$DATA_VOLUME_FSTAB_CONF - - # Check if data disc is already in fstab and replace the line if it is with the new disc UUID - if [ $(grep -c "data" /etc/fstab) -gt 0 ]; then - SED_REPLACEMENT_STRING="$(grep -n "/data" /etc/fstab | cut -d: -f1)s#.*#$DATA_VOLUME_FSTAB_CONF#" - cp /etc/fstab /etc/fstab.bak - sed -i "$SED_REPLACEMENT_STRING" /etc/fstab - else - echo $DATA_VOLUME_FSTAB_CONF | sudo tee -a /etc/fstab - fi - - sudo mount -a - - else - echo "Data volume is mounted, nothing changed" - fi -fi diff --git a/lib/theta/lib/assets/sync-checker/syncchecker-theta.sh b/lib/theta/lib/assets/sync-checker/syncchecker-theta.sh deleted file mode 100644 index 70e6f624..00000000 --- a/lib/theta/lib/assets/sync-checker/syncchecker-theta.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - - -THETA_BLOCK_HEIGHT=`curl -X POST -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","method":"edgecore.GetStatus","params":[],"id":1}' http://localhost:17888/rpc | jq -r ".result.current_height"` - -# Sending data to CloudWatch -TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") -INSTANCE_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/instance-id) -REGION=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq .region -r) -TIMESTAMP=$(date +"%Y-%m-%dT%H:%M:%S%:z") - -aws cloudwatch put-metric-data --metric-name theta_current_block_height --namespace CWAgent --value $THETA_BLOCK_HEIGHT --timestamp $TIMESTAMP --dimensions InstanceId=$INSTANCE_ID --region $REGION - -curl -X POST -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","method":"edgecore.GetPeers","params":[],"id":1}' http://localhost:17888/rpc | jq -rc ".result.peers[]" | while IFS=$'\n' read -r peer; do - aws cloudwatch put-metric-data --metric-name edge_peer --namespace CWAgent --value 1 --timestamp $TIMESTAMP --dimensions InstanceId=$INSTANCE_ID,PeerAddress=$peer --region $REGION --output text -done; diff --git a/lib/theta/lib/assets/user-data/node.sh b/lib/theta/lib/assets/user-data/node.sh deleted file mode 100644 index 743b003c..00000000 --- a/lib/theta/lib/assets/user-data/node.sh +++ /dev/null @@ -1,176 +0,0 @@ -#!/bin/bash -set +e - -echo "AWS_REGION=${_AWS_REGION_}" >> /etc/environment -echo "ASSETS_S3_PATH=${_ASSETS_S3_PATH_}" >> /etc/environment -echo "STACK_NAME=${_STACK_NAME_}" >> /etc/environment -echo "STACK_ID=${_STACK_ID_}" >> /etc/environment -echo "RESOURCE_ID=${_NODE_CF_LOGICAL_ID_}" >> /etc/environment -echo "DATA_VOLUME_TYPE=${_DATA_VOLUME_TYPE_}" >> /etc/environment -echo "DATA_VOLUME_SIZE=${_DATA_VOLUME_SIZE_}" >> /etc/environment -echo "EDGE_NETWORK=${_EDGE_NETWORK_}" >> /etc/environment -echo "EDGE_LAUNCHER_VERSION=${_EDGE_LAUNCHER_VERSION_}" >> /etc/environment -echo "EDGE_NODE_GPU=${_EDGE_NODE_GPU_}" >> /etc/environment -echo "NODE_ROLE=${_NODE_ROLE_}" >> /etc/environment -EDGE_NODE_PASSWORD_SSM_ARN=${_EDGE_NODE_PASSWORD_SSM_ARN_} - -source /etc/environment - -arch=$(uname -m) - -echo "Architecture detected: $arch" - -if [ "$arch" == "x86_64" ]; then - SSM_AGENT_BINARY_URI=https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm - AWS_CLI_BINARY_URI=https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip - S5CMD_URI=https://github.com/peak/s5cmd/releases/download/v2.1.0/s5cmd_2.1.0_Linux-64bit.tar.gz -else - SSM_AGENT_BINARY_URI=https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_arm64/amazon-ssm-agent.rpm - AWS_CLI_BINARY_URI=https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip - S5CMD_URI=https://github.com/peak/s5cmd/releases/download/v2.1.0/s5cmd_2.1.0_Linux-arm64.tar.gz -fi - -echo "Updating and installing required system packages" -yum update -y -amazon-linux-extras install epel -y -yum groupinstall "Development Tools" -y -yum -y install amazon-cloudwatch-agent collectd jq gcc10-10.5.0-1.amzn2.0.2 ncurses-devel telnet aws-cfn-bootstrap - -cd /opt - -echo 'Installing AWS CLI v2' -curl $AWS_CLI_BINARY_URI -o "awscliv2.zip" -unzip -q awscliv2.zip -./aws/install -rm /usr/bin/aws -ln /usr/local/bin/aws /usr/bin/aws - -echo "Downloading assets zip file" -aws s3 cp $ASSETS_S3_PATH ./assets.zip --region $AWS_REGION -unzip -q assets.zip - -echo 'Configuring CloudWatch Agent' -cp /opt/cw-agent.json /opt/aws/amazon-cloudwatch-agent/etc/custom-amazon-cloudwatch-agent.json - -echo "Starting CloudWatch Agent" -/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ --a fetch-config -c file:/opt/aws/amazon-cloudwatch-agent/etc/custom-amazon-cloudwatch-agent.json -m ec2 -s -systemctl status amazon-cloudwatch-agent - -aws configure set default.s3.max_concurrent_requests 50 -aws configure set default.s3.multipart_chunksize 256MB - -echo 'Installing SSM Agent' -yum install -y $SSM_AGENT_BINARY_URI - -if [ "$EDGE_NODE_GPU" == "enabled" ]; then - echo "Installing nvidia drivers" - yum install kernel-devel-$(uname -r) kernel-headers-$(uname -r) -y - - sudo mv /usr/bin/gcc /usr/bin/gcc.bak - sudo mv /usr/bin/cc /usr/bin/cc.bak - - - sudo ln -s /usr/bin/x86_64-redhat-linux-gcc10-gcc /usr/bin/gcc - sudo ln -s /usr/bin/x86_64-redhat-linux-gcc10-gcc /usr/bin/cc - - wget https://us.download.nvidia.com/tesla/535.161.08/NVIDIA-Linux-x86_64-535.161.08.run - chmod +x NVIDIA-Linux-x86_64-535.161.08.run - yes Y | sudo bash NVIDIA-Linux-x86_64-535.161.08.run --ui=none - rm NVIDIA-Linux-x86_64-535.161.08.run - - distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \ - && curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.repo | sudo tee /etc/yum.repos.d/nvidia-docker.repo - - yum install -y nvidia-docker2 - - sudo rm /usr/bin/gcc - sudo rm /usr/bin/cc - - sudo mv /usr/bin/gcc.bak /usr/bin/gcc - sudo mv /usr/bin/cc.bak /usr/bin/cc -fi - -echo "Installing docker" -yum install docker -y -sudo service docker start -sudo systemctl enable docker - -echo 'Adding bcuser user and group' -sudo groupadd -g 1002 bcuser -sudo useradd -u 1002 -g 1002 -m -s /bin/bash bcuser -sudo usermod -aG docker bcuser - -echo "Configuring syncchecker script" -cd /opt -mv /opt/sync-checker/syncchecker-theta.sh /opt/syncchecker.sh -chmod +x /opt/syncchecker.sh - -(crontab -l; echo "*/1 * * * * /opt/syncchecker.sh >/tmp/syncchecker.log 2>&1") | crontab - -crontab -l - -if [ "$NODE_ROLE" == "single-node" ]; then - echo "Single node. Signaling completion to CloudFormation" - /opt/aws/bin/cfn-signal --stack $STACK_NAME --resource $RESOURCE_ID --region $AWS_REGION -fi - -if [ "$NODE_ROLE" == "single-node" ]; then - echo "Single node. Wait for one minute for the volume to be available" - sleep 60 -fi - -mkdir -p /data - -if [[ "$DATA_VOLUME_TYPE" == "instance-store" ]]; then - echo "Data volume type is instance store" - - cd /opt - chmod +x /opt/setup-instance-store-volumes.sh - - (crontab -l; echo "@reboot /opt/setup-instance-store-volumes.sh >/tmp/setup-instance-store-volumes.log 2>&1") | crontab - - crontab -l - - DATA_VOLUME_ID=/dev/$(lsblk -lnb | awk 'max < $4 {max = $4; vol = $1} END {print vol}') - -else - echo "Data volume type is EBS" - - DATA_VOLUME_ID=/dev/$(lsblk -lnb | awk -v VOLUME_SIZE_BYTES="$DATA_VOLUME_SIZE" '{if ($4== VOLUME_SIZE_BYTES) {print $1}}') -fi - -mkfs -t ext4 $DATA_VOLUME_ID -echo "waiting for volume to get UUID" - OUTPUT=0; - while [ "$OUTPUT" = 0 ]; do - DATA_VOLUME_UUID=$(lsblk -fn -o UUID $DATA_VOLUME_ID) - OUTPUT=$(echo $DATA_VOLUME_UUID | grep -c - $2) - echo $OUTPUT - done -DATA_VOLUME_UUID=$(lsblk -fn -o UUID $DATA_VOLUME_ID) -DATA_VOLUME_FSTAB_CONF="UUID=$DATA_VOLUME_UUID /data ext4 defaults 0 2" -echo "DATA_VOLUME_ID="$DATA_VOLUME_ID -echo "DATA_VOLUME_UUID="$DATA_VOLUME_UUID -echo "DATA_VOLUME_FSTAB_CONF="$DATA_VOLUME_FSTAB_CONF -echo $DATA_VOLUME_FSTAB_CONF | tee -a /etc/fstab -mount -a - -lsblk -d - -chown bcuser:bcuser -R /data - -docker pull thetalabsorg/edgelauncher_mainnet:latest - -sudo su bcuser - -EDGE_NODE_PASSWORD=$(aws secretsmanager get-secret-value --secret-id $EDGE_NODE_PASSWORD_SSM_ARN --query SecretString --output text --region $AWS_REGION) - -if [[ "$EDGE_NODE_GPU" == "enabled" ]]; then - docker run -d --gpus all --user $(id -u):$(id -g) -e EDGELAUNCHER_CONFIG_PATH=/edgelauncher/data/$EDGE_NETWORK -e PASSWORD=$EDGE_NODE_PASSWORD -v /data/edgelauncher:/edgelauncher/data/$EDGE_NETWORK -p 127.0.0.1:15888:15888 -p 127.0.0.1:17888:17888 -p 127.0.0.1:17935:17935 --name edgelauncher --restart unless-stopped -it thetalabsorg/edgelauncher_$EDGE_NETWORK:$EDGE_LAUNCHER_VERSION - -else - docker run -d --user $(id -u):$(id -g) -e EDGELAUNCHER_CONFIG_PATH=/edgelauncher/data/$EDGE_NETWORK -e PASSWORD=$EDGE_NODE_PASSWORD -v /data/edgelauncher:/edgelauncher/data/$EDGE_NETWORK -p 127.0.0.1:15888:15888 -p 127.0.0.1:17888:17888 -p 127.0.0.1:17935:17935 --name edgelauncher --restart unless-stopped -it thetalabsorg/edgelauncher_$EDGE_NETWORK:$EDGE_LAUNCHER_VERSION -fi -EDGE_NODE_PASSWORD="" - -echo "All Done!!" -set -e diff --git a/lib/theta/lib/common-stack.ts b/lib/theta/lib/common-stack.ts deleted file mode 100644 index be86ae5d..00000000 --- a/lib/theta/lib/common-stack.ts +++ /dev/null @@ -1,81 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as iam from "aws-cdk-lib/aws-iam"; -import * as secrets from "aws-cdk-lib/aws-secretsmanager"; -import * as nag from "cdk-nag"; - -export interface EdgeCommonStackProps extends cdk.StackProps { - -} - -export class EdgeCommonStack extends cdk.Stack { - AWS_STACK_NAME = cdk.Stack.of(this).stackName; - AWS_ACCOUNT_ID = cdk.Stack.of(this).account; - - constructor(scope: cdkConstructs.Construct, id: string, props: EdgeCommonStackProps) { - super(scope, id, props); - - const region = cdk.Stack.of(this).region; - - const instanceRole = new iam.Role(this, "node-role", { - assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com"), - managedPolicies: [ - iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMManagedInstanceCore"), - iam.ManagedPolicy.fromAwsManagedPolicyName("CloudWatchAgentServerPolicy") - - ] - }); - - instanceRole.addToPolicy(new iam.PolicyStatement({ - resources: ["*"], - actions: ["cloudformation:SignalResource"] - })); - - - new cdk.CfnOutput(this, "Instance Role ARN", { - value: instanceRole.roleArn, - exportName: "EdgeNodeInstanceRoleArn" - }); - - - const edgeNodePassword = new cdk.CfnParameter(this, 'edgeNodePassword', { - type: 'String', - noEcho: true - }); - - const secretEdgeNodePassword = new secrets.Secret(this, 'Edge Node Password', { - secretName: 'edgeNodePassword', - secretStringValue: cdk.SecretValue.unsafePlainText( - edgeNodePassword.valueAsString - ), - }); - - secretEdgeNodePassword.grantRead(instanceRole) - - new cdk.CfnOutput(this, "ssm-edge-node-password-ARN", { - value: secretEdgeNodePassword.secretArn, - exportName: "SSMEdgeNodePasswordARN", - }); - - - // cdk-nag suppressions - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-IAM4", - reason: "AmazonSSMManagedInstanceCore and CloudWatchAgentServerPolicy are restrictive enough" - }, - { - id: "AwsSolutions-IAM5", - reason: "Can't target specific stack: https://github.com/aws/aws-cdk/issues/22657" - }, - { - id: "AwsSolutions-SMG4", - reason: "Secret 'Edge Node Password' isn't meant to be rotated" - } - ], - true - ); - } -} diff --git a/lib/theta/lib/config/edgeConfig.interface.ts b/lib/theta/lib/config/edgeConfig.interface.ts deleted file mode 100644 index e04d1c3f..00000000 --- a/lib/theta/lib/config/edgeConfig.interface.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as configTypes from "../../../constructs/config.interface"; - -export type EdgeNetwork = "mainnet"; - -export type EdgeNodeRole = "single-node"; - -export type EdgeNodeGPU = "enabled" | "disabled"; - -export type EdgeLauncherVersion = "latest" | string; - - - -export interface EdgeDataVolumeConfig extends configTypes.DataVolumeConfig { - -} - -export interface EdgeBaseConfig extends configTypes.BaseConfig { - -} - -export interface EdgeBaseNodeConfig extends configTypes.BaseNodeConfig { - edgeNetwork: EdgeNetwork; - edgeNodeGpu: EdgeNodeGPU; - edgeLauncherVersion: EdgeLauncherVersion - dataVolume: EdgeDataVolumeConfig; -} diff --git a/lib/theta/lib/config/edgeConfig.ts b/lib/theta/lib/config/edgeConfig.ts deleted file mode 100644 index 10214a73..00000000 --- a/lib/theta/lib/config/edgeConfig.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as configTypes from "./edgeConfig.interface"; -import * as constants from "../../../constructs/constants"; - -const parseDataVolumeType = (dataVolumeType: string) => { - switch (dataVolumeType) { - case "gp3": - return ec2.EbsDeviceVolumeType.GP3; - case "io2": - return ec2.EbsDeviceVolumeType.IO2; - case "io1": - return ec2.EbsDeviceVolumeType.IO1; - case "instance-store": - return constants.InstanceStoreageDeviceVolumeType; - default: - return ec2.EbsDeviceVolumeType.GP3; - } -}; - -export const baseConfig: configTypes.EdgeBaseConfig = { - accountId: process.env.AWS_ACCOUNT_ID || "xxxxxxxxxxx", - region: process.env.AWS_REGION || "us-east-1" -}; - -export const baseNodeConfig: configTypes.EdgeBaseNodeConfig = { - instanceType: new ec2.InstanceType(process.env.EDGE_NODE_INSTANCE_TYPE ? process.env.EDGE_NODE_INSTANCE_TYPE : "c4.xlarge"), - instanceCpuType: process.env.EDGE_NODE_CPU_TYPE?.toLowerCase() == "x86_64" ? ec2.AmazonLinuxCpuType.X86_64 : ec2.AmazonLinuxCpuType.X86_64, - edgeNetwork: process.env.EDGE_CLUSTER || "mainnet", - edgeLauncherVersion: process.env.EDGE_LAUNCHER_VERSION || "latest", - edgeNodeGpu: process.env.EDGE_NODE_GPU || "disabled", - dataVolume: { - sizeGiB: process.env.EDGE_DATA_VOL_SIZE ? parseInt(process.env.EDGE_DATA_VOL_SIZE) : 256, - type: parseDataVolumeType(process.env.EDGE_DATA_VOL_TYPE?.toLowerCase() ? process.env.EDGE_DATA_VOL_TYPE?.toLowerCase() : "gp3"), - iops: process.env.EDGE_DATA_VOL_IOPS ? parseInt(process.env.EDGE_DATA_VOL_IOPS) : 10000, - throughput: process.env.EDGE_DATA_VOL_THROUGHPUT ? parseInt(process.env.EDGE_DATA_VOL_THROUGHPUT) : 700 - } -}; diff --git a/lib/theta/lib/constructs/edge-node-security-group.ts b/lib/theta/lib/constructs/edge-node-security-group.ts deleted file mode 100644 index 71463faf..00000000 --- a/lib/theta/lib/constructs/edge-node-security-group.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as nag from "cdk-nag"; - -export interface EdgeNodeSecurityGroupConstructsProps { - vpc: cdk.aws_ec2.IVpc; -} - -export class EdgeNodeSecurityGroupConstructs extends cdkConstructs.Construct { - public securityGroup: cdk.aws_ec2.SecurityGroup; - - constructor(scope: cdkConstructs.Construct, id: string, props: EdgeNodeSecurityGroupConstructsProps) { - super(scope, id); - - const { - vpc - } = props; - - const sg = new ec2.SecurityGroup(this, `rpc-node-security-group`, { - vpc, - description: "Security Group for Blockchain nodes", - allowAllOutbound: true - }); - - - // private ports - sg.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(15888), "Theta Edge Node RPC Port"); - sg.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(17888), "Theta Edge Core RPC Port"); - sg.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(17935), "Theta Edge Encoder RPC Port"); - - - this.securityGroup = sg; - - } -} diff --git a/lib/theta/lib/constructs/node-cw-dashboard.ts b/lib/theta/lib/constructs/node-cw-dashboard.ts deleted file mode 100644 index 5cf85a3d..00000000 --- a/lib/theta/lib/constructs/node-cw-dashboard.ts +++ /dev/null @@ -1,584 +0,0 @@ -export const SyncNodeCWDashboardJSON = (instanceType: string, dataVolumeType: string) => { - - let disk_name: string = "xvdf"; - if (instance_storage_types.has(instanceType) && dataVolumeType != "instance-store"){ - disk_name = "nvme2n1" - }else if (instance_storage_types.has(instanceType) && dataVolumeType == "instance-store"){ - disk_name = "nvme1n1" - } - - return { - "widgets": [ - { - "height": 5, - "width": 6, - "y": 0, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "CPUUtilization", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "CPU utilization (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 18, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "NetworkIn", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Network in (bytes)" - } - }, - { - "height": 5, - "width": 6, - "y": 0, - "x": 18, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Average", - "period": 300, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ "AWS/EC2", "NetworkOut", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Network out (bytes)" - } - }, - { - "height": 5, - "width": 6, - "y": 10, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 300, - "metrics": [ - [ "CWAgent", "mem_used_percent", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Mem Used (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 0, - "type": "metric", - "properties": { - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 300, - "metrics": [ - [ "CWAgent", "cpu_usage_iowait", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "CPU Usage IO wait (%)" - } - }, - { - "height": 5, - "width": 6, - "y": 0, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ { "expression": "m7/PERIOD(m7)", "label": "Read", "id": "e7" } ], - [ "CWAgent", "diskio_reads", "InstanceId", "${INSTANCE_ID}", "name", disk_name, { "id": "m7", "visible": false, "stat": "Sum", "period": 60 } ], - [ { "expression": "m8/PERIOD(m8)", "label": "Write", "id": "e8" } ], - [ "CWAgent", "diskio_writes", "InstanceId", "${INSTANCE_ID}", "name", disk_name, { "id": "m8", "visible": false, "stat": "Sum", "period": 60 } ] - ], - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Sum", - "period": 60, - "title": `${disk_name} Volume Read/Write (IO/sec)` - } - }, - { - "height": 4, - "width": 6, - "y": 0, - "x": 12, - "type": "metric", - "properties": { - "metrics": [ - [ "CWAgent", "theta_current_block_height", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "sparkline": true, - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Maximum", - "period": 60, - "title": "Theta Client Block Height" - } - }, - { - "height": 4, - "width": 6, - "y": 4, - "x": 12, - "type": "metric", - "properties": { - "sparkline": true, - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Maximum", - "period": 60, - "metrics": [ - [ { "expression": "SELECT COUNT(edge_peer) FROM CWAgent GROUP BY InstanceId", "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ] - ], - "title": "Theta Client Peer Count" - } - }, - { - "height": 5, - "width": 6, - "y": 5, - "x": 6, - "type": "metric", - "properties": { - "view": "timeSeries", - "stat": "Sum", - "period": 60, - "stacked": false, - "yAxis": { - "left": { - "min": 0 - } - }, - "region": "${REGION}", - "metrics": [ - [ { "expression": "IF(m7_2 !=0, (m7_1 / m7_2), 0)", "label": "Read", "id": "e7" } ], - [ "CWAgent", "diskio_read_time", "InstanceId", "${INSTANCE_ID}", "name", disk_name, { "id": "m7_1", "visible": false, "stat": "Sum", "period": 60 } ], - [ "CWAgent", "diskio_reads", "InstanceId", "${INSTANCE_ID}", "name", disk_name, { "id": "m7_2", "visible": false, "stat": "Sum", "period": 60 } ], - [ { "expression": "IF(m7_4 !=0, (m7_3 / m7_4), 0)", "label": "Write", "id": "e8" } ], - [ "CWAgent", "diskio_write_time", "InstanceId", "${INSTANCE_ID}", "name", disk_name, { "id": "m7_3", "visible": false, "stat": "Sum", "period": 60 } ], - [ "CWAgent", "diskio_writes", "InstanceId", "${INSTANCE_ID}", "name", disk_name, { "id": "m7_4", "visible": false, "stat": "Sum", "period": 60 } ] - ], - "title": `${disk_name} Volume Read/Write latency (ms/op)` - } - }, - { - "height": 5, - "width": 6, - "y": 10, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ { "expression": "(m2/1048576)/PERIOD(m2)", "label": "Read", "id": "e2", "period": 60, "region": "${REGION}" } ], - [ "CWAgent", "diskio_read_bytes", "InstanceId", "${INSTANCE_ID}", "name", disk_name, { "id": "m2", "stat": "Sum", "visible": false, "period": 60 } ], - [ { "expression": "(m3/1048576)/PERIOD(m3)", "label": "Write", "id": "e3", "period": 60, "region": "${REGION}" } ], - [ "CWAgent", "diskio_write_bytes", "InstanceId", "${INSTANCE_ID}", "name", disk_name, { "id": "m3", "stat": "Sum", "visible": false, "period": 60 } ] - ], - "view": "timeSeries", - "stacked": false, - "region": "${REGION}", - "stat": "Average", - "period": 60, - "title": `${disk_name} Volume Read/Write throughput (MiB/sec)` - } - }, - { - "height": 3, - "width": 6, - "y": 15, - "x": 6, - "type": "metric", - "properties": { - "metrics": [ - [ "CWAgent", "disk_used_percent", "path", "/data", "InstanceId", "${INSTANCE_ID}", "device", disk_name, "fstype", "ext4", { "region": "${REGION}", "label": "/data" } ] - ], - "sparkline": true, - "view": "singleValue", - "region": "${REGION}", - "title": `${disk_name} Disk Used (%)`, - "period": 60, - "stat": "Average" - } - } - ] - } - -} - -const instance_storage_types = new Set(["d3en.12xlarge", - "d3en.8xlarge", - "d3en.6xlarge", - "d3en.4xlarge", - "g6.48xlarge", - "i3en.24xlarge", - "i3en.metal", - "d3en.2xlarge", - "d2.8xlarge", - "d3.8xlarge", - "p5.48xlarge", - "i3en.12xlarge", - "i4i.metal", - "i4i.32xlarge", - "im4gn.16xlarge", - "is4gen.8xlarge", - "d3en.xlarge", - "d2.4xlarge", - "d3.4xlarge", - "i4i.24xlarge", - "h1.16xlarge", - "i3.16xlarge", - "g6.24xlarge", - "i3.metal", - "g6.12xlarge", - "i3en.6xlarge", - "i4i.16xlarge", - "is4gen.4xlarge", - "i4g.16xlarge", - "im4gn.8xlarge", - "d2.2xlarge", - "d3.2xlarge", - "i4i.12xlarge", - "p4d.24xlarge", - "h1.8xlarge", - "r6idn.metal", - "m6id.32xlarge", - "c6id.32xlarge", - "r6id.32xlarge", - "c6id.metal", - "r6id.metal", - "m6idn.metal", - "m6id.metal", - "g5.48xlarge", - "r6idn.32xlarge", - "i3.8xlarge", - "m6idn.32xlarge", - "trn1.32xlarge", - "trn1n.32xlarge", - "im4gn.4xlarge", - "i3en.3xlarge", - "i4i.8xlarge", - "i4g.8xlarge", - "is4gen.2xlarge", - "i2.8xlarge", - "d2.xlarge", - "d3.xlarge", - "r6id.24xlarge", - "m6id.24xlarge", - "c6id.24xlarge", - "r6idn.24xlarge", - "m6idn.24xlarge", - "i3en.2xlarge", - "h1.4xlarge", - "dl1.24xlarge", - "x1e.32xlarge", - "x1.32xlarge", - "x2iedn.32xlarge", - "x2idn.metal", - "r7gd.metal", - "r6id.16xlarge", - "m6id.16xlarge", - "m7gd.16xlarge", - "x2iedn.metal", - "g5.12xlarge", - "r6idn.16xlarge", - "x2gd.metal", - "m6gd.16xlarge", - "c5ad.24xlarge", - "c6id.16xlarge", - "m7gd.metal", - "m6gd.metal", - "c7gd.metal", - "r7gd.16xlarge", - "m6idn.16xlarge", - "c7gd.16xlarge", - "c6gd.16xlarge", - "x2idn.32xlarge", - "g5.24xlarge", - "i3.4xlarge", - "x2gd.16xlarge", - "r6gd.16xlarge", - "g6.16xlarge", - "r6gd.metal", - "c6gd.metal", - "f1.16xlarge", - "i4i.4xlarge", - "i4g.4xlarge", - "im4gn.2xlarge", - "is4gen.xlarge", - "c5d.24xlarge", - "c5d.metal", - "r5d.24xlarge", - "m5d.24xlarge", - "m5dn.24xlarge", - "m5d.metal", - "r5ad.24xlarge", - "r5dn.24xlarge", - "m5ad.24xlarge", - "r5dn.metal", - "m5dn.metal", - "r5d.metal", - "i2.4xlarge", - "r6idn.12xlarge", - "c6id.12xlarge", - "m6id.12xlarge", - "x2iedn.24xlarge", - "m6gd.12xlarge", - "m6idn.12xlarge", - "c7gd.12xlarge", - "x2idn.24xlarge", - "r6id.12xlarge", - "r6gd.12xlarge", - "r7gd.12xlarge", - "c6gd.12xlarge", - "m7gd.12xlarge", - "x2gd.12xlarge", - "i3en.xlarge", - "m5ad.16xlarge", - "r5d.16xlarge", - "m5dn.16xlarge", - "r5ad.16xlarge", - "m5d.16xlarge", - "r5dn.16xlarge", - "c5ad.16xlarge", - "g4ad.16xlarge", - "h1.2xlarge", - "x1e.16xlarge", - "x1.16xlarge", - "r6id.8xlarge", - "r6idn.8xlarge", - "x2iedn.16xlarge", - "m6id.8xlarge", - "i3.2xlarge", - "x2idn.16xlarge", - "c6id.8xlarge", - "g5.16xlarge", - "r6gd.8xlarge", - "m6gd.8xlarge", - "c6gd.8xlarge", - "m6idn.8xlarge", - "x2gd.8xlarge", - "r7gd.8xlarge", - "m7gd.8xlarge", - "c7gd.8xlarge", - "i4i.2xlarge", - "im4gn.xlarge", - "i4g.2xlarge", - "is4gen.large", - "m5dn.12xlarge", - "r5d.12xlarge", - "z1d.12xlarge", - "c5d.18xlarge", - "r5dn.12xlarge", - "m5d.12xlarge", - "c5d.12xlarge", - "c5ad.12xlarge", - "g4dn.metal", - "m5ad.12xlarge", - "r5ad.12xlarge", - "z1d.metal", - "p3dn.24xlarge", - "m1.xlarge", - "m2.4xlarge", - "c1.xlarge", - "i2.2xlarge", - "i3en.large", - "m5d.8xlarge", - "r5dn.8xlarge", - "m5dn.8xlarge", - "c5ad.8xlarge", - "r5d.8xlarge", - "m5ad.8xlarge", - "r5ad.8xlarge", - "g4ad.8xlarge", - "x1e.8xlarge", - "r6idn.4xlarge", - "c6id.4xlarge", - "r6gd.4xlarge", - "r6id.4xlarge", - "c7gd.4xlarge", - "m6id.4xlarge", - "m6gd.4xlarge", - "m6idn.4xlarge", - "i3.xlarge", - "r7gd.4xlarge", - "x2iedn.8xlarge", - "c6gd.4xlarge", - "m7gd.4xlarge", - "x2gd.4xlarge", - "f1.4xlarge", - "i4i.xlarge", - "im4gn.large", - "i4g.xlarge", - "is4gen.medium", - "c5d.9xlarge", - "g5.8xlarge", - "g4dn.8xlarge", - "g4dn.12xlarge", - "z1d.6xlarge", - "gr6.8xlarge", - "g4dn.16xlarge", - "g6.8xlarge", - "m2.2xlarge", - "m1.large", - "i2.xlarge", - "r3.8xlarge", - "c3.8xlarge", - "r5ad.4xlarge", - "r5d.4xlarge", - "c5ad.4xlarge", - "g5.4xlarge", - "g6.4xlarge", - "m5dn.4xlarge", - "m5d.4xlarge", - "m5ad.4xlarge", - "r5dn.4xlarge", - "gr6.4xlarge", - "g4ad.4xlarge", - "x1e.4xlarge", - "x2iedn.4xlarge", - "x2gd.2xlarge", - "i3.large", - "m6id.2xlarge", - "r6id.2xlarge", - "c6id.2xlarge", - "m6idn.2xlarge", - "m7gd.2xlarge", - "c7gd.2xlarge", - "r7gd.2xlarge", - "r6gd.2xlarge", - "m6gd.2xlarge", - "c6gd.2xlarge", - "r6idn.2xlarge", - "trn1.2xlarge", - "f1.2xlarge", - "i4g.large", - "i4i.large", - "z1d.3xlarge", - "g6.2xlarge", - "g5.2xlarge", - "m2.xlarge", - "m1.medium", - "c5d.4xlarge", - "c1.medium", - "r3.4xlarge", - "c3.4xlarge", - "m5d.2xlarge", - "z1d.2xlarge", - "r5ad.2xlarge", - "m5dn.2xlarge", - "c5ad.2xlarge", - "m5ad.2xlarge", - "r5dn.2xlarge", - "r5d.2xlarge", - "g4ad.2xlarge", - "g6.xlarge", - "g5.xlarge", - "x1e.2xlarge", - "m6gd.xlarge", - "m6id.xlarge", - "m6idn.xlarge", - "c7gd.xlarge", - "x2iedn.2xlarge", - "c6id.xlarge", - "r6id.xlarge", - "c6gd.xlarge", - "r7gd.xlarge", - "m7gd.xlarge", - "r6gd.xlarge", - "r6idn.xlarge", - "x2gd.xlarge", - "g4dn.2xlarge", - "g4dn.4xlarge", - "c5d.2xlarge", - "r3.2xlarge", - "m3.2xlarge", - "c3.2xlarge", - "m1.small", - "r5ad.xlarge", - "r5dn.xlarge", - "m5dn.xlarge", - "m5ad.xlarge", - "g4ad.xlarge", - "c5ad.xlarge", - "m5d.xlarge", - "r5d.xlarge", - "z1d.xlarge", - "g4dn.xlarge", - "x1e.xlarge", - "r7gd.large", - "x2iedn.xlarge", - "c6id.large", - "c7gd.large", - "r6id.large", - "m6id.large", - "r6idn.large", - "m7gd.large", - "m6idn.large", - "c6gd.large", - "x2gd.large", - "m6gd.large", - "r6gd.large", - "c5d.xlarge", - "r3.xlarge", - "c3.xlarge", - "m3.xlarge", - "z1d.large", - "c5ad.large", - "r5dn.large", - "m5ad.large", - "m5d.large", - "r5d.large", - "r5ad.large", - "m5dn.large", - "m7gd.medium", - "m6gd.medium", - "r7gd.medium", - "c6gd.medium", - "x2gd.medium", - "r6gd.medium", - "c7gd.medium", - "c5d.large", - "r3.large", - "c3.large", - "m3.large", - "m3.medium"]) diff --git a/lib/theta/lib/single-node-stack.ts b/lib/theta/lib/single-node-stack.ts deleted file mode 100644 index 4e65b53a..00000000 --- a/lib/theta/lib/single-node-stack.ts +++ /dev/null @@ -1,142 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import * as cdkConstructs from "constructs"; -import * as ec2 from "aws-cdk-lib/aws-ec2"; -import * as iam from "aws-cdk-lib/aws-iam"; -import * as s3Assets from "aws-cdk-lib/aws-s3-assets"; -import * as path from "path"; -import * as fs from "fs"; -import * as nodeCwDashboard from "./constructs/node-cw-dashboard" -import * as cw from 'aws-cdk-lib/aws-cloudwatch'; -import * as constants from "../../constructs/constants"; -import { SingleNodeConstruct } from "../../constructs/single-node" -import * as configTypes from "./config/edgeConfig.interface"; -import { EdgeNodeSecurityGroupConstructs } from "./constructs/edge-node-security-group" -import * as nag from "cdk-nag"; - -export interface EdgeSingleNodeStackProps extends cdk.StackProps { - nodeRole: configTypes.EdgeNodeRole; - instanceType: ec2.InstanceType; - instanceCpuType: ec2.AmazonLinuxCpuType; - edgeNetwork: configTypes.EdgeNetwork; - edgeNodeGpu: configTypes.EdgeNodeGPU; - dataVolume: configTypes.EdgeDataVolumeConfig; - edgeLauncherVersion: configTypes.EdgeLauncherVersion -} - -export class EdgeSingleNodeStack extends cdk.Stack { - constructor(scope: cdkConstructs.Construct, id: string, props: EdgeSingleNodeStackProps) { - super(scope, id, props); - - // Setting up necessary environment variables - const REGION = cdk.Stack.of(this).region; - const STACK_NAME = cdk.Stack.of(this).stackName; - const STACK_ID = cdk.Stack.of(this).stackId; - const availabilityZones = cdk.Stack.of(this).availabilityZones; - const chosenAvailabilityZone = availabilityZones.slice(0, 1)[0]; - - // Getting our config from initialization properties - const { - instanceType, - nodeRole, - instanceCpuType, - edgeNetwork, - dataVolume, - edgeNodeGpu, - edgeLauncherVersion - } = props; - - // Using default VPC - const vpc = ec2.Vpc.fromLookup(this, "vpc", { isDefault: true }); - - // Setting up the security group for the node from Ethereum-specific construct - const instanceSG = new EdgeNodeSecurityGroupConstructs (this, "security-group", { - vpc: vpc, - }) - - // Making our scripts and configis from the local "assets" directory available for instance to download - const asset = new s3Assets.Asset(this, "assets", { - path: path.join(__dirname, "assets"), - }); - - // Getting the snapshot bucket name and IAM role ARN from the common stack - const importedInstanceRoleArn = cdk.Fn.importValue("EdgeNodeInstanceRoleArn"); - - const instanceRole = iam.Role.fromRoleArn(this, "iam-role", importedInstanceRoleArn); - - const sSMEdgeNodePasswordARN = cdk.Fn.importValue("SSMEdgeNodePasswordARN"); - - // Making sure our instance will be able to read the assets - asset.bucket.grantRead(instanceRole); - - // Setting up the node using generic Single Node constract - const node = new SingleNodeConstruct(this, "single-node", { - instanceName: STACK_NAME, - instanceType, - dataVolumes: [dataVolume], - machineImage: new ec2.AmazonLinuxImage({ - generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2, - kernel:ec2.AmazonLinuxKernel.KERNEL5_X, - cpuType: instanceCpuType, - }), - vpc, - availabilityZone: chosenAvailabilityZone, - role: instanceRole, - securityGroup: instanceSG.securityGroup, - vpcSubnets: { - subnetType: ec2.SubnetType.PUBLIC, - }, - }); - - // Parsing user data script and injecting necessary variables - const userData = fs.readFileSync(path.join(__dirname, "assets", "user-data", "node.sh")).toString(); - - const dataVolumeSizeBytes = dataVolume.sizeGiB * constants.GibibytesToBytesConversionCoefficient; - - - const modifiedUserData = cdk.Fn.sub(userData, { - _AWS_REGION_: REGION, - _ASSETS_S3_PATH_: `s3://${asset.s3BucketName}/${asset.s3ObjectKey}`, - _STACK_NAME_: STACK_NAME, - _STACK_ID_: STACK_ID, - _NODE_CF_LOGICAL_ID_: node.nodeCFLogicalId, - _DATA_VOLUME_TYPE_: dataVolume.type, - _DATA_VOLUME_SIZE_: dataVolumeSizeBytes.toString(), - _NODE_ROLE_: nodeRole, - _EDGE_NETWORK_: edgeNetwork, - _EDGE_NODE_GPU_: edgeNodeGpu, - _EDGE_LAUNCHER_VERSION_: edgeLauncherVersion, - _EDGE_NODE_PASSWORD_SSM_ARN_: sSMEdgeNodePasswordARN - }); - - // Adding modified userdata script to the instance prepared fro us by Single Node constract - node.instance.addUserData(modifiedUserData); - - // Adding CloudWatch dashboard to the node - const dashboardString = cdk.Fn.sub(JSON.stringify(nodeCwDashboard.SyncNodeCWDashboardJSON(instanceType.toString(),dataVolume.type)), { - INSTANCE_ID:node.instanceId, - INSTANCE_NAME: STACK_NAME, - REGION: REGION, - }) - - new cw.CfnDashboard(this, 'single-cw-dashboard', { - dashboardName: `${STACK_NAME}-${node.instanceId}`, - dashboardBody: dashboardString, - }); - - new cdk.CfnOutput(this, "single-instance-id", { - value: node.instanceId, - }); - - // Adding suppressions to the stack - nag.NagSuppressions.addResourceSuppressions( - this, - [ - { - id: "AwsSolutions-IAM5", - reason: "Need read and write access to the S3 bucket", - }, - ], - true - ); - } -} diff --git a/lib/theta/package.json b/lib/theta/package.json deleted file mode 100644 index 16380b8a..00000000 --- a/lib/theta/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "aws-blockchain-node-runners-theta", - "version": "0.1.0", - "scripts": { - "build": "tsc", - "watch": "tsc -w", - "test": "jest", - "cdk": "cdk", - "cdk_deploy_common": "cdk deploy edge-common", - "cdk_destroy_common": "cdk destroy edge-common" - } -} diff --git a/lib/theta/sample-configs/.env-sample-full b/lib/theta/sample-configs/.env-sample-full deleted file mode 100644 index 93bb2a57..00000000 --- a/lib/theta/sample-configs/.env-sample-full +++ /dev/null @@ -1,23 +0,0 @@ -############################################################# -# Example configuration for BSC nodes runner app on AWS # -############################################################# - -## Set the AWS account is and region for your environment ## -AWS_ACCOUNT_ID="xxxxxxxxxxx" -AWS_REGION="us-east-2" - -EDGE_LAUNCHER_VERSION="latest" # Specify version of the edgelauncher_mainnet Docker Container: https://hub.docker.com/r/thetalabsorg/edgelauncher_mainnet/tags - -## Common configuration parameters ## -EDGE_NODE_NETWORK="mainnet" # All options: "mainnet" - -## Instance Nodes -EDGE_NODE_INSTANCE_TYPE="g4dn.xlarge" # 1 GPU; 4 vCPUs; 16GB Ram; 1 x 125GB NVMe SSD; $0.526/hr. View all GPU instances: https://docs.aws.amazon.com/dlami/latest/devguide/gpu.html -EDGE_NODE_CPU_TYPE="x86_64" # All options: "x86_64". IMPORTANT: Make sure the CPU type matches the instance type used. Theta Edge Node does not support "ARM_64" -EDGE_NODE_GPU="enabled" # All options: "enabled" | "disabled". IMPORTANT: Only set to "enabled" when using an instance with a GPU - -# Data volume configuration -EDGE_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: "instance-store" NOT recommended as it is ephermal and will be reset after stopping the instance. Use "instance-store" option only with instance types that support that feature, like popular for node g4dn, d3, i3en, and i4i instance families -EDGE_DATA_VOL_SIZE="256" # Current required data size to keep both snapshot archive and unarchived version of it (not applicable for "instance-store") -EDGE_DATA_VOL_IOPS="3000" # Max IOPS for EBS volumes (not applicable for "instance-store") -EDGE_DATA_VOL_THROUGHPUT="125" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") diff --git a/lib/theta/test/.env-test b/lib/theta/test/.env-test deleted file mode 100644 index 33725a3d..00000000 --- a/lib/theta/test/.env-test +++ /dev/null @@ -1,23 +0,0 @@ -############################################################# -# Example configuration for BSC nodes runner app on AWS # -############################################################# - -## Set the AWS account is and region for your environment ## -AWS_ACCOUNT_ID="xxxxxxxxxxx" -AWS_REGION="us-east-2" - -EDGE_LAUNCHER_VERSION="latest" # Specify version of the edgelauncher_mainnet Docker Container: https://hub.docker.com/r/thetalabsorg/edgelauncher_mainnet/tags - -## Common configuration parameters ## -EDGE_NODE_NETWORK="mainnet" # All options: "mainnet" - -## Instance Nodes -EDGE_NODE_INSTANCE_TYPE="g4dn.2xlarge" # 1 GPU; 8 vCPUs; 32GB Ram; 1 x 225GB NVMe SSD; $0.526/hr. View all GPU instances: https://docs.aws.amazon.com/dlami/latest/devguide/gpu.html -EDGE_NODE_CPU_TYPE="x86_64" # All options: "x86_64". IMPORTANT: Make sure the CPU type matches the instance type used. Theta Edge Node does not support "ARM_64" -EDGE_NODE_GPU="enabled" # All options: "enabled" | "disabled". IMPORTANT: Only set to "enabled" when using an instance with a GPU - -# Data volume configuration -EDGE_DATA_VOL_TYPE="gp3" # Other options: "io1" | "io2" | "gp3" | "instance-store" . IMPORTANT: "instance-store" NOT recommended as it is ephermal and will be reset after stopping the instance. Use "instance-store" option only with instance types that support that feature, like popular for node g4dn, d3, i3en, and i4i instance families -EDGE_DATA_VOL_SIZE="256" # Current required data size to keep both snapshot archive and unarchived version of it (not applicable for "instance-store") -EDGE_DATA_VOL_IOPS="10000" # Max IOPS for EBS volumes (not applicable for "instance-store") -EDGE_DATA_VOL_THROUGHPUT="700" # Max throughput for EBS gp3 volumes (not applicable for "io1" | "io2" | "instance-store") diff --git a/lib/theta/test/common-stack.test.ts b/lib/theta/test/common-stack.test.ts deleted file mode 100644 index d20caf85..00000000 --- a/lib/theta/test/common-stack.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Match, Template } from "aws-cdk-lib/assertions"; -import * as cdk from "aws-cdk-lib"; -import * as dotenv from 'dotenv'; -dotenv.config({ path: './test/.env-test' }); -import * as config from "../lib/config/edgeConfig"; -import { EdgeCommonStack } from "../lib/common-stack"; - -describe("EdgeCommonStack", () => { - test("synthesizes the way we expect", () => { - const app = new cdk.App(); - - // Create the edgeCommonStack. - const edgeCommonStack = new EdgeCommonStack(app, "edge-common", { - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - stackName: `edge-nodes-common`, - }); - - // Prepare the stack for assertions. - const template = Template.fromStack(edgeCommonStack); - - // Has EC2 instance role. - template.hasResourceProperties("AWS::IAM::Role", { - AssumeRolePolicyDocument: { - Statement: [ - { - Action: "sts:AssumeRole", - Effect: "Allow", - Principal: { - Service: "ec2.amazonaws.com" - } - } - ] - }, - ManagedPolicyArns: [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - Ref: "AWS::Partition" - }, - ":iam::aws:policy/AmazonSSMManagedInstanceCore" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/CloudWatchAgentServerPolicy" - ] - ] - } - ] - }) - - template.hasResourceProperties("AWS::SecretsManager::Secret", - { - Name: "edgeNodePassword", - SecretString: { - Ref: "edgeNodePassword" - } - }) - - }); -}); diff --git a/lib/theta/test/single-node-stack.test.ts b/lib/theta/test/single-node-stack.test.ts deleted file mode 100644 index 78842f35..00000000 --- a/lib/theta/test/single-node-stack.test.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Match, Template } from "aws-cdk-lib/assertions"; -import * as cdk from "aws-cdk-lib"; -import * as dotenv from 'dotenv'; -dotenv.config({ path: './test/.env-test' }); -import * as config from "../lib/config/edgeConfig"; -import * as configTypes from "../lib/config/edgeConfig.interface"; -import { EdgeSingleNodeStack } from "../lib/single-node-stack"; - -describe("EdgeSingleNodeStack", () => { - test("synthesizes the way we expect", () => { - const app = new cdk.App(); - - // Create the EthSingleNodeStack. - const edgeSingleNodeStack = new EdgeSingleNodeStack(app, "edge-single-node", { - stackName: `edge-single-node`, - - env: { account: config.baseConfig.accountId, region: config.baseConfig.region }, - nodeRole: "single-node", - instanceType: config.baseNodeConfig.instanceType, - instanceCpuType: config.baseNodeConfig.instanceCpuType, - edgeNodeGpu: config.baseNodeConfig.edgeNodeGpu, - edgeNetwork: config.baseNodeConfig.edgeNetwork, - edgeLauncherVersion: config.baseNodeConfig.edgeLauncherVersion, - dataVolume: config.baseNodeConfig.dataVolume, - }); - - // Prepare the stack for assertions. - const template = Template.fromStack(edgeSingleNodeStack); - - // Has EC2 instance security group. - template.hasResourceProperties("AWS::EC2::SecurityGroup", { - GroupDescription: Match.anyValue(), - VpcId: Match.anyValue(), - SecurityGroupEgress: [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" - } - ], - SecurityGroupIngress: [ - - { - "CidrIp": "1.2.3.4/5", - "Description": "Theta Edge Node RPC Port", - "FromPort": 15888, - "IpProtocol": "tcp", - "ToPort": 15888 - }, - { - "CidrIp": "1.2.3.4/5", - "Description": "Theta Edge Core RPC Port", - "FromPort": 17888, - "IpProtocol": "tcp", - "ToPort": 17888 - }, - { - "CidrIp": "1.2.3.4/5", - "Description": "Theta Edge Encoder RPC Port", - "FromPort": 17935, - "IpProtocol": "tcp", - "ToPort": 17935 - } - ] - }) - - // Has EC2 instance with node configuration - template.hasResourceProperties("AWS::EC2::Instance", { - AvailabilityZone: Match.anyValue(), - UserData: Match.anyValue(), - BlockDeviceMappings: [ - { - DeviceName: "/dev/xvda", - Ebs: { - DeleteOnTermination: true, - Encrypted: true, - Iops: 3000, - VolumeSize: 46, - VolumeType: "gp3" - } - } - ], - IamInstanceProfile: Match.anyValue(), - ImageId: Match.anyValue(), - InstanceType: "g4dn.2xlarge", - Monitoring: true, - PropagateTagsToVolumeOnCreation: true, - SecurityGroupIds: Match.anyValue(), - SubnetId: Match.anyValue(), - }) - - // Has EBS data volume. - template.hasResourceProperties("AWS::EC2::Volume", { - AvailabilityZone: Match.anyValue(), - Encrypted: true, - Iops: 10000, - MultiAttachEnabled: false, - Size: 256, - Throughput: 700, - VolumeType: "gp3" - }) - - // Has EBS data volume attachment. - template.hasResourceProperties("AWS::EC2::VolumeAttachment", { - Device: "/dev/sdf", - InstanceId: Match.anyValue(), - VolumeId: Match.anyValue(), - }) - - // Has CloudWatch dashboard. - template.hasResourceProperties("AWS::CloudWatch::Dashboard", { - DashboardBody: Match.anyValue(), - DashboardName: { - "Fn::Join": ["", ["edge-single-node-",{ "Ref": Match.anyValue() }]] - } - }) - - }); -}); diff --git a/lib/theta/tsconfig.json b/lib/theta/tsconfig.json deleted file mode 100644 index 8e1979f3..00000000 --- a/lib/theta/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": [ - "es2020", - "dom" - ], - "declaration": true, - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": false, - "noUnusedParameters": false, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": false, - "inlineSourceMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictPropertyInitialization": false, - "typeRoots": [ - "../../node_modules/@types" - ] - }, - "exclude": [ - "node_modules", - "cdk.out" - ] -} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..d4a1045c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4296 @@ +{ + "name": "aws-blockchain-node-runners", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "aws-blockchain-node-runners", + "version": "0.1.0", + "dependencies": { + "aws-cdk-lib": "^2.84.0", + "constructs": "^10.2.55", + "dotenv": "^16.3.1", + "source-map-support": "^0.5.21" + }, + "devDependencies": { + "@types/jest": "^29.5.11", + "@types/node": "^20.10.0", + "aws-cdk": "^2.84.0", + "cdk-nag": "^2.27.43", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "~5.1.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@aws-cdk/asset-awscli-v1": { + "version": "2.2.200", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.200.tgz", + "integrity": "sha512-Kf5J8DfJK4wZFWT2Myca0lhwke7LwHcHBo+4TvWOGJrFVVKVuuiLCkzPPRBQQVDj0Vtn2NBokZAz8pfMpAqAKg==" + }, + "node_modules/@aws-cdk/asset-kubectl-v20": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", + "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==" + }, + "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.1.tgz", + "integrity": "sha512-DDt4SLdLOwWCjGtltH4VCST7hpOI5DzieuhGZsBpZ+AgJdSI2GCjklCXm0GCTwJG/SolkL5dtQXyUKgg9luBDg==" + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.17.tgz", + "integrity": "sha512-2EENLmhpwplDux5PSsZnSbnSkB3tZ6QTksgO25xwEL7pIDcNOMhF5v/s6RzwjMZzZzw9Ofc30gHv5ChCC8pifQ==", + "dev": true, + "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.17", + "@babel/helpers": "^7.22.15", + "@babel/parser": "^7.22.16", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.17", + "@babel/types": "^7.22.17", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dev": true, + "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" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.17.tgz", + "integrity": "sha512-XouDDhQESrLHTpnBtCKExJdyY4gJCdrvH2Pyv8r8kovX2U8G0dRUOT45T9XlbLtuu9CLXP15eusnkprhoPV5iQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@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.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", + "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", + "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", + "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", + "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.6.4", + "@jest/reporters": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.6.3", + "jest-config": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-resolve-dependencies": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "jest-watcher": "^29.6.4", + "micromatch": "^4.0.4", + "pretty-format": "^29.6.3", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", + "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", + "dev": true, + "dependencies": { + "expect": "^29.6.4", + "jest-snapshot": "^29.6.4" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", + "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", + "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", + "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/types": "^29.6.3", + "jest-mock": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", + "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@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.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.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-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", + "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", + "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.6.4", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.4", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", + "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", + "dev": true, + "dependencies": { + "@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": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.4", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", + "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aws-cdk": { + "version": "2.95.1", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.95.1.tgz", + "integrity": "sha512-KUJ63n2cB6qxpsHARmMWDhu8VITA7rKvYybbfS7BaBpXl4Tb9Bt/mEAY1EeVeyO/mpInvSRpMpdjyqE0kNAKtA==", + "dev": true, + "bin": { + "cdk": "bin/cdk" + }, + "engines": { + "node": ">= 14.15.0" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/aws-cdk-lib": { + "version": "2.95.1", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.95.1.tgz", + "integrity": "sha512-FQlnW3+c1j2W7hmu+QMSiWnBgbW1Lhn1ZpBQ6cwYZa97rII1zlEyTowAfzQk6szPIzUhJv5xK03nWZtvCvpAWw==", + "bundleDependencies": [ + "@balena/dockerignore", + "case", + "fs-extra", + "ignore", + "jsonschema", + "minimatch", + "punycode", + "semver", + "table", + "yaml" + ], + "dependencies": { + "@aws-cdk/asset-awscli-v1": "^2.2.200", + "@aws-cdk/asset-kubectl-v20": "^2.1.2", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.1", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^11.1.1", + "ignore": "^5.2.4", + "jsonschema": "^1.4.1", + "minimatch": "^3.1.2", + "punycode": "^2.3.0", + "semver": "^7.5.4", + "table": "^6.8.1", + "yaml": "1.10.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "constructs": "^10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { + "version": "1.0.2", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/aws-cdk-lib/node_modules/ajv": { + "version": "8.12.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/astral-regex": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/brace-expansion": { + "version": "1.1.11", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/aws-cdk-lib/node_modules/case": { + "version": "1.6.3", + "inBundle": true, + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/concat-map": { + "version": "0.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { + "version": "3.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fs-extra": { + "version": "11.1.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/aws-cdk-lib/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/ignore": { + "version": "5.2.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/jsonfile": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/jsonschema": { + "version": "1.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { + "version": "4.4.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/lru-cache": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/minimatch": { + "version": "3.1.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/punycode": { + "version": "2.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/aws-cdk-lib/node_modules/require-from-string": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/semver": { + "version": "7.5.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/slice-ansi": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/table": { + "version": "6.8.1", + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/universalify": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/uri-js": { + "version": "4.4.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/yaml": { + "version": "1.10.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/babel-jest": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", + "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.6.4", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001532", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001532.tgz", + "integrity": "sha512-FbDFnNat3nMnrROzqrsg314zhqN5LGQ1kyyMk2opcrwGbVGpHRhgCWtAgD5YJUqNAiQ+dklreil/c3Qf1dfCTw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/cdk-nag": { + "version": "2.27.127", + "resolved": "https://registry.npmjs.org/cdk-nag/-/cdk-nag-2.27.127.tgz", + "integrity": "sha512-IIMB5ycAqiYhrCuQaB/ZEi61WQmLuNthy/alWnzm2BbMjYmnXBTiTtu31P3OUcfhLJmtDvZeJg7RNIV9m/sUAg==", + "dev": true, + "peerDependencies": { + "aws-cdk-lib": "^2.78.0", + "constructs": "^10.0.5" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/constructs": { + "version": "10.2.70", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.2.70.tgz", + "integrity": "sha512-z6zr1E8K/9tzJbCQzY0UGX0/oVKPFKu9C/mzEnghCG6TAJINnvlq0CMKm63XqqeMleadZYm5T3sZGJKcxJS/Pg==", + "engines": { + "node": ">= 16.14.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.513", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.513.tgz", + "integrity": "sha512-cOB0xcInjm+E5qIssHeXJ29BaUyWpMyFKT5RB3bsLENDheCja0wMkHJyiPl0NBE/VzDI7JDuNEQWhe6RitEUcw==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", + "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "dev": true, + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", + "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.6.4", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.6.4" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", + "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.6.3", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", + "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", + "p-limit": "^3.1.0", + "pretty-format": "^29.6.3", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", + "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", + "dev": true, + "dependencies": { + "@jest/core": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", + "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.6.4", + "@jest/types": "^29.6.3", + "babel-jest": "^29.6.4", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.6.4", + "jest-environment-node": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.6.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", + "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", + "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", + "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.6.3", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", + "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", + "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", + "dev": true, + "dependencies": { + "@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": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", + "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", + "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", + "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@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": "^29.6.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", + "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", + "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", + "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.6.4" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", + "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", + "dev": true, + "dependencies": { + "@jest/console": "^29.6.4", + "@jest/environment": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.6.3", + "jest-environment-node": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-leak-detector": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-util": "^29.6.3", + "jest-watcher": "^29.6.4", + "jest-worker": "^29.6.4", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", + "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/globals": "^29.6.4", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", + "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", + "dev": true, + "dependencies": { + "@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/types": "^7.3.3", + "@jest/expect-utils": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.6.4", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "natural-compare": "^1.4.0", + "pretty-format": "^29.6.3", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/jest-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", + "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", + "dev": true, + "dependencies": { + "@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" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", + "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", + "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.6.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", + "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.6.3", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", + "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz", + "integrity": "sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", + "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index ba1a5cc8..c98c4710 100644 --- a/package.json +++ b/package.json @@ -13,18 +13,18 @@ }, "devDependencies": { "@types/jest": "^29.5.11", - "@types/node": "^20.14.2", - "aws-cdk": "^2.143.0", - "cdk-nag": "^2.28.135", - "jest": "^29.7.0", - "ts-jest": "^29.1.4", - "ts-node": "^10.9.2", - "typescript": "~5.4.5" + "@types/node": "^20.10.0", + "aws-cdk": "^2.84.0", + "cdk-nag": "^2.27.43", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "~5.1.3" }, "dependencies": { - "aws-cdk-lib": "^2.143.0", - "constructs": "^10.3.0", - "dotenv": "^16.4.5", + "aws-cdk-lib": "^2.84.0", + "constructs": "^10.2.55", + "dotenv": "^16.3.1", "source-map-support": "^0.5.21" } } diff --git a/website/docs/Blueprints/Bsc.md b/website/docs/Blueprints/Bsc.md index 56be4470..857ab1d4 100644 --- a/website/docs/Blueprints/Bsc.md +++ b/website/docs/Blueprints/Bsc.md @@ -1,4 +1,5 @@ --- +sidebar_position: 5 sidebar_label: BSC --- # diff --git a/website/docs/Blueprints/Ethereum.md b/website/docs/Blueprints/Ethereum.md index 4bbaa8fc..b944033b 100644 --- a/website/docs/Blueprints/Ethereum.md +++ b/website/docs/Blueprints/Ethereum.md @@ -1,4 +1,5 @@ --- +sidebar_position: 2 sidebar_label: Ethereum --- # diff --git a/website/docs/Blueprints/Fantom.md b/website/docs/Blueprints/Fantom.md deleted file mode 100644 index 3421e383..00000000 --- a/website/docs/Blueprints/Fantom.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -sidebar_label: Fantom ---- -# - -import Readme from '../../../lib/fantom/README.md'; - - diff --git a/website/docs/Blueprints/Scroll.md b/website/docs/Blueprints/Scroll.md index b1a3c5d3..246a5305 100644 --- a/website/docs/Blueprints/Scroll.md +++ b/website/docs/Blueprints/Scroll.md @@ -1,4 +1,5 @@ --- +sidebar_position: 4 sidebar_label: Scroll --- # diff --git a/website/docs/Blueprints/Solana.md b/website/docs/Blueprints/Solana.md index 0f3874e1..24f958a9 100644 --- a/website/docs/Blueprints/Solana.md +++ b/website/docs/Blueprints/Solana.md @@ -1,4 +1,5 @@ --- +sidebar_position: 3 sidebar_label: Solana --- # diff --git a/website/docs/Blueprints/Stacks.md b/website/docs/Blueprints/Stacks.md index 56ca8849..7d2f44b3 100644 --- a/website/docs/Blueprints/Stacks.md +++ b/website/docs/Blueprints/Stacks.md @@ -1,4 +1,5 @@ --- +sidebar_position: 4 sidebar_label: Stacks --- # diff --git a/website/docs/Blueprints/Starknet.md b/website/docs/Blueprints/Starknet.md deleted file mode 100644 index 3e6407ba..00000000 --- a/website/docs/Blueprints/Starknet.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -sidebar_label: Starknet ---- -# - -import Readme from '../../../lib/starknet/README.md'; - - diff --git a/website/docs/Blueprints/Sui.md b/website/docs/Blueprints/Sui.md deleted file mode 100644 index 90385819..00000000 --- a/website/docs/Blueprints/Sui.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -sidebar_label: Sui ---- -# - -import Readme from '../../../lib/sui/README.md'; - - diff --git a/website/docs/Blueprints/Tezos.md b/website/docs/Blueprints/Tezos.md deleted file mode 100644 index cf55f34b..00000000 --- a/website/docs/Blueprints/Tezos.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -sidebar_label: Tezos ---- -# - -import Readme from '../../../lib/tezos/README.md'; - - diff --git a/website/docs/Blueprints/Theta.md b/website/docs/Blueprints/Theta.md deleted file mode 100644 index dc3fdd5d..00000000 --- a/website/docs/Blueprints/Theta.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -sidebar_label: Theta ---- -# - -import Readme from '../../../lib/theta/README.md'; - - diff --git a/website/docs/Blueprints/Wax.md b/website/docs/Blueprints/Wax.md index 493b658f..79aa8892 100644 --- a/website/docs/Blueprints/Wax.md +++ b/website/docs/Blueprints/Wax.md @@ -1,4 +1,5 @@ --- +sidebar_position: 6 sidebar_label: WAX --- # diff --git a/website/package-lock.json b/website/package-lock.json index 7bc3b654..12493543 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -8,10 +8,10 @@ "name": "aws-blockchain-node-runners", "version": "1.1.0", "dependencies": { - "@docusaurus/core": "^3.4.0", - "@docusaurus/plugin-google-gtag": "^3.4.0", - "@docusaurus/plugin-google-tag-manager": "^3.4.0", - "@docusaurus/preset-classic": "^3.4.0", + "@docusaurus/core": "^3.2.1", + "@docusaurus/plugin-google-gtag": "^3.2.1", + "@docusaurus/plugin-google-tag-manager": "^3.2.1", + "@docusaurus/preset-classic": "^3.2.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "prism-react-renderer": "^2.3.0", @@ -20,8 +20,8 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/types": "3.4.0" + "@docusaurus/module-type-aliases": "3.2.1", + "@docusaurus/types": "3.2.1" }, "engines": { "node": ">=18.0" @@ -69,74 +69,74 @@ } }, "node_modules/@algolia/cache-browser-local-storage": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.24.0.tgz", - "integrity": "sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz", + "integrity": "sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==", "dependencies": { - "@algolia/cache-common": "4.24.0" + "@algolia/cache-common": "4.23.3" } }, "node_modules/@algolia/cache-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.24.0.tgz", - "integrity": "sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g==" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.3.tgz", + "integrity": "sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==" }, "node_modules/@algolia/cache-in-memory": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.24.0.tgz", - "integrity": "sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz", + "integrity": "sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==", "dependencies": { - "@algolia/cache-common": "4.24.0" + "@algolia/cache-common": "4.23.3" } }, "node_modules/@algolia/client-account": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.24.0.tgz", - "integrity": "sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.3.tgz", + "integrity": "sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==", "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/client-search": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/@algolia/client-analytics": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.24.0.tgz", - "integrity": "sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.3.tgz", + "integrity": "sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==", "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/client-search": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/@algolia/client-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", - "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.3.tgz", + "integrity": "sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==", "dependencies": { - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/@algolia/client-personalization": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.24.0.tgz", - "integrity": "sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.3.tgz", + "integrity": "sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==", "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/@algolia/client-search": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", - "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz", + "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/@algolia/events": { @@ -145,65 +145,65 @@ "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" }, "node_modules/@algolia/logger-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.24.0.tgz", - "integrity": "sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.3.tgz", + "integrity": "sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==" }, "node_modules/@algolia/logger-console": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.24.0.tgz", - "integrity": "sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.3.tgz", + "integrity": "sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==", "dependencies": { - "@algolia/logger-common": "4.24.0" + "@algolia/logger-common": "4.23.3" } }, "node_modules/@algolia/recommend": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.24.0.tgz", - "integrity": "sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw==", - "dependencies": { - "@algolia/cache-browser-local-storage": "4.24.0", - "@algolia/cache-common": "4.24.0", - "@algolia/cache-in-memory": "4.24.0", - "@algolia/client-common": "4.24.0", - "@algolia/client-search": "4.24.0", - "@algolia/logger-common": "4.24.0", - "@algolia/logger-console": "4.24.0", - "@algolia/requester-browser-xhr": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/requester-node-http": "4.24.0", - "@algolia/transporter": "4.24.0" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.3.tgz", + "integrity": "sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz", - "integrity": "sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz", + "integrity": "sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==", "dependencies": { - "@algolia/requester-common": "4.24.0" + "@algolia/requester-common": "4.23.3" } }, "node_modules/@algolia/requester-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.24.0.tgz", - "integrity": "sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.3.tgz", + "integrity": "sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==" }, "node_modules/@algolia/requester-node-http": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", - "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz", + "integrity": "sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==", "dependencies": { - "@algolia/requester-common": "4.24.0" + "@algolia/requester-common": "4.23.3" } }, "node_modules/@algolia/transporter": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.24.0.tgz", - "integrity": "sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.3.tgz", + "integrity": "sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==", "dependencies": { - "@algolia/cache-common": "4.24.0", - "@algolia/logger-common": "4.24.0", - "@algolia/requester-common": "4.24.0" + "@algolia/cache-common": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/requester-common": "4.23.3" } }, "node_modules/@ampproject/remapping": { @@ -219,11 +219,11 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/highlight": "^7.24.2", "picocolors": "^1.0.0" }, "engines": { @@ -231,28 +231,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", + "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.4", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.4", + "@babel/parser": "^7.24.4", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -276,11 +276,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", + "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.24.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -290,35 +290,34 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", - "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -336,18 +335,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz", - "integrity": "sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.4.tgz", + "integrity": "sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.24.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" }, "engines": { @@ -366,11 +365,11 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", "semver": "^6.3.1" }, @@ -390,9 +389,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.1.tgz", + "integrity": "sha512-o7SDgTJuvx5vLKD6SFvkydkSMBvahDKGiNJzG22IZYXhiqoe9efY7zocICBgzHV4IRg5wdgl2nEL/tulKIEIbA==", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -405,73 +404,68 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@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" }, "engines": { "node": ">=6.9.0" @@ -481,32 +475,32 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", - "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -516,13 +510,13 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz", + "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -532,96 +526,94 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", - "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", + "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -695,9 +687,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -706,12 +698,12 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.4.tgz", + "integrity": "sha512-qpl6vOOEEzTLLcsuqYYo8yDtrTocmu2xkGvgNebvPjT9DTtfFYGmgDqY+rBYXNlqL4s9qLDn6xkrJv4RxAPiTA==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -721,11 +713,11 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz", + "integrity": "sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -735,13 +727,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", - "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz", + "integrity": "sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -751,12 +743,12 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz", + "integrity": "sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -835,11 +827,11 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz", + "integrity": "sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -849,11 +841,11 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz", + "integrity": "sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -885,11 +877,11 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", + "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -993,11 +985,11 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", - "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1022,11 +1014,11 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", - "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz", + "integrity": "sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1036,13 +1028,13 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz", + "integrity": "sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-remap-async-to-generator": "^7.22.20", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { @@ -1053,13 +1045,13 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz", + "integrity": "sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==", "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" + "@babel/helper-module-imports": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-remap-async-to-generator": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -1069,11 +1061,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", - "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz", + "integrity": "sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1083,11 +1075,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.4.tgz", + "integrity": "sha512-nIFUZIpGKDf9O9ttyRXpHFpKC+X3Y5mtshZONuEUYBomAKoM4y029Jr+uB1bHGPhNmK8YXHevDtKDOLmtRrp6g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1097,12 +1089,12 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz", + "integrity": "sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1112,12 +1104,12 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", - "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz", + "integrity": "sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-create-class-features-plugin": "^7.24.4", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -1128,17 +1120,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", - "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz", + "integrity": "sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1", + "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" }, "engines": { @@ -1149,12 +1141,12 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", - "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz", + "integrity": "sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/template": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/template": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1164,11 +1156,11 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", - "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz", + "integrity": "sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1178,12 +1170,12 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", - "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz", + "integrity": "sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1193,11 +1185,11 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", - "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz", + "integrity": "sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1207,11 +1199,11 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", - "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz", + "integrity": "sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { @@ -1222,12 +1214,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", - "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz", + "integrity": "sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw==", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1237,11 +1229,11 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", - "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz", + "integrity": "sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { @@ -1252,12 +1244,12 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", - "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz", + "integrity": "sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1267,13 +1259,13 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz", + "integrity": "sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1283,11 +1275,11 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", - "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz", + "integrity": "sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { @@ -1298,11 +1290,11 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz", + "integrity": "sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1312,11 +1304,11 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", - "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz", + "integrity": "sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -1327,11 +1319,11 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", - "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz", + "integrity": "sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1341,12 +1333,12 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", - "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz", + "integrity": "sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ==", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1356,13 +1348,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", - "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz", + "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-simple-access": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1372,14 +1364,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz", + "integrity": "sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA==", "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -1389,12 +1381,12 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", - "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz", + "integrity": "sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg==", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1404,12 +1396,12 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", - "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1419,11 +1411,11 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", - "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz", + "integrity": "sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1433,11 +1425,11 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", - "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz", + "integrity": "sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { @@ -1448,11 +1440,11 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", - "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz", + "integrity": "sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { @@ -1463,14 +1455,14 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", - "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.1.tgz", + "integrity": "sha512-XjD5f0YqOtebto4HGISLNfiNMTTs6tbkFf2TOqJlYKYmbo+mN9Dnpl4SRoofiziuOWMIyq3sZEUqLo3hLITFEA==", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.7" + "@babel/plugin-transform-parameters": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -1480,12 +1472,12 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", - "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz", + "integrity": "sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -1495,11 +1487,11 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", - "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz", + "integrity": "sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { @@ -1510,12 +1502,12 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz", + "integrity": "sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { @@ -1526,11 +1518,11 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", - "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz", + "integrity": "sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1540,12 +1532,12 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz", + "integrity": "sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1555,13 +1547,13 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", - "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.1.tgz", + "integrity": "sha512-pTHxDVa0BpUbvAgX3Gat+7cSciXqUcY9j2VZKTbSB6+VQGpNgNO9ailxTGHSXlqOnX1Hcx1Enme2+yv7VqP9bg==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -1572,11 +1564,11 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", - "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz", + "integrity": "sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1586,11 +1578,11 @@ } }, "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.7.tgz", - "integrity": "sha512-7LidzZfUXyfZ8/buRW6qIIHBY8wAZ1OrY9c/wTr8YhZ6vMPo+Uc/CVFLYY1spZrEQlD4w5u8wjqk5NQ3OVqQKA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.1.tgz", + "integrity": "sha512-QXp1U9x0R7tkiGB0FOk8o74jhnap0FlZ5gNkRIWdG3eP+SvMFg118e1zaWewDzgABb106QSKpVsD3Wgd8t6ifA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1600,11 +1592,11 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz", - "integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz", + "integrity": "sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1614,15 +1606,15 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz", - "integrity": "sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" }, "engines": { "node": ">=6.9.0" @@ -1632,11 +1624,11 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz", - "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.24.7" + "@babel/plugin-transform-react-jsx": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1646,12 +1638,12 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz", - "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.1.tgz", + "integrity": "sha512-+pWEAaDJvSm9aFvJNpLiM2+ktl2Sn2U5DdyiWdZBxmLc6+xGt88dvFqsHiAiDS+8WqUwbDfkKz9jRxK3M0k+kA==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1661,11 +1653,11 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", - "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz", + "integrity": "sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.0", "regenerator-transform": "^0.15.2" }, "engines": { @@ -1676,11 +1668,11 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", - "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz", + "integrity": "sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1690,12 +1682,12 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", - "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.3.tgz", + "integrity": "sha512-J0BuRPNlNqlMTRJ72eVptpt9VcInbxO6iP3jaxr+1NPhC0UkKL+6oeX6VXMEYdADnuqmMmsBspt4d5w8Y/TCbQ==", "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-plugin-utils": "^7.24.0", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.1", "babel-plugin-polyfill-regenerator": "^0.6.1", @@ -1717,11 +1709,11 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", - "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz", + "integrity": "sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1731,12 +1723,12 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", - "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz", + "integrity": "sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1746,11 +1738,11 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", - "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz", + "integrity": "sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1760,11 +1752,11 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", - "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz", + "integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1774,11 +1766,11 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz", + "integrity": "sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1788,14 +1780,14 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.7.tgz", - "integrity": "sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.4.tgz", + "integrity": "sha512-79t3CQ8+oBGk/80SQ8MN3Bs3obf83zJ0YZjDmDaEZN8MqhMI760apl5z6a20kFeMXBwJX99VpKT8CKxEBp5H1g==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-typescript": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.4", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-typescript": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -1805,11 +1797,11 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", - "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz", + "integrity": "sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1819,12 +1811,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", - "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz", + "integrity": "sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1834,12 +1826,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", - "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz", + "integrity": "sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1849,12 +1841,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz", + "integrity": "sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1864,26 +1856,26 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", - "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.4.tgz", + "integrity": "sha512-7Kl6cSmYkak0FK/FXjSEnLJ1N9T/WA2RkMhu17gZ/dsxKJUuTYNIylahPTzqpLyJN4WhDif8X0XK1R8Wsguo/A==", + "dependencies": { + "@babel/compat-data": "^7.24.4", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.4", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-assertions": "^7.24.1", + "@babel/plugin-syntax-import-attributes": "^7.24.1", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -1895,54 +1887,54 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", - "@babel/plugin-transform-class-properties": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/plugin-transform-arrow-functions": "^7.24.1", + "@babel/plugin-transform-async-generator-functions": "^7.24.3", + "@babel/plugin-transform-async-to-generator": "^7.24.1", + "@babel/plugin-transform-block-scoped-functions": "^7.24.1", + "@babel/plugin-transform-block-scoping": "^7.24.4", + "@babel/plugin-transform-class-properties": "^7.24.1", + "@babel/plugin-transform-class-static-block": "^7.24.4", + "@babel/plugin-transform-classes": "^7.24.1", + "@babel/plugin-transform-computed-properties": "^7.24.1", + "@babel/plugin-transform-destructuring": "^7.24.1", + "@babel/plugin-transform-dotall-regex": "^7.24.1", + "@babel/plugin-transform-duplicate-keys": "^7.24.1", + "@babel/plugin-transform-dynamic-import": "^7.24.1", + "@babel/plugin-transform-exponentiation-operator": "^7.24.1", + "@babel/plugin-transform-export-namespace-from": "^7.24.1", + "@babel/plugin-transform-for-of": "^7.24.1", + "@babel/plugin-transform-function-name": "^7.24.1", + "@babel/plugin-transform-json-strings": "^7.24.1", + "@babel/plugin-transform-literals": "^7.24.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", + "@babel/plugin-transform-member-expression-literals": "^7.24.1", + "@babel/plugin-transform-modules-amd": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-modules-systemjs": "^7.24.1", + "@babel/plugin-transform-modules-umd": "^7.24.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.24.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", + "@babel/plugin-transform-numeric-separator": "^7.24.1", + "@babel/plugin-transform-object-rest-spread": "^7.24.1", + "@babel/plugin-transform-object-super": "^7.24.1", + "@babel/plugin-transform-optional-catch-binding": "^7.24.1", + "@babel/plugin-transform-optional-chaining": "^7.24.1", + "@babel/plugin-transform-parameters": "^7.24.1", + "@babel/plugin-transform-private-methods": "^7.24.1", + "@babel/plugin-transform-private-property-in-object": "^7.24.1", + "@babel/plugin-transform-property-literals": "^7.24.1", + "@babel/plugin-transform-regenerator": "^7.24.1", + "@babel/plugin-transform-reserved-words": "^7.24.1", + "@babel/plugin-transform-shorthand-properties": "^7.24.1", + "@babel/plugin-transform-spread": "^7.24.1", + "@babel/plugin-transform-sticky-regex": "^7.24.1", + "@babel/plugin-transform-template-literals": "^7.24.1", + "@babel/plugin-transform-typeof-symbol": "^7.24.1", + "@babel/plugin-transform-unicode-escapes": "^7.24.1", + "@babel/plugin-transform-unicode-property-regex": "^7.24.1", + "@babel/plugin-transform-unicode-regex": "^7.24.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.4", @@ -1979,16 +1971,16 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz", - "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.1.tgz", + "integrity": "sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.24.7", - "@babel/plugin-transform-react-jsx-development": "^7.24.7", - "@babel/plugin-transform-react-pure-annotations": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-transform-react-display-name": "^7.24.1", + "@babel/plugin-transform-react-jsx": "^7.23.4", + "@babel/plugin-transform-react-jsx-development": "^7.22.5", + "@babel/plugin-transform-react-pure-annotations": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -1998,15 +1990,15 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz", - "integrity": "sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.1.tgz", + "integrity": "sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-typescript": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-syntax-jsx": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-typescript": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -2021,9 +2013,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", + "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2032,9 +2024,9 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.7.tgz", - "integrity": "sha512-eytSX6JLBY6PVAeQa2bFlDx/7Mmln/gaEpsit5a3WEvjGfiIytEsgAwuIXCPM0xvw0v0cJn3ilq0/TvXrW0kgA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.4.tgz", + "integrity": "sha512-VOQOexSilscN24VEY810G/PqtpFvx/z6UqDIjIWbDe2368HhDLkYN5TYwaEz/+eRCUkhJ2WaNLLmQAlxzfWj4w==", "dependencies": { "core-js-pure": "^3.30.2", "regenerator-runtime": "^0.14.0" @@ -2044,31 +2036,31 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", + "dependencies": { + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2077,12 +2069,12 @@ } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dependencies": { - "@babel/helper-string-parser": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2143,9 +2135,9 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.4.0.tgz", - "integrity": "sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.2.1.tgz", + "integrity": "sha512-ZeMAqNvy0eBv2dThEeMuNzzuu+4thqMQakhxsgT5s02A8LqRcdkg+rbcnuNqUIpekQ4GRx3+M5nj0ODJhBXo9w==", "dependencies": { "@babel/core": "^7.23.3", "@babel/generator": "^7.23.3", @@ -2157,12 +2149,14 @@ "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/cssnano-preset": "3.2.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/mdx-loader": "3.2.1", + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", + "@svgr/webpack": "^6.5.1", "autoprefixer": "^10.4.14", "babel-loader": "^9.1.3", "babel-plugin-dynamic-import-node": "^2.3.3", @@ -2176,8 +2170,8 @@ "copy-webpack-plugin": "^11.0.0", "core-js": "^3.31.1", "css-loader": "^6.8.1", - "css-minimizer-webpack-plugin": "^5.0.1", - "cssnano": "^6.1.2", + "css-minimizer-webpack-plugin": "^4.2.2", + "cssnano": "^5.1.15", "del": "^6.1.1", "detect-port": "^1.5.1", "escape-html": "^1.0.3", @@ -2197,7 +2191,7 @@ "prompts": "^2.4.2", "react-dev-utils": "^12.0.1", "react-helmet-async": "^1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2", "react-loadable-ssr-addon-v5-slorber": "^1.0.1", "react-router": "^5.3.4", "react-router-config": "^5.1.1", @@ -2228,13 +2222,13 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.4.0.tgz", - "integrity": "sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.2.1.tgz", + "integrity": "sha512-wTL9KuSSbMJjKrfu385HZEzAoamUsbKqwscAQByZw4k6Ja/RWpqgVvt/CbAC+aYEH6inLzOt+MjuRwMOrD3VBA==", "dependencies": { - "cssnano-preset-advanced": "^6.1.2", - "postcss": "^8.4.38", - "postcss-sort-media-queries": "^5.2.0", + "cssnano-preset-advanced": "^5.3.10", + "postcss": "^8.4.26", + "postcss-sort-media-queries": "^4.4.1", "tslib": "^2.6.0" }, "engines": { @@ -2242,9 +2236,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.4.0.tgz", - "integrity": "sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.2.1.tgz", + "integrity": "sha512-0voOKJCn9RaM3np6soqEfo7SsVvf2C+CDTWhW+H/1AyBhybASpExtDEz+7ECck9TwPzFQ5tt+I3zVugUJbJWDg==", "dependencies": { "chalk": "^4.1.2", "tslib": "^2.6.0" @@ -2254,13 +2248,13 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.4.0.tgz", - "integrity": "sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.2.1.tgz", + "integrity": "sha512-Fs8tXhXKZjNkdGaOy1xSLXSwfjCMT73J3Zfrju2U16uGedRFRjgK0ojpK5tiC7TnunsL3tOFgp1BSMBRflX9gw==", "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/logger": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -2292,17 +2286,18 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", - "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.2.1.tgz", + "integrity": "sha512-FyViV5TqhL1vsM7eh29nJ5NtbRE6Ra6LP1PDcPvhwPSlA7eiWGRKAn3jWwMUcmjkos5SYY+sr0/feCdbM3eQHQ==", "dependencies": { - "@docusaurus/types": "3.4.0", + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/types": "3.2.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", "@types/react-router-dom": "*", "react-helmet-async": "*", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2" }, "peerDependencies": { "react": "*", @@ -2310,17 +2305,17 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.4.0.tgz", - "integrity": "sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.2.1.tgz", + "integrity": "sha512-lOx0JfhlGZoZu6pEJfeEpSISZR5dQbJGGvb42IP13G5YThNHhG9R9uoWuo4IOimPqBC7sHThdLA3VLevk61Fsw==", + "dependencies": { + "@docusaurus/core": "3.2.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/mdx-loader": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "cheerio": "^1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", @@ -2341,18 +2336,18 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.4.0.tgz", - "integrity": "sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.2.1.tgz", + "integrity": "sha512-GHe5b/lCskAR8QVbfWAfPAApvRZgqk7FN3sOHgjCtjzQACZxkHmq6QqyqZ8Jp45V7lVck4wt2Xw2IzBJ7Cz3bA==", + "dependencies": { + "@docusaurus/core": "3.2.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/mdx-loader": "3.2.1", + "@docusaurus/module-type-aliases": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -2371,15 +2366,15 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.4.0.tgz", - "integrity": "sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.2.1.tgz", + "integrity": "sha512-TOqVfMVTAHqWNEGM94Drz+PUpHDbwFy6ucHFgyTx9zJY7wPNSG5EN+rd/mU7OvAi26qpOn2o9xTdUmb28QLjEQ==", + "dependencies": { + "@docusaurus/core": "3.2.1", + "@docusaurus/mdx-loader": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -2393,13 +2388,13 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.4.0.tgz", - "integrity": "sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.2.1.tgz", + "integrity": "sha512-AMKq8NuUKf2sRpN1m/sIbqbRbnmk+rSA+8mNU1LNxEl9BW9F/Gng8m9HKlzeyMPrf5XidzS1jqkuTLDJ6KIrFw==", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", + "@docusaurus/core": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils": "3.2.1", "fs-extra": "^11.1.1", "react-json-view-lite": "^1.2.0", "tslib": "^2.6.0" @@ -2413,13 +2408,13 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.4.0.tgz", - "integrity": "sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.2.1.tgz", + "integrity": "sha512-/rJ+9u+Px0eTCiF4TNcNtj3kHf8cp6K1HCwOTdbsSlz6Xn21syZYcy+f1VM9wF6HrvUkXUcbM5TDCvg2IRL6bQ==", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "tslib": "^2.6.0" }, "engines": { @@ -2431,13 +2426,13 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.4.0.tgz", - "integrity": "sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.2.1.tgz", + "integrity": "sha512-XtuJnlMvYfppeVdUyKiDIJAa/gTJKCQU92z8CLZZ9ibJdgVjFOLS10s0hIC0eL5z0U2u2loJz2rZ63HOkNHbBA==", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -2450,13 +2445,13 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.4.0.tgz", - "integrity": "sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.2.1.tgz", + "integrity": "sha512-wiS/kE0Ny5pnjTxVCs8ljRnkL1RVMj59t6jmSsgEX7piDOoaXSMIUaoIt9ogS/v132uO0xEsxHstkRUZHQyPcQ==", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "tslib": "^2.6.0" }, "engines": { @@ -2468,16 +2463,16 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.4.0.tgz", - "integrity": "sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.2.1.tgz", + "integrity": "sha512-uWZ7AxzdeaQSTCwD2yZtOiEm9zyKU+wqCmi/Sf25kQQqqFSBZUStXfaQ8OHP9cecnw893ZpZ811rPhB/wfujJw==", + "dependencies": { + "@docusaurus/core": "3.2.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -2491,23 +2486,23 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.4.0.tgz", - "integrity": "sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/plugin-debug": "3.4.0", - "@docusaurus/plugin-google-analytics": "3.4.0", - "@docusaurus/plugin-google-gtag": "3.4.0", - "@docusaurus/plugin-google-tag-manager": "3.4.0", - "@docusaurus/plugin-sitemap": "3.4.0", - "@docusaurus/theme-classic": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-search-algolia": "3.4.0", - "@docusaurus/types": "3.4.0" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.2.1.tgz", + "integrity": "sha512-E3OHSmttpEBcSMhfPBq3EJMBxZBM01W1rnaCUTXy9EHvkmB5AwgTfW1PwGAybPAX579ntE03R+2zmXdizWfKnQ==", + "dependencies": { + "@docusaurus/core": "3.2.1", + "@docusaurus/plugin-content-blog": "3.2.1", + "@docusaurus/plugin-content-docs": "3.2.1", + "@docusaurus/plugin-content-pages": "3.2.1", + "@docusaurus/plugin-debug": "3.2.1", + "@docusaurus/plugin-google-analytics": "3.2.1", + "@docusaurus/plugin-google-gtag": "3.2.1", + "@docusaurus/plugin-google-tag-manager": "3.2.1", + "@docusaurus/plugin-sitemap": "3.2.1", + "@docusaurus/theme-classic": "3.2.1", + "@docusaurus/theme-common": "3.2.1", + "@docusaurus/theme-search-algolia": "3.2.1", + "@docusaurus/types": "3.2.1" }, "engines": { "node": ">=18.0" @@ -2517,23 +2512,35 @@ "react-dom": "^18.0.0" } }, + "node_modules/@docusaurus/react-loadable": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "dependencies": { + "@types/react": "*", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "*" + } + }, "node_modules/@docusaurus/theme-classic": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.4.0.tgz", - "integrity": "sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-translations": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.2.1.tgz", + "integrity": "sha512-+vSbnQyoWjc6vRZi4vJO2dBU02wqzynsai15KK+FANZudrYaBHtkbLZAQhgmxzBGVpxzi87gRohlMm+5D8f4tA==", + "dependencies": { + "@docusaurus/core": "3.2.1", + "@docusaurus/mdx-loader": "3.2.1", + "@docusaurus/module-type-aliases": "3.2.1", + "@docusaurus/plugin-content-blog": "3.2.1", + "@docusaurus/plugin-content-docs": "3.2.1", + "@docusaurus/plugin-content-pages": "3.2.1", + "@docusaurus/theme-common": "3.2.1", + "@docusaurus/theme-translations": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", @@ -2557,17 +2564,17 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.4.0.tgz", - "integrity": "sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==", - "dependencies": { - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.2.1.tgz", + "integrity": "sha512-d+adiD7L9xv6EvfaAwUqdKf4orsM3jqgeqAM+HAjgL/Ux0GkVVnfKr+tsoe+4ow4rHe6NUt+nkkW8/K8dKdilA==", + "dependencies": { + "@docusaurus/mdx-loader": "3.2.1", + "@docusaurus/module-type-aliases": "3.2.1", + "@docusaurus/plugin-content-blog": "3.2.1", + "@docusaurus/plugin-content-docs": "3.2.1", + "@docusaurus/plugin-content-pages": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2586,18 +2593,18 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.4.0.tgz", - "integrity": "sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.2.1.tgz", + "integrity": "sha512-bzhCrpyXBXzeydNUH83II2akvFEGfhsNTPPWsk5N7e+odgQCQwoHhcF+2qILbQXjaoZ6B3c48hrvkyCpeyqGHw==", "dependencies": { "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-translations": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.2.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/plugin-content-docs": "3.2.1", + "@docusaurus/theme-common": "3.2.1", + "@docusaurus/theme-translations": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "algoliasearch": "^4.18.0", "algoliasearch-helper": "^3.13.3", "clsx": "^2.0.0", @@ -2616,9 +2623,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.4.0.tgz", - "integrity": "sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.2.1.tgz", + "integrity": "sha512-jAUMkIkFfY+OAhJhv6mV8zlwY6J4AQxJPTgLdR2l+Otof9+QdJjHNh/ifVEu9q0lp3oSPlJj9l05AaP7Ref+cg==", "dependencies": { "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -2628,9 +2635,9 @@ } }, "node_modules/@docusaurus/types": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.4.0.tgz", - "integrity": "sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.2.1.tgz", + "integrity": "sha512-n/toxBzL2oxTtRTOFiGKsHypzn/Pm+sXyw+VSk1UbqbXQiHOwHwts55bpKwbcUgA530Is6kix3ELiFOv9GAMfw==", "dependencies": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", @@ -2648,13 +2655,13 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.4.0.tgz", - "integrity": "sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.2.1.tgz", + "integrity": "sha512-DPkIS/EPc+pGAV798PUXgNzJFM3HJouoQXgr0KDZuJVz1EkWbDLOcQwLIz8Qx7liI9ddfkN/TXTRQdsTPZNakw==", "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@svgr/webpack": "^8.1.0", + "@docusaurus/logger": "3.2.1", + "@docusaurus/utils-common": "3.2.1", + "@svgr/webpack": "^6.5.1", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", @@ -2670,7 +2677,6 @@ "shelljs": "^0.8.5", "tslib": "^2.6.0", "url-loader": "^4.1.1", - "utility-types": "^3.10.0", "webpack": "^5.88.1" }, "engines": { @@ -2686,9 +2692,9 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.4.0.tgz", - "integrity": "sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.2.1.tgz", + "integrity": "sha512-N5vadULnRLiqX2QfTjVEU3u5vo6RG2EZTdyXvJdzDOdrLCGIZAfnf/VkssinFZ922sVfaFfQ4FnStdhn5TWdVg==", "dependencies": { "tslib": "^2.6.0" }, @@ -2705,17 +2711,15 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.4.0.tgz", - "integrity": "sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==", - "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "fs-extra": "^11.2.0", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.2.1.tgz", + "integrity": "sha512-+x7IR9hNMXi62L1YAglwd0s95fR7+EtirjTxSN4kahYRWGqOi3jlQl1EV0az/yTEvKbxVvOPcdYicGu9dk4LJw==", + "dependencies": { + "@docusaurus/logger": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", "joi": "^17.9.2", "js-yaml": "^4.1.0", - "lodash": "^4.17.21", "tslib": "^2.6.0" }, "engines": { @@ -2988,11 +2992,11 @@ } }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz", + "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==", "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { "type": "github", @@ -3033,11 +3037,11 @@ } }, "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", - "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz", + "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==", "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { "type": "github", @@ -3048,11 +3052,11 @@ } }, "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", - "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz", + "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==", "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { "type": "github", @@ -3063,11 +3067,11 @@ } }, "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", - "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz", + "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==", "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { "type": "github", @@ -3078,11 +3082,11 @@ } }, "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", - "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz", + "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==", "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { "type": "github", @@ -3093,9 +3097,9 @@ } }, "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", - "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz", + "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==", "engines": { "node": ">=12" }, @@ -3108,21 +3112,21 @@ } }, "node_modules/@svgr/babel-preset": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", - "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz", + "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==", "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", - "@svgr/babel-plugin-transform-svg-component": "8.0.0" + "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1", + "@svgr/babel-plugin-remove-jsx-attribute": "*", + "@svgr/babel-plugin-remove-jsx-empty-expression": "*", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1", + "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1", + "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1", + "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1", + "@svgr/babel-plugin-transform-svg-component": "^6.5.1" }, "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { "type": "github", @@ -3133,18 +3137,18 @@ } }, "node_modules/@svgr/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", - "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz", + "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==", "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", + "@babel/core": "^7.19.6", + "@svgr/babel-preset": "^6.5.1", + "@svgr/plugin-jsx": "^6.5.1", "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3", - "snake-case": "^3.0.4" + "cosmiconfig": "^7.0.1" }, "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { "type": "github", @@ -3152,15 +3156,15 @@ } }, "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", - "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz", + "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==", "dependencies": { - "@babel/types": "^7.21.3", + "@babel/types": "^7.20.0", "entities": "^4.4.0" }, "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { "type": "github", @@ -3168,37 +3172,37 @@ } }, "node_modules/@svgr/plugin-jsx": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", - "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz", + "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==", "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "@svgr/hast-util-to-babel-ast": "8.0.0", + "@babel/core": "^7.19.6", + "@svgr/babel-preset": "^6.5.1", + "@svgr/hast-util-to-babel-ast": "^6.5.1", "svg-parser": "^2.0.4" }, "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/gregberge" }, "peerDependencies": { - "@svgr/core": "*" + "@svgr/core": "^6.0.0" } }, "node_modules/@svgr/plugin-svgo": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", - "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz", + "integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==", "dependencies": { - "cosmiconfig": "^8.1.3", - "deepmerge": "^4.3.1", - "svgo": "^3.0.2" + "cosmiconfig": "^7.0.1", + "deepmerge": "^4.2.2", + "svgo": "^2.8.0" }, "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { "type": "github", @@ -3209,21 +3213,21 @@ } }, "node_modules/@svgr/webpack": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", - "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz", + "integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==", "dependencies": { - "@babel/core": "^7.21.3", - "@babel/plugin-transform-react-constant-elements": "^7.21.3", - "@babel/preset-env": "^7.20.2", + "@babel/core": "^7.19.6", + "@babel/plugin-transform-react-constant-elements": "^7.18.12", + "@babel/preset-env": "^7.19.4", "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.21.0", - "@svgr/core": "8.1.0", - "@svgr/plugin-jsx": "8.1.0", - "@svgr/plugin-svgo": "8.1.0" + "@babel/preset-typescript": "^7.18.6", + "@svgr/core": "^6.5.1", + "@svgr/plugin-jsx": "^6.5.1", + "@svgr/plugin-svgo": "^6.5.1" }, "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { "type": "github", @@ -3300,9 +3304,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "version": "8.56.9", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.9.tgz", + "integrity": "sha512-W4W3KcqzjJ0sHg2vAq9vfml6OhsJ53TcUjUqfzzZf/EChUtwspszj/S0pzMxnfRcO55/iGq47dscXw71Fxc4Zg==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -3342,9 +3346,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", - "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz", + "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -3420,9 +3424,9 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", "dependencies": { "@types/unist": "*" } @@ -3443,9 +3447,9 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.14.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", - "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "dependencies": { "undici-types": "~5.26.4" } @@ -3464,9 +3468,9 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "node_modules/@types/prismjs": { - "version": "1.26.4", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.4.tgz", - "integrity": "sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg==" + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", + "integrity": "sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==" }, "node_modules/@types/prop-types": { "version": "15.7.12", @@ -3474,9 +3478,9 @@ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" + "version": "6.9.14", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.14.tgz", + "integrity": "sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA==" }, "node_modules/@types/range-parser": { "version": "1.2.7", @@ -3484,9 +3488,9 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "node_modules/@types/react": { - "version": "18.3.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", - "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "version": "18.2.78", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.78.tgz", + "integrity": "sha512-qOwdPnnitQY4xKlKayt42q5W5UQrSHjgoXNVEtxeqdITJ99k4VXJOP3vt8Rkm9HmgJpH50UNU+rlqfkfWOqp0A==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -3773,9 +3777,9 @@ } }, "node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "bin": { "acorn": "bin/acorn" }, @@ -3783,10 +3787,10 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "peerDependencies": { "acorn": "^8" } @@ -3800,12 +3804,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", - "dependencies": { - "acorn": "^8.11.0" - }, + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "engines": { "node": ">=0.4.0" } @@ -3831,14 +3832,14 @@ } }, "node_modules/ajv": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", - "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dependencies": { - "fast-deep-equal": "^3.1.3", + "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" + "uri-js": "^4.2.2" }, "funding": { "type": "github", @@ -3873,31 +3874,31 @@ } }, "node_modules/algoliasearch": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.24.0.tgz", - "integrity": "sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==", - "dependencies": { - "@algolia/cache-browser-local-storage": "4.24.0", - "@algolia/cache-common": "4.24.0", - "@algolia/cache-in-memory": "4.24.0", - "@algolia/client-account": "4.24.0", - "@algolia/client-analytics": "4.24.0", - "@algolia/client-common": "4.24.0", - "@algolia/client-personalization": "4.24.0", - "@algolia/client-search": "4.24.0", - "@algolia/logger-common": "4.24.0", - "@algolia/logger-console": "4.24.0", - "@algolia/recommend": "4.24.0", - "@algolia/requester-browser-xhr": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/requester-node-http": "4.24.0", - "@algolia/transporter": "4.24.0" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", + "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-account": "4.23.3", + "@algolia/client-analytics": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-personalization": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/recommend": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/algoliasearch-helper": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.22.1.tgz", - "integrity": "sha512-fSxJ4YreH4kOME9CnKazbAn2tK/rvBoV37ETd6nTt4j7QfkcnW+c+F22WfuE9Q/sRpvOMnUwU/BXAVEiwW7p/w==", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.17.0.tgz", + "integrity": "sha512-R5422OiQjvjlK3VdpNQ/Qk7KsTIGeM5ACm8civGifOVWdRRV/3SgXuKmeNxe94Dz6fwj/IgpVmXbHutU4mHubg==", "dependencies": { "@algolia/events": "^4.0.1" }, @@ -4076,12 +4077,12 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.10.tgz", + "integrity": "sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ==", "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", + "@babel/helper-define-polyfill-provider": "^0.6.1", "semver": "^6.3.1" }, "peerDependencies": { @@ -4109,11 +4110,11 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.1.tgz", + "integrity": "sha512-JfTApdE++cgcTWjsiCQlLyFBMbTUft9ja17saCc93lgV33h4tuCVj7tlvu//qpLwaG+3yEz7/KhahGrUMkVq9g==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" + "@babel/helper-define-polyfill-provider": "^0.6.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -4246,20 +4247,20 @@ } }, "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dependencies": { - "fill-range": "^7.1.1" + "fill-range": "^7.0.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", - "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "funding": [ { "type": "opencollective", @@ -4275,10 +4276,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -4325,6 +4326,17 @@ "node": ">=14.16" } }, + "node_modules/cacheable-request/node_modules/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -4383,9 +4395,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001639", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001639.tgz", - "integrity": "sha512-eFHflNTBIlFwP2AIKaYuBQN/apnUoKNhBdza8ZnW/h2di4LCZ4xFqYlxUxo+LQ76KFI1PGcC1QDxMbxTZpSCAg==", + "version": "1.0.30001609", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz", + "integrity": "sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA==", "funding": [ { "type": "opencollective", @@ -4529,9 +4541,9 @@ } }, "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "engines": { "node": ">=6.0" } @@ -4589,9 +4601,9 @@ } }, "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.4.tgz", + "integrity": "sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==", "dependencies": { "string-width": "^4.2.0" }, @@ -4634,9 +4646,9 @@ } }, "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", + "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", "engines": { "node": ">=6" } @@ -4914,9 +4926,9 @@ } }, "node_modules/core-js": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", - "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.1.tgz", + "integrity": "sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -4924,9 +4936,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", - "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", + "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==", "dependencies": { "browserslist": "^4.23.0" }, @@ -4936,9 +4948,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.37.1.tgz", - "integrity": "sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==", + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.36.1.tgz", + "integrity": "sha512-NXCvHvSVYSrewP0L5OhltzXeWFJLo2AL2TYnj6iLV3Bw8mM62wAQMNgUCRI6EBu6hVVpbCxmOPlxh1Ikw2PfUA==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -4951,28 +4963,18 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=10" } }, "node_modules/cross-spawn": { @@ -5014,11 +5016,11 @@ } }, "node_modules/css-declaration-sorter": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", - "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", "engines": { - "node": "^14 || ^16 || >=18" + "node": "^10 || ^12 || >=14" }, "peerDependencies": { "postcss": "^8.0.9" @@ -5059,16 +5061,16 @@ } }, "node_modules/css-minimizer-webpack-plugin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", - "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", + "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "cssnano": "^6.0.1", - "jest-worker": "^29.4.3", - "postcss": "^8.4.24", - "schema-utils": "^4.0.1", - "serialize-javascript": "^6.0.1" + "cssnano": "^5.1.8", + "jest-worker": "^29.1.2", + "postcss": "^8.4.17", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" }, "engines": { "node": ">= 14.15.0" @@ -5101,6 +5103,14 @@ } } }, + "node_modules/css-minimizer-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -5117,15 +5127,23 @@ } }, "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" + "mdn-data": "2.0.14", + "source-map": "^0.6.1" }, "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" } }, "node_modules/css-what": { @@ -5151,128 +5169,108 @@ } }, "node_modules/cssnano": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", - "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", + "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", "dependencies": { - "cssnano-preset-default": "^6.1.2", - "lilconfig": "^3.1.1" + "cssnano-preset-default": "^5.2.14", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/cssnano" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/cssnano-preset-advanced": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", - "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.10.tgz", + "integrity": "sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ==", "dependencies": { - "autoprefixer": "^10.4.19", - "browserslist": "^4.23.0", - "cssnano-preset-default": "^6.1.2", - "postcss-discard-unused": "^6.0.5", - "postcss-merge-idents": "^6.0.3", - "postcss-reduce-idents": "^6.0.3", - "postcss-zindex": "^6.0.2" + "autoprefixer": "^10.4.12", + "cssnano-preset-default": "^5.2.14", + "postcss-discard-unused": "^5.1.0", + "postcss-merge-idents": "^5.1.1", + "postcss-reduce-idents": "^5.2.0", + "postcss-zindex": "^5.1.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/cssnano-preset-default": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", - "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", - "dependencies": { - "browserslist": "^4.23.0", - "css-declaration-sorter": "^7.2.0", - "cssnano-utils": "^4.0.2", - "postcss-calc": "^9.0.1", - "postcss-colormin": "^6.1.0", - "postcss-convert-values": "^6.1.0", - "postcss-discard-comments": "^6.0.2", - "postcss-discard-duplicates": "^6.0.3", - "postcss-discard-empty": "^6.0.3", - "postcss-discard-overridden": "^6.0.2", - "postcss-merge-longhand": "^6.0.5", - "postcss-merge-rules": "^6.1.1", - "postcss-minify-font-values": "^6.1.0", - "postcss-minify-gradients": "^6.0.3", - "postcss-minify-params": "^6.1.0", - "postcss-minify-selectors": "^6.0.4", - "postcss-normalize-charset": "^6.0.2", - "postcss-normalize-display-values": "^6.0.2", - "postcss-normalize-positions": "^6.0.2", - "postcss-normalize-repeat-style": "^6.0.2", - "postcss-normalize-string": "^6.0.2", - "postcss-normalize-timing-functions": "^6.0.2", - "postcss-normalize-unicode": "^6.1.0", - "postcss-normalize-url": "^6.0.2", - "postcss-normalize-whitespace": "^6.0.2", - "postcss-ordered-values": "^6.0.2", - "postcss-reduce-initial": "^6.1.0", - "postcss-reduce-transforms": "^6.0.2", - "postcss-svgo": "^6.0.3", - "postcss-unique-selectors": "^6.0.4" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", + "dependencies": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.2", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, "node_modules/cssnano-utils": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", - "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" + "css-tree": "^1.1.2" }, "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" + "node": ">=8.0.0" } }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" - }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -5284,9 +5282,9 @@ "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, @@ -5463,9 +5461,9 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" }, "node_modules/detect-port": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", - "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", + "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", "dependencies": { "address": "^1.0.1", "debug": "4" @@ -5473,9 +5471,6 @@ "bin": { "detect": "bin/detect-port.js", "detect-port": "bin/detect-port.js" - }, - "engines": { - "node": ">= 4.0.0" } }, "node_modules/detect-port-alt": { @@ -5647,9 +5642,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.815", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.815.tgz", - "integrity": "sha512-OvpTT2ItpOXJL7IGcYakRjHCt8L5GrrN/wHCQsRB4PQa1X9fe+X9oen245mIId7s14xvArCGSTIq644yPUKKLg==" + "version": "1.4.736", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.736.tgz", + "integrity": "sha512-Rer6wc3ynLelKNM4lOCg7/zPQj8tPOCB2hzD32PX9wd3hgRRi9MxEbmkFCokzcEhRVMiOVLjnL9ig9cefJ+6+Q==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -5687,9 +5682,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", + "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -5737,9 +5732,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.0.tgz", + "integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==" }, "node_modules/escalade": { "version": "3.1.2", @@ -5878,11 +5873,12 @@ } }, "node_modules/estree-util-value-to-estree": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.1.2.tgz", - "integrity": "sha512-S0gW2+XZkmsx00tU2uJ4L9hUT7IFabbml9pHh2WQqFmAbxit++YGZne0sKJbNwkj9Wvg9E4uqWl4nCIFQMmfag==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.1.1.tgz", + "integrity": "sha512-5mvUrF2suuv5f5cGDnDphIy4/gW86z82kl5qG6mM9z04SEQI4FB5Apmaw/TGEf3l55nLtMs5s51dmhUzvAHQCA==", "dependencies": { - "@types/estree": "^1.0.0" + "@types/estree": "^1.0.0", + "is-plain-obj": "^4.0.0" }, "funding": { "url": "https://github.com/sponsors/remcohaszing" @@ -6225,9 +6221,9 @@ } }, "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -6500,9 +6496,9 @@ } }, "node_modules/fs-monkey": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==" }, "node_modules/fs.realpath": { "version": "1.0.0", @@ -6581,7 +6577,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -6896,9 +6891,9 @@ } }, "node_modules/hast-util-raw": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.4.tgz", - "integrity": "sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.2.tgz", + "integrity": "sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA==", "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", @@ -7449,7 +7444,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -7487,9 +7481,9 @@ } }, "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", + "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", "engines": { "node": ">= 10" } @@ -7544,14 +7538,11 @@ } }, "node_modules/is-core-module": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", - "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7826,17 +7817,17 @@ } }, "node_modules/jiti": { - "version": "1.21.6", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", - "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", "bin": { "jiti": "bin/jiti.js" } }, "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "version": "17.12.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.3.tgz", + "integrity": "sha512-2RRziagf555owrm9IRVtdKynOBeITiDpuZqIpgwqXShPncPKNiRQoiGsl/T8SQdq+8ugRzH2LqY67irr2y/d+g==", "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", @@ -7953,9 +7944,9 @@ } }, "node_modules/launch-editor": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.0.tgz", - "integrity": "sha512-vJranOAJrI/llyWGRQqiDM+adrw+k83fvmmx3+nV47g3+36xM15jE+zyZ6Ffel02+xSvuM0b2GDRosXZkbb6wA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", + "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", "dependencies": { "picocolors": "^1.0.0", "shell-quote": "^1.8.1" @@ -7970,14 +7961,11 @@ } }, "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" + "node": ">=10" } }, "node_modules/lines-and-columns": { @@ -8153,9 +8141,9 @@ } }, "node_modules/mdast-util-from-markdown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz", - "integrity": "sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", @@ -8435,9 +8423,9 @@ } }, "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz", + "integrity": "sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==", "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", @@ -8486,9 +8474,9 @@ } }, "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" }, "node_modules/media-typer": { "version": "0.3.0", @@ -8570,9 +8558,9 @@ } }, "node_modules/micromark-core-commonmark": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz", - "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", "funding": [ { "type": "GitHub Sponsors", @@ -10088,9 +10076,9 @@ ] }, "node_modules/micromark-util-subtokenize": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", - "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", + "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", "funding": [ { "type": "GitHub Sponsors", @@ -10207,11 +10195,11 @@ ] }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dependencies": { - "braces": "^3.0.3", + "braces": "^3.0.2", "picomatch": "^2.3.1" }, "engines": { @@ -10268,9 +10256,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", - "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", + "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -10418,11 +10406,11 @@ } }, "node_modules/normalize-url": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", - "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "engines": { - "node": ">=14.16" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -10464,12 +10452,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "engines": { - "node": ">= 0.4" - }, + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10823,9 +10808,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -10920,9 +10905,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "funding": [ { "type": "opencollective", @@ -10939,7 +10924,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", + "picocolors": "^1.0.0", "source-map-js": "^1.2.0" }, "engines": { @@ -10947,108 +10932,105 @@ } }, "node_modules/postcss-calc": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", - "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", "dependencies": { - "postcss-selector-parser": "^6.0.11", + "postcss-selector-parser": "^6.0.9", "postcss-value-parser": "^4.2.0" }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, "peerDependencies": { "postcss": "^8.2.2" } }, "node_modules/postcss-colormin": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", - "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", - "colord": "^2.9.3", + "colord": "^2.9.1", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-convert-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", - "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-discard-comments": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", - "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-discard-duplicates": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", - "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-discard-empty": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", - "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-discard-overridden": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", - "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-discard-unused": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", - "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz", + "integrity": "sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==", "dependencies": { - "postcss-selector-parser": "^6.0.16" + "postcss-selector-parser": "^6.0.5" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-loader": { @@ -11072,111 +11054,136 @@ "webpack": "^5.0.0" } }, + "node_modules/postcss-loader/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/postcss-merge-idents": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", - "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz", + "integrity": "sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==", "dependencies": { - "cssnano-utils": "^4.0.2", + "cssnano-utils": "^3.1.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-merge-longhand": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", - "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", "dependencies": { "postcss-value-parser": "^4.2.0", - "stylehacks": "^6.1.1" + "stylehacks": "^5.1.1" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-merge-rules": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", - "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", - "cssnano-utils": "^4.0.2", - "postcss-selector-parser": "^6.0.16" + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-minify-font-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", - "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-minify-gradients": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", - "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", "dependencies": { - "colord": "^2.9.3", - "cssnano-utils": "^4.0.2", + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-minify-params": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", - "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", "dependencies": { - "browserslist": "^4.23.0", - "cssnano-utils": "^4.0.2", + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-minify-selectors": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", - "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", "dependencies": { - "postcss-selector-parser": "^6.0.16" + "postcss-selector-parser": "^6.0.5" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-modules-extract-imports": { @@ -11235,191 +11242,192 @@ } }, "node_modules/postcss-normalize-charset": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", - "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-normalize-display-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", - "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-normalize-positions": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", - "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-normalize-repeat-style": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", - "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-normalize-string": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", - "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-normalize-timing-functions": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", - "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-normalize-unicode": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", - "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-normalize-url": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", - "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", "dependencies": { + "normalize-url": "^6.0.1", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-normalize-whitespace": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", - "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-ordered-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", - "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", "dependencies": { - "cssnano-utils": "^4.0.2", + "cssnano-utils": "^3.1.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-reduce-idents": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", - "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz", + "integrity": "sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-reduce-initial": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", - "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.21.4", "caniuse-api": "^3.0.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-reduce-transforms": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", - "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-selector-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", - "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -11429,46 +11437,46 @@ } }, "node_modules/postcss-sort-media-queries": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", - "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz", + "integrity": "sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw==", "dependencies": { - "sort-css-media-queries": "2.2.0" + "sort-css-media-queries": "2.1.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=10.0.0" }, "peerDependencies": { - "postcss": "^8.4.23" + "postcss": "^8.4.16" } }, "node_modules/postcss-svgo": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", - "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", "dependencies": { "postcss-value-parser": "^4.2.0", - "svgo": "^3.2.0" + "svgo": "^2.7.0" }, "engines": { - "node": "^14 || ^16 || >= 18" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-unique-selectors": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", - "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", "dependencies": { - "postcss-selector-parser": "^6.0.16" + "postcss-selector-parser": "^6.0.5" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/postcss-value-parser": { @@ -11477,14 +11485,14 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/postcss-zindex": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", - "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.1.0.tgz", + "integrity": "sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==", "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/pretty-error": { @@ -11717,9 +11725,9 @@ } }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -11791,9 +11799,9 @@ } }, "node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", - "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", "engines": { "node": ">= 12.13.0" } @@ -11860,15 +11868,15 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.23.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^18.2.0" } }, "node_modules/react-error-overlay": { @@ -11903,9 +11911,9 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-json-view-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.4.0.tgz", - "integrity": "sha512-wh6F6uJyYAmQ4fK0e8dSQMEWuvTs2Wr3el3sLD9bambX1+pSWUVXIz1RFaoy3TI1mZ0FqdpKq9YgbgTTgyrmXA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.3.0.tgz", + "integrity": "sha512-aN1biKC5v4DQkmQBlZjuMFR09MKZGMPtIg+cut8zEeg2HXd6gl2gRy0n4HMacHf0dznQgo0SVXN7eT8zV3hEuQ==", "engines": { "node": ">=14" }, @@ -11915,11 +11923,12 @@ }, "node_modules/react-loadable": { "name": "@docusaurus/react-loadable", - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", - "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", "dependencies": { - "@types/react": "*" + "@types/react": "*", + "prop-types": "^15.6.2" }, "peerDependencies": { "react": "*" @@ -12453,7 +12462,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", "dependencies": { "glob": "^7.1.3" }, @@ -12533,14 +12541,14 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "dependencies": { "loose-envify": "^1.1.0" } @@ -12564,9 +12572,9 @@ } }, "node_modules/search-insights": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.14.0.tgz", - "integrity": "sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.13.0.tgz", + "integrity": "sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==", "peer": true }, "node_modules/section-matter": { @@ -12599,9 +12607,12 @@ } }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" }, @@ -12623,6 +12634,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -12905,9 +12932,9 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, "node_modules/sitemap": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", - "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz", + "integrity": "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==", "dependencies": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", @@ -12946,15 +12973,6 @@ "node": ">=8" } }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -12966,9 +12984,9 @@ } }, "node_modules/sort-css-media-queries": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", - "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz", + "integrity": "sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==", "engines": { "node": ">= 6.3.0" } @@ -13059,6 +13077,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -13194,18 +13218,18 @@ } }, "node_modules/stylehacks": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", - "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", "dependencies": { - "browserslist": "^4.23.0", - "postcss-selector-parser": "^6.0.16" + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.2.15" } }, "node_modules/supports-color": { @@ -13236,27 +13260,23 @@ "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" }, "node_modules/svgo": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", - "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.3.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.0.0" + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" }, "bin": { "svgo": "bin/svgo" }, "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" + "node": ">=10.13.0" } }, "node_modules/svgo/node_modules/commander": { @@ -13267,6 +13287,69 @@ "node": ">= 10" } }, + "node_modules/svgo/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/svgo/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/svgo/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -13276,9 +13359,9 @@ } }, "node_modules/terser": { - "version": "5.31.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.1.tgz", - "integrity": "sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==", + "version": "5.30.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.3.tgz", + "integrity": "sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -13476,9 +13559,9 @@ } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/type-fest": { "version": "2.19.0", @@ -13531,9 +13614,9 @@ } }, "node_modules/typescript": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", - "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "peer": true, "bin": { "tsc": "bin/tsc", @@ -13593,9 +13676,9 @@ } }, "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", + "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", @@ -13729,9 +13812,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "funding": [ { "type": "opencollective", @@ -13747,8 +13830,8 @@ } ], "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.1.1", + "picocolors": "^1.0.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -14050,9 +14133,9 @@ } }, "node_modules/webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", + "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", @@ -14060,10 +14143,10 @@ "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.16.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -14236,9 +14319,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "engines": { "node": ">=10.0.0" }, @@ -14480,9 +14563,9 @@ } }, "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "engines": { "node": ">=8.3.0" }, @@ -14535,9 +14618,9 @@ } }, "node_modules/yocto-queue": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", - "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", "engines": { "node": ">=12.20" }, diff --git a/website/package.json b/website/package.json index ad425931..618b8c83 100644 --- a/website/package.json +++ b/website/package.json @@ -14,10 +14,10 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "^3.4.0", - "@docusaurus/plugin-google-gtag": "^3.4.0", - "@docusaurus/plugin-google-tag-manager": "^3.4.0", - "@docusaurus/preset-classic": "^3.4.0", + "@docusaurus/core": "^3.2.1", + "@docusaurus/plugin-google-gtag": "^3.2.1", + "@docusaurus/plugin-google-tag-manager": "^3.2.1", + "@docusaurus/preset-classic": "^3.2.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "prism-react-renderer": "^2.3.0", @@ -26,8 +26,8 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/types": "3.4.0" + "@docusaurus/module-type-aliases": "3.2.1", + "@docusaurus/types": "3.2.1" }, "browserslist": { "production": [ From b67470edd8d60cb015a9657b9e24d3f6bf54aee7 Mon Sep 17 00:00:00 2001 From: Katsuya Matsuoka Date: Thu, 5 Sep 2024 15:50:43 +0900 Subject: [PATCH 22/22] update doc for indy --- lib/indy/README.md | 55 ++++++++++++++++++++++++++++++++----------- lib/indy/README_ja.md | 47 +++++++++++++++++++++++++++++------- 2 files changed, 79 insertions(+), 23 deletions(-) diff --git a/lib/indy/README.md b/lib/indy/README.md index 3ce7cf23..c471bf31 100644 --- a/lib/indy/README.md +++ b/lib/indy/README.md @@ -2,7 +2,7 @@ | Contributed by | |:--------------------:| -| [@fsatsuki](https://github.com/fsatsuki) | +| [@fsatsuki](https://github.com/fsatsuki), [@KatsuyaMatsuoka](https://github.com/KatsuyaMatsuoka) | [View this page in Japanese (日本語)](./README_ja.md) @@ -13,6 +13,31 @@ This is a sample of building a Hyperledger Indy network on AWS. The overall architecture is shown below, processing itself is performed by 4 Stewards (Validator Nodes), and network management is performed with Trustee. It consists of 4 EC2 instances for Steward and 3 EC2 instances for Trustee. +## Well-Architected + +
+ +Review the for pros and cons of this solution. + +### Well-Architected Checklist + +This is the Well-Architected checklist for Hyperledger Indy nodes implementation of the AWS Blockchain Node Runner app. This checklist takes into account questions from the [AWS Well-Architected Framework](https://aws.amazon.com/architecture/well-architected/) which are relevant to this workload. Please feel free to add more checks from the framework if required for your workload. + +| Pillar | Control | Question/Check | Remarks | +|:------------------------|:----------------------------------|:---------------------------------------------------------------------------------|:-----------------| +| Security | Network protection | Are there unnecessary open ports in security groups? | Please note that only ports 9701 and 9702 are open for trustee and steward instances. | +| | | Traffic inspection | AWS WAF could be implemented for traffic inspection. Additional charges will apply. | +| | Compute protection | Reduce attack surface | This solution uses Amazon Linux 2 AMI. You may choose to run hardening scripts on it. | +| | | Enable people to perform actions at a distance | This solution uses AWS Systems Manager for terminal session, not ssh ports. | +| | Data protection at rest | Use encrypted Amazon Elastic Block Store (Amazon EBS) volumes | This solution uses encrypted Amazon EBS volumes. | +| | | Use encrypted Amazon Simple Storage Service (Amazon S3) buckets | This solution uses Amazon S3 managed keys (SSE-S3) encryption. | +| | Authorization and access control | Use instance profile with Amazon Elastic Compute Cloud (Amazon EC2) instances | This solution uses AWS Identity and Access Management (AWS IAM) role instead of IAM user. | +| | Application security | Security focused development practices | cdk-nag is being used with appropriate suppressions. | +| Cost optimization | Cost awareness | Estimate costs | Steward instances are t3.large and trustee instances are t3.medium for optimal cost in the test environment. If you use this solution in production environment, we recommend to change to M instances. | +| Performance efficiency | Storage selection | How is storage solution selected? | Storage solution is selected based on best price-performance, i.e. gp3 Amazon EBS volumes with optimal IOPS and throughput. | + +
+ ## Solution Walkthrough ### Setup Cloud9 @@ -189,7 +214,11 @@ INDY_NETWORK_NAME: sample-network ansible-playbook playbook/site.yml ``` -## Clearing up and undeploying everything +### Access to Indy Nodes + +To use Indy nodes, there is ways to access to the node as an issuer/holder/verifier using the Hyperledger Aries framework. You should implement the Aries Agents using [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript/tree/main/demo) or [Aries CloudAgent Python](https://github.com/hyperledger/aries-cloudagent-python), etc. So, you can access to the nodes from those agents. + +### Clearing up and undeploying everything 1. Remove Indy's seed, nodeInfo, did on the Secrets Manager @@ -213,22 +242,20 @@ INDY_NETWORK_NAME: sample-network npx cdk destroy --all ``` +### Reference information -#### reference information - -- [Buidling Indy Network](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md) -- [Setting up EC2 instances for Indy Node](https://github.com/hyperledger/indy-node/blob/main/docs/source/install-docs/AWS-NodeInstall-20.04.md) -- [Setting up Indy Node](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md) +- [Buidling Indy Network](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md) +- [Setting up EC2 instances for Indy Node](https://github.com/hyperledger/indy-node/blob/main/docs/source/install-docs/AWS-NodeInstall-20.04.md) +- [Setting up Indy Node](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md) ​ ### Considerations Matters to be examined in additional development etc. when using this sample are described. -- Change the instance type to M - - Currently, it is a T instance, but in production environments, it is recommended to change to M -- Fix the security group for Node NICs attached to Steward (Validator Node) - - Limit source IPs to node IPs of other nodes (currently open within VPC and can also be accessed by clients) - - Fix Node's private IP -- If necessary, change the subnet to which the node belongs to a public subnet -- Make Steward and Node separate instances +- Change the instance type to M + - Currently, this solution uses T instances, but in production environments, we recommend to change to M instances +- Fix the security group for Node NICs attached to Steward (Validator Node) + - Limit source IPs to node IPs of other nodes (currently open within VPC and can also be accessed by clients) + - Fix Node's private IP +- If necessary, change the subnet to which the node belongs to a public subnet diff --git a/lib/indy/README_ja.md b/lib/indy/README_ja.md index d8fe5b6f..b342a567 100644 --- a/lib/indy/README_ja.md +++ b/lib/indy/README_ja.md @@ -9,6 +9,31 @@ Hyperledger Indy のネットワークを AWS 上に構築するサンプルである。 全体像は下図の通り、処理自体は 4 つの Steward (Validator Node) で行われ、ネットワークの管理は Trustee で行われる。実体は Steward 用の 4 つの EC2 インスタンスと、Trustee 用の 3 つの EC2 インスタンスである。 +## Well-Architected + +
+ +Review the for pros and cons of this solution. + +### Well-Architected Checklist + +This is the Well-Architected checklist for Hyperledger Indy nodes implementation of the AWS Blockchain Node Runner app. This checklist takes into account questions from the [AWS Well-Architected Framework](https://aws.amazon.com/architecture/well-architected/) which are relevant to this workload. Please feel free to add more checks from the framework if required for your workload. + +| Pillar | Control | Question/Check | Remarks | +|:------------------------|:----------------------------------|:---------------------------------------------------------------------------------|:-----------------| +| Security | Network protection | Are there unnecessary open ports in security groups? | Please note that only ports 9701 and 9702 are open for trustee and steward instances. | +| | | Traffic inspection | AWS WAF could be implemented for traffic inspection. Additional charges will apply. | +| | Compute protection | Reduce attack surface | This solution uses Amazon Linux 2 AMI. You may choose to run hardening scripts on it. | +| | | Enable people to perform actions at a distance | This solution uses AWS Systems Manager for terminal session, not ssh ports. | +| | Data protection at rest | Use encrypted Amazon Elastic Block Store (Amazon EBS) volumes | This solution uses encrypted Amazon EBS volumes. | +| | | Use encrypted Amazon Simple Storage Service (Amazon S3) buckets | This solution uses Amazon S3 managed keys (SSE-S3) encryption. | +| | Authorization and access control | Use instance profile with Amazon Elastic Compute Cloud (Amazon EC2) instances | This solution uses AWS Identity and Access Management (AWS IAM) role instead of IAM user. | +| | Application security | Security focused development practices | cdk-nag is being used with appropriate suppressions. | +| Cost optimization | Cost awareness | Estimate costs | Steward instances are t3.large and trustee instances are t3.medium for optimal cost in the test environment. If you use this solution in production environment, we recommend to change to M instances. | +| Performance efficiency | Storage selection | How is storage solution selected? | Storage solution is selected based on best price-performance, i.e. gp3 Amazon EBS volumes with optimal IOPS and throughput. | + +
+ ## Solution Walkthrough ### Setup Cloud9 @@ -170,7 +195,11 @@ INDY_NETWORK_NAME: sample-network $ ansible-playbook playbook/site.yml ``` -## すべてを削除する方法 +### Access to Indy Nodes + +Indy node を使用するには、Hyperledger Aries フレームワークを使用して Issuer / Holder / Verifier としてノードにアクセスする方法があります。Aries agent は [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript/tree/main/demo) や [Aries CloudAgent Python](https://github.com/hyperledger/aries-cloudagent-python) などを使用して実装する必要があります。そして、それらの Aries agent から Indy node にアクセスできるようになります。 + +### すべてを削除する方法 1. Secrets ManagerからIndyのseed, nodeInfo, didを削除する @@ -192,7 +221,8 @@ $ ansible-playbook playbook/999_cleanup.yml cdk destroy --all ``` -## 参考情報 +### 参考情報 + - [Indy Network の構築](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/NewNetwork/NewNetwork.md) - [Indy Node のための EC2 セットアップ](https://github.com/hyperledger/indy-node/blob/main/docs/source/install-docs/AWS-NodeInstall-20.04.md) - [Indy Node のセットアップ](https://github.com/pSchlarb/indy-node/blob/documentationUpdate/docs/source/installation-and-configuration.md) @@ -201,10 +231,9 @@ $ ansible-playbook playbook/999_cleanup.yml 本サンプルを利用するにあたり追加開発などで検討する事項を記載する。 -- インスタンスタイプを M 系に変更 - - 現状は T 系インスタンスであるが本番環境では M 系などへの変更を推奨 -- Steward (Validator Node) にアタッチされている Node NIC の Security Group を修正 - - Source IP を他ノードの Node IP に制限する (現在は VPC 内にオープンになっており、Client からもアクセスできる) - - Node の Private IP を固定 -- 必要に応じて Node の属するサブネットを Public Subnet にする -- Steward と Node を別インスタンスにする +- インスタンスタイプを M 系に変更 + - 現状は T 系インスタンスであるが本番環境では M 系などへの変更を推奨 +- Steward (Validator Node) にアタッチされている Node NIC の Security Group を修正 + - Source IP を他ノードの Node IP に制限する (現在は VPC 内にオープンになっており、Client からもアクセスできる) + - Node の Private IP を固定 +- 必要に応じて Node の属するサブネットを Public Subnet にする