From 2451ab7a2df93cd9bb81390ae365abd9b7971469 Mon Sep 17 00:00:00 2001 From: Tanner Lewis Date: Thu, 2 Feb 2023 10:47:08 -0500 Subject: [PATCH 1/8] Partial os service cdk added Signed-off-by: Tanner Lewis --- opensearch-service-domain-cdk/.gitignore | 8 + opensearch-service-domain-cdk/.npmignore | 6 + opensearch-service-domain-cdk/README.md | 24 + opensearch-service-domain-cdk/bin/app.ts | 10 + .../cdk.context.json | 12 + opensearch-service-domain-cdk/cdk.json | 44 + opensearch-service-domain-cdk/jest.config.js | 8 + .../opensearch-service-domain-cdk-stack.ts | 38 + .../lib/stack-composer.ts | 48 + .../package-lock.json | 2986 +++++++++++++++++ opensearch-service-domain-cdk/package.json | 27 + .../opensearch-service-domain-cdk.test.ts | 17 + opensearch-service-domain-cdk/tsconfig.json | 30 + 13 files changed, 3258 insertions(+) create mode 100644 opensearch-service-domain-cdk/.gitignore create mode 100644 opensearch-service-domain-cdk/.npmignore create mode 100644 opensearch-service-domain-cdk/README.md create mode 100644 opensearch-service-domain-cdk/bin/app.ts create mode 100644 opensearch-service-domain-cdk/cdk.context.json create mode 100644 opensearch-service-domain-cdk/cdk.json create mode 100644 opensearch-service-domain-cdk/jest.config.js create mode 100644 opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts create mode 100644 opensearch-service-domain-cdk/lib/stack-composer.ts create mode 100644 opensearch-service-domain-cdk/package-lock.json create mode 100644 opensearch-service-domain-cdk/package.json create mode 100644 opensearch-service-domain-cdk/test/opensearch-service-domain-cdk.test.ts create mode 100644 opensearch-service-domain-cdk/tsconfig.json diff --git a/opensearch-service-domain-cdk/.gitignore b/opensearch-service-domain-cdk/.gitignore new file mode 100644 index 000000000..f60797b6a --- /dev/null +++ b/opensearch-service-domain-cdk/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/opensearch-service-domain-cdk/.npmignore b/opensearch-service-domain-cdk/.npmignore new file mode 100644 index 000000000..c1d6d45dc --- /dev/null +++ b/opensearch-service-domain-cdk/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/opensearch-service-domain-cdk/README.md b/opensearch-service-domain-cdk/README.md new file mode 100644 index 000000000..2edca139a --- /dev/null +++ b/opensearch-service-domain-cdk/README.md @@ -0,0 +1,24 @@ +# OpenSearch Service Domain CDK + +### Getting Started + +### Deploying your Domain Stack +Before deploying your Domain stack you should fill in any desired context parameters that will dictate the composition +of your OpenSearch Service Domain + +This can be accomplished by simply filling in the values in the `cdk.context.json` + +As well as by passing these context options as options in the CDK CLI +``` +cdk deploy --c domainName='cdk-os-service-domain' --c engineVersion='OS_1_3_6' --c dataNodeType='r6g.large.search' --c dataNodeCount=1 +``` +* Note that these context parameters can also be passed to `cdk synth` and `cdk bootstrap` commands + +### Useful CDK commands + +* `npm run build` compile typescript to js +* `npm run watch` watch for changes and compile +* `npm run test` perform the jest unit tests +* `cdk deploy` deploy this stack to your default AWS account/region +* `cdk diff` compare deployed stack with current state +* `cdk synth` emits the synthesized CloudFormation template diff --git a/opensearch-service-domain-cdk/bin/app.ts b/opensearch-service-domain-cdk/bin/app.ts new file mode 100644 index 000000000..34ab40274 --- /dev/null +++ b/opensearch-service-domain-cdk/bin/app.ts @@ -0,0 +1,10 @@ +#!/usr/bin/env node +import 'source-map-support/register'; +import * as cdk from 'aws-cdk-lib'; +import {StackComposer} from "../lib/stack-composer"; + +const app = new cdk.App(); +new StackComposer(app, { + env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, + description: "This stack contains resources to create/manage an OpenSearch Service domain" +}); \ No newline at end of file diff --git a/opensearch-service-domain-cdk/cdk.context.json b/opensearch-service-domain-cdk/cdk.context.json new file mode 100644 index 000000000..0173cc121 --- /dev/null +++ b/opensearch-service-domain-cdk/cdk.context.json @@ -0,0 +1,12 @@ +{ + "domainName": "cdk-os-service-domain", + "engineVersion": "OS_1.3", + "dataNodeType": "r6g.large.search", + "dataNodeCount": 1, + "dedicatedMasterNodeType": "r6g.large.search", + "dedicatedMasterNodeCount": 3, + "warmNodeType": "ultrawarm1.medium.search", + "warmNodeCount": 2, + "coldStorageEnabled": false, + "zoneAwarenessAvailabilityZoneCount": 1 +} diff --git a/opensearch-service-domain-cdk/cdk.json b/opensearch-service-domain-cdk/cdk.json new file mode 100644 index 000000000..eba107d56 --- /dev/null +++ b/opensearch-service-domain-cdk/cdk.json @@ -0,0 +1,44 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/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/opensearch-service-domain-cdk/jest.config.js b/opensearch-service-domain-cdk/jest.config.js new file mode 100644 index 000000000..08263b895 --- /dev/null +++ b/opensearch-service-domain-cdk/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; diff --git a/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts b/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts new file mode 100644 index 000000000..fa45b8c4f --- /dev/null +++ b/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts @@ -0,0 +1,38 @@ +import { Construct } from 'constructs'; +import { EbsDeviceVolumeType } from "aws-cdk-lib/aws-ec2"; +import { Domain, EngineVersion } from "aws-cdk-lib/aws-opensearchservice"; +import {Stack, StackProps} from "aws-cdk-lib"; + + +export interface opensearchServiceDomainCdkProps extends StackProps{ + readonly version: EngineVersion, + readonly domainName?: string, + readonly dataNodeInstanceType?: string, + readonly dataNodes?: number, + readonly masterNodeInstanceType?: string, + readonly masterNodes?: number, + readonly warmInstanceType?: string, + readonly warmNodes?: number +} + + +export class OpensearchServiceDomainCdkStack extends Stack { + constructor(scope: Construct, id: string, props: opensearchServiceDomainCdkProps) { + super(scope, id, props); + + // The code that defines your stack goes here + + const domain = new Domain(this, 'Domain', { + version: props.version, + domainName: props.domainName, + capacity: { + dataNodeInstanceType: props.dataNodeInstanceType, + dataNodes: props.dataNodes, + masterNodeInstanceType: props.masterNodeInstanceType, + masterNodes: props.masterNodes, + warmInstanceType: props.warmInstanceType, + warmNodes: props.warmNodes + } + }); + } +} diff --git a/opensearch-service-domain-cdk/lib/stack-composer.ts b/opensearch-service-domain-cdk/lib/stack-composer.ts new file mode 100644 index 000000000..7df1126ea --- /dev/null +++ b/opensearch-service-domain-cdk/lib/stack-composer.ts @@ -0,0 +1,48 @@ +import { Construct } from 'constructs'; +import { Stack, StackProps } from 'aws-cdk-lib'; +import {OpensearchServiceDomainCdkStack} from "./opensearch-service-domain-cdk-stack"; +import {EngineVersion} from "aws-cdk-lib/aws-opensearchservice"; + +export class StackComposer { + + constructor(scope: Construct, props: StackProps) { + + let version: EngineVersion + + const domainName: string = scope.node.tryGetContext('domainName') + const dataNodeType: string = scope.node.tryGetContext('dataNodeType') + const dataNodeCount: number = scope.node.tryGetContext('dataNodeCount') + const dedicatedMasterNodeType: string = scope.node.tryGetContext('dedicatedMasterNodeType') + const dedicatedMasterNodeCount: number = scope.node.tryGetContext('dedicatedMasterNodeCount') + const warmNodeType: string = scope.node.tryGetContext('warmNodeType') + const warmNodeCount: number = scope.node.tryGetContext('warmNodeCount') + + const engineVersion: string = scope.node.tryGetContext('engineVersion') + if (engineVersion.startsWith("OS_")) { + // Will accept a period delimited version string (i.e. 1.3) and return a proper EngineVersion + version = EngineVersion.openSearch(engineVersion.substring(3)) + } + else if (engineVersion.startsWith("ES_")) { + version = EngineVersion.elasticsearch(engineVersion.substring(3)) + } + else { + throw new Error("Engine version is not present or does not match the expected format, i.e. OS_1.3 or ES_7.9") + } + + const opensearchStack = new OpensearchServiceDomainCdkStack(scope, 'OpenSearchServiceDomainCDKStack', { + version: version, + domainName: getValidOrUndef(domainName), + dataNodeInstanceType: getValidOrUndef(dataNodeType), + dataNodes: getValidOrUndef(dataNodeCount), + masterNodeInstanceType: getValidOrUndef(dedicatedMasterNodeType), + masterNodes: getValidOrUndef(dedicatedMasterNodeCount), + warmInstanceType: getValidOrUndef(warmNodeType), + warmNodes: getValidOrUndef(warmNodeCount), + ...props, + }); + + function getValidOrUndef(value: any):any { + return !value ? undefined : value + } + } +} \ No newline at end of file diff --git a/opensearch-service-domain-cdk/package-lock.json b/opensearch-service-domain-cdk/package-lock.json new file mode 100644 index 000000000..be4048604 --- /dev/null +++ b/opensearch-service-domain-cdk/package-lock.json @@ -0,0 +1,2986 @@ +{ + "name": "opensearch-service-domain-cdk", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@aws-cdk/asset-awscli-v1": { + "version": "2.2.52", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.52.tgz", + "integrity": "sha512-9dBPrvByWrUOcs5Rjwv08FWSummo1Uk/EgE3dCFDqvIqlSTudEmu6TGU3zrs00rfcAjqDv6gBuSttzG5f9tfdQ==" + }, + "@aws-cdk/asset-kubectl-v20": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz", + "integrity": "sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==" + }, + "@aws-cdk/asset-node-proxy-agent-v5": { + "version": "2.0.42", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.42.tgz", + "integrity": "sha512-PxvP1UU2xa4k3Ea78DxAYY8ADvwWZ/nPu+xsjQLsT+MP+aB3RZ3pGc/fNlH7Rg56Zyb/j3GSdihAy4Oi5xa+TQ==" + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", + "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", + "dev": true + }, + "@babel/core": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", + "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helpers": "^7.20.7", + "@babel/parser": "^7.20.7", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.12", + "@babel/types": "^7.20.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "dependencies": { + "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 + } + } + }, + "@babel/generator": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", + "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", + "dev": true, + "requires": { + "@babel/types": "^7.20.7", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", + "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.10", + "@babel/types": "^7.20.7" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", + "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", + "dev": true, + "requires": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.13", + "@babel/types": "^7.20.7" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "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 + }, + "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/parser": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz", + "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==", + "dev": true + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + } + }, + "@babel/traverse": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", + "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.13", + "@babel/types": "^7.20.7", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@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 + }, + "@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, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@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, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@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, + "requires": { + "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" + } + }, + "@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 + }, + "@jest/console": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.4.1.tgz", + "integrity": "sha512-m+XpwKSi3PPM9znm5NGS8bBReeAJJpSkL1OuFCqaMaJL2YX9YXLkkI+MBchMPwu+ZuM2rynL51sgfkQteQ1CKQ==", + "dev": true, + "requires": { + "@jest/types": "^29.4.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.4.1", + "jest-util": "^29.4.1", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.4.1.tgz", + "integrity": "sha512-RXFTohpBqpaTebNdg5l3I5yadnKo9zLBajMT0I38D0tDhreVBYv3fA8kywthI00sWxPztWLD3yjiUkewwu/wKA==", + "dev": true, + "requires": { + "@jest/console": "^29.4.1", + "@jest/reporters": "^29.4.1", + "@jest/test-result": "^29.4.1", + "@jest/transform": "^29.4.1", + "@jest/types": "^29.4.1", + "@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.4.0", + "jest-config": "^29.4.1", + "jest-haste-map": "^29.4.1", + "jest-message-util": "^29.4.1", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.4.1", + "jest-resolve-dependencies": "^29.4.1", + "jest-runner": "^29.4.1", + "jest-runtime": "^29.4.1", + "jest-snapshot": "^29.4.1", + "jest-util": "^29.4.1", + "jest-validate": "^29.4.1", + "jest-watcher": "^29.4.1", + "micromatch": "^4.0.4", + "pretty-format": "^29.4.1", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.4.1.tgz", + "integrity": "sha512-pJ14dHGSQke7Q3mkL/UZR9ZtTOxqskZaC91NzamEH4dlKRt42W+maRBXiw/LWkdJe+P0f/zDR37+SPMplMRlPg==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.4.1", + "@jest/types": "^29.4.1", + "@types/node": "*", + "jest-mock": "^29.4.1" + } + }, + "@jest/expect": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.4.1.tgz", + "integrity": "sha512-ZxKJP5DTUNF2XkpJeZIzvnzF1KkfrhEF6Rz0HGG69fHl6Bgx5/GoU3XyaeFYEjuuKSOOsbqD/k72wFvFxc3iTw==", + "dev": true, + "requires": { + "expect": "^29.4.1", + "jest-snapshot": "^29.4.1" + } + }, + "@jest/expect-utils": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.4.1.tgz", + "integrity": "sha512-w6YJMn5DlzmxjO00i9wu2YSozUYRBhIoJ6nQwpMYcBMtiqMGJm1QBzOf6DDgRao8dbtpDoaqLg6iiQTvv0UHhQ==", + "dev": true, + "requires": { + "jest-get-type": "^29.2.0" + } + }, + "@jest/fake-timers": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.4.1.tgz", + "integrity": "sha512-/1joI6rfHFmmm39JxNfmNAO3Nwm6Y0VoL5fJDy7H1AtWrD1CgRtqJbN9Ld6rhAkGO76qqp4cwhhxJ9o9kYjQMw==", + "dev": true, + "requires": { + "@jest/types": "^29.4.1", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.4.1", + "jest-mock": "^29.4.1", + "jest-util": "^29.4.1" + } + }, + "@jest/globals": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.4.1.tgz", + "integrity": "sha512-znoK2EuFytbHH0ZSf2mQK2K1xtIgmaw4Da21R2C/NE/+NnItm5mPEFQmn8gmF3f0rfOlmZ3Y3bIf7bFj7DHxAA==", + "dev": true, + "requires": { + "@jest/environment": "^29.4.1", + "@jest/expect": "^29.4.1", + "@jest/types": "^29.4.1", + "jest-mock": "^29.4.1" + } + }, + "@jest/reporters": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.4.1.tgz", + "integrity": "sha512-AISY5xpt2Xpxj9R6y0RF1+O6GRy9JsGa8+vK23Lmzdy1AYcpQn5ItX79wJSsTmfzPKSAcsY1LNt/8Y5Xe5LOSg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.4.1", + "@jest/test-result": "^29.4.1", + "@jest/transform": "^29.4.1", + "@jest/types": "^29.4.1", + "@jridgewell/trace-mapping": "^0.3.15", + "@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": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.4.1", + "jest-util": "^29.4.1", + "jest-worker": "^29.4.1", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + } + }, + "@jest/schemas": { + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.0.tgz", + "integrity": "sha512-0E01f/gOZeNTG76i5eWWSupvSHaIINrTie7vCyjiYFKgzNdyEGd12BUv4oNBFHOqlHDbtoJi3HrQ38KCC90NsQ==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.25.16" + } + }, + "@jest/source-map": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.2.0.tgz", + "integrity": "sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.4.1.tgz", + "integrity": "sha512-WRt29Lwt+hEgfN8QDrXqXGgCTidq1rLyFqmZ4lmJOpVArC8daXrZWkWjiaijQvgd3aOUj2fM8INclKHsQW9YyQ==", + "dev": true, + "requires": { + "@jest/console": "^29.4.1", + "@jest/types": "^29.4.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.4.1.tgz", + "integrity": "sha512-v5qLBNSsM0eHzWLXsQ5fiB65xi49A3ILPSFQKPXzGL4Vyux0DPZAIN7NAFJa9b4BiTDP9MBF/Zqc/QA1vuiJ0w==", + "dev": true, + "requires": { + "@jest/test-result": "^29.4.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.4.1", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.4.1.tgz", + "integrity": "sha512-5w6YJrVAtiAgr0phzKjYd83UPbCXsBRTeYI4BXokv9Er9CcrH9hfXL/crCvP2d2nGOcovPUnlYiLPFLZrkG5Hg==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.4.1", + "@jridgewell/trace-mapping": "^0.3.15", + "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.4.1", + "jest-regex-util": "^29.2.0", + "jest-util": "^29.4.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.0" + } + }, + "@jest/types": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.4.1.tgz", + "integrity": "sha512-zbrAXDUOnpJ+FMST2rV7QZOgec8rskg2zv8g2ajeqitp4tvZiyqTCYXANrKsM+ryj5o+LI+ZN2EgU9drrkiwSA==", + "dev": true, + "requires": { + "@jest/schemas": "^29.4.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@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 + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "@sinclair/typebox": { + "version": "0.25.21", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.21.tgz", + "integrity": "sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g==", + "dev": true + }, + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^2.0.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@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 + }, + "@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 + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "@types/babel__core": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", + "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@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, + "requires": { + "@types/node": "*" + } + }, + "@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 + }, + "@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, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@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, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz", + "integrity": "sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==", + "dev": true, + "requires": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", + "dev": true + }, + "@types/prettier": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", + "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "dev": true + }, + "@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 + }, + "@types/yargs": { + "version": "17.0.22", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", + "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@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 + }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + }, + "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 + }, + "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, + "requires": { + "type-fest": "^0.21.3" + } + }, + "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 + }, + "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" + } + }, + "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, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aws-cdk": { + "version": "2.62.2", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.62.2.tgz", + "integrity": "sha512-fACUQEdc43AfiqKKXUZLuHB2VADLU965hl50Fn6BhMemFmUsc+iS/GFfOwvfCUO3/iaRtsn/gk+dQ3zZ+snKRw==", + "dev": true, + "requires": { + "fsevents": "2.3.2" + } + }, + "aws-cdk-lib": { + "version": "2.62.2", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.62.2.tgz", + "integrity": "sha512-ynyoEFQckICFJzbUd89pWjol3GGbxRF05E8BCPEyy++vLHJZdqaJxRL4REl4lrdznnkb1kvxtBSGg4cOkR4o3w==", + "requires": { + "@aws-cdk/asset-awscli-v1": "^2.2.49", + "@aws-cdk/asset-kubectl-v20": "^2.1.1", + "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.38", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^9.1.0", + "ignore": "^5.2.4", + "jsonschema": "^1.4.1", + "minimatch": "^3.1.2", + "punycode": "^2.2.0", + "semver": "^7.3.8", + "yaml": "1.10.2" + }, + "dependencies": { + "@balena/dockerignore": { + "version": "1.0.2", + "bundled": true + }, + "at-least-node": { + "version": "1.0.0", + "bundled": true + }, + "balanced-match": { + "version": "1.0.2", + "bundled": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "case": { + "version": "1.6.3", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "fs-extra": { + "version": "9.1.0", + "bundled": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "bundled": true + }, + "ignore": { + "version": "5.2.4", + "bundled": true + }, + "jsonfile": { + "version": "6.1.0", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonschema": { + "version": "1.4.1", + "bundled": true + }, + "lru-cache": { + "version": "6.0.0", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "punycode": { + "version": "2.2.0", + "bundled": true + }, + "semver": { + "version": "7.3.8", + "bundled": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "bundled": true + }, + "yallist": { + "version": "4.0.0", + "bundled": true + }, + "yaml": { + "version": "1.10.2", + "bundled": true + } + } + }, + "babel-jest": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.4.1.tgz", + "integrity": "sha512-xBZa/pLSsF/1sNpkgsiT3CmY7zV1kAsZ9OxxtrFqYucnOuRftXAfcJqcDVyOPeN4lttWTwhLdu0T9f8uvoPEUg==", + "dev": true, + "requires": { + "@jest/transform": "^29.4.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.4.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "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, + "requires": { + "@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" + } + }, + "babel-plugin-jest-hoist": { + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.0.tgz", + "integrity": "sha512-a/sZRLQJEmsmejQ2rPEUe35nO1+C9dc9O1gplH1SXmJxveQSRUYdBk8yGZG/VOUuZs1u2aHZJusEGoRMbhhwCg==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "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, + "requires": { + "@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" + } + }, + "babel-preset-jest": { + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.4.0.tgz", + "integrity": "sha512-fUB9vZflUSM3dO/6M2TCAepTzvA4VkOvl67PjErcrQMGt9Eve7uazaeyCZ2th3UtI7ljpiBJES0F7A1vBRsLZA==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^29.4.0", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "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 + }, + "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, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.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" + } + }, + "browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + } + }, + "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, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "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, + "requires": { + "node-int64": "^0.4.0" + } + }, + "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==" + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001449", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001449.tgz", + "integrity": "sha512-CPB+UL9XMT/Av+pJxCKGhdx+yg1hzplvFJQlJ2n68PyQGMz9L/E2zCyLdOL8uasbouTUgnPl+y0tccI/se+BEw==", + "dev": true + }, + "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" + } + }, + "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 + }, + "ci-info": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", + "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "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, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "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 + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "constructs": { + "version": "10.1.236", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.1.236.tgz", + "integrity": "sha512-JBikdlmN5sKDjwZmPPoveNr7K6N7SQLBEJY83r3Px86CGyMu0YXtaYpjFg2fQd9zs8X77QnyNzb54SKiicSD9g==" + }, + "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 + }, + "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 + }, + "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, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "deepmerge": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", + "dev": true + }, + "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 + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diff-sequences": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz", + "integrity": "sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true + }, + "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 + }, + "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, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "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 + }, + "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 + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "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" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true + }, + "expect": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.4.1.tgz", + "integrity": "sha512-OKrGESHOaMxK3b6zxIq9SOW8kEXztKff/Dvg88j4xIJxur1hspEbedVkR3GpHe5LO+WB2Qw7OWN0RMTdp6as5A==", + "dev": true, + "requires": { + "@jest/expect-utils": "^29.4.1", + "jest-get-type": "^29.2.0", + "jest-matcher-utils": "^29.4.1", + "jest-message-util": "^29.4.1", + "jest-util": "^29.4.1" + } + }, + "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 + }, + "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, + "requires": { + "bser": "2.1.1" + } + }, + "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" + } + }, + "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, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "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 + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "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" + } + }, + "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 + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "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 + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "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, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "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 + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "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 + }, + "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, + "requires": { + "@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" + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "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, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.4.1.tgz", + "integrity": "sha512-cknimw7gAXPDOmj0QqztlxVtBVCw2lYY9CeIE5N6kD+kET1H4H79HSNISJmijb1HF+qk+G+ploJgiDi5k/fRlg==", + "dev": true, + "requires": { + "@jest/core": "^29.4.1", + "@jest/types": "^29.4.1", + "import-local": "^3.0.2", + "jest-cli": "^29.4.1" + }, + "dependencies": { + "jest-cli": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.4.1.tgz", + "integrity": "sha512-jz7GDIhtxQ37M+9dlbv5K+/FVcIo1O/b1sX3cJgzlQUf/3VG25nvuWzlDC4F1FLLzUThJeWLu8I7JF9eWpuURQ==", + "dev": true, + "requires": { + "@jest/core": "^29.4.1", + "@jest/test-result": "^29.4.1", + "@jest/types": "^29.4.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.4.1", + "jest-util": "^29.4.1", + "jest-validate": "^29.4.1", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + } + } + } + }, + "jest-changed-files": { + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.4.0.tgz", + "integrity": "sha512-rnI1oPxgFghoz32Y8eZsGJMjW54UlqT17ycQeCEktcxxwqqKdlj9afl8LNeO0Pbu+h2JQHThQP0BzS67eTRx4w==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "dependencies": { + "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, + "requires": { + "yocto-queue": "^0.1.0" + } + } + } + }, + "jest-circus": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.4.1.tgz", + "integrity": "sha512-v02NuL5crMNY4CGPHBEflLzl4v91NFb85a+dH9a1pUNx6Xjggrd8l9pPy4LZ1VYNRXlb+f65+7O/MSIbLir6pA==", + "dev": true, + "requires": { + "@jest/environment": "^29.4.1", + "@jest/expect": "^29.4.1", + "@jest/test-result": "^29.4.1", + "@jest/types": "^29.4.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.4.1", + "jest-matcher-utils": "^29.4.1", + "jest-message-util": "^29.4.1", + "jest-runtime": "^29.4.1", + "jest-snapshot": "^29.4.1", + "jest-util": "^29.4.1", + "p-limit": "^3.1.0", + "pretty-format": "^29.4.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "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, + "requires": { + "yocto-queue": "^0.1.0" + } + } + } + }, + "jest-config": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.4.1.tgz", + "integrity": "sha512-g7p3q4NuXiM4hrS4XFATTkd+2z0Ml2RhFmFPM8c3WyKwVDNszbl4E7cV7WIx1YZeqqCtqbtTtZhGZWJlJqngzg==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.4.1", + "@jest/types": "^29.4.1", + "babel-jest": "^29.4.1", + "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.4.1", + "jest-environment-node": "^29.4.1", + "jest-get-type": "^29.2.0", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.4.1", + "jest-runner": "^29.4.1", + "jest-util": "^29.4.1", + "jest-validate": "^29.4.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.4.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "jest-diff": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.4.1.tgz", + "integrity": "sha512-uazdl2g331iY56CEyfbNA0Ut7Mn2ulAG5vUaEHXycf1L6IPyuImIxSz4F0VYBKi7LYIuxOwTZzK3wh5jHzASMw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.3.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.4.1" + } + }, + "jest-docblock": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.2.0.tgz", + "integrity": "sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.4.1.tgz", + "integrity": "sha512-QlYFiX3llJMWUV0BtWht/esGEz9w+0i7BHwODKCze7YzZzizgExB9MOfiivF/vVT0GSQ8wXLhvHXh3x2fVD4QQ==", + "dev": true, + "requires": { + "@jest/types": "^29.4.1", + "chalk": "^4.0.0", + "jest-get-type": "^29.2.0", + "jest-util": "^29.4.1", + "pretty-format": "^29.4.1" + } + }, + "jest-environment-node": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.4.1.tgz", + "integrity": "sha512-x/H2kdVgxSkxWAIlIh9MfMuBa0hZySmfsC5lCsWmWr6tZySP44ediRKDUiNggX/eHLH7Cd5ZN10Rw+XF5tXsqg==", + "dev": true, + "requires": { + "@jest/environment": "^29.4.1", + "@jest/fake-timers": "^29.4.1", + "@jest/types": "^29.4.1", + "@types/node": "*", + "jest-mock": "^29.4.1", + "jest-util": "^29.4.1" + } + }, + "jest-get-type": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", + "integrity": "sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==", + "dev": true + }, + "jest-haste-map": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.4.1.tgz", + "integrity": "sha512-imTjcgfVVTvg02khXL11NNLTx9ZaofbAWhilrMg/G8dIkp+HYCswhxf0xxJwBkfhWb3e8dwbjuWburvxmcr58w==", + "dev": true, + "requires": { + "@jest/types": "^29.4.1", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.2.0", + "jest-util": "^29.4.1", + "jest-worker": "^29.4.1", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-leak-detector": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.4.1.tgz", + "integrity": "sha512-akpZv7TPyGMnH2RimOCgy+hPmWZf55EyFUvymQ4LMsQP8xSPlZumCPtXGoDhFNhUE2039RApZkTQDKU79p/FiQ==", + "dev": true, + "requires": { + "jest-get-type": "^29.2.0", + "pretty-format": "^29.4.1" + } + }, + "jest-matcher-utils": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.4.1.tgz", + "integrity": "sha512-k5h0u8V4nAEy6lSACepxL/rw78FLDkBnXhZVgFneVpnJONhb2DhZj/Gv4eNe+1XqQ5IhgUcqj745UwH0HJmMnA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.4.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.4.1" + } + }, + "jest-message-util": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.4.1.tgz", + "integrity": "sha512-H4/I0cXUaLeCw6FM+i4AwCnOwHRgitdaUFOdm49022YD5nfyr8C/DrbXOBEyJaj+w/y0gGJ57klssOaUiLLQGQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.4.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.4.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.4.1.tgz", + "integrity": "sha512-MwA4hQ7zBOcgVCVnsM8TzaFLVUD/pFWTfbkY953Y81L5ret3GFRZtmPmRFAjKQSdCKoJvvqOu6Bvfpqlwwb0dQ==", + "dev": true, + "requires": { + "@jest/types": "^29.4.1", + "@types/node": "*", + "jest-util": "^29.4.1" + } + }, + "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 + }, + "jest-regex-util": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.2.0.tgz", + "integrity": "sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA==", + "dev": true + }, + "jest-resolve": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.4.1.tgz", + "integrity": "sha512-j/ZFNV2lm9IJ2wmlq1uYK0Y/1PiyDq9g4HEGsNTNr3viRbJdV+8Lf1SXIiLZXFvyiisu0qUyIXGBnw+OKWkJwQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.4.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.4.1", + "jest-validate": "^29.4.1", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.1.tgz", + "integrity": "sha512-Y3QG3M1ncAMxfjbYgtqNXC5B595zmB6e//p/qpA/58JkQXu/IpLDoLeOa8YoYfsSglBKQQzNUqtfGJJT/qLmJg==", + "dev": true, + "requires": { + "jest-regex-util": "^29.2.0", + "jest-snapshot": "^29.4.1" + } + }, + "jest-runner": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.4.1.tgz", + "integrity": "sha512-8d6XXXi7GtHmsHrnaqBKWxjKb166Eyj/ksSaUYdcBK09VbjPwIgWov1VwSmtupCIz8q1Xv4Qkzt/BTo3ZqiCeg==", + "dev": true, + "requires": { + "@jest/console": "^29.4.1", + "@jest/environment": "^29.4.1", + "@jest/test-result": "^29.4.1", + "@jest/transform": "^29.4.1", + "@jest/types": "^29.4.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.2.0", + "jest-environment-node": "^29.4.1", + "jest-haste-map": "^29.4.1", + "jest-leak-detector": "^29.4.1", + "jest-message-util": "^29.4.1", + "jest-resolve": "^29.4.1", + "jest-runtime": "^29.4.1", + "jest-util": "^29.4.1", + "jest-watcher": "^29.4.1", + "jest-worker": "^29.4.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "dependencies": { + "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, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "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, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "jest-runtime": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.4.1.tgz", + "integrity": "sha512-UXTMU9uKu2GjYwTtoAw5rn4STxWw/nadOfW7v1sx6LaJYa3V/iymdCLQM6xy3+7C6mY8GfX22vKpgxY171UIoA==", + "dev": true, + "requires": { + "@jest/environment": "^29.4.1", + "@jest/fake-timers": "^29.4.1", + "@jest/globals": "^29.4.1", + "@jest/source-map": "^29.2.0", + "@jest/test-result": "^29.4.1", + "@jest/transform": "^29.4.1", + "@jest/types": "^29.4.1", + "@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.4.1", + "jest-message-util": "^29.4.1", + "jest-mock": "^29.4.1", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.4.1", + "jest-snapshot": "^29.4.1", + "jest-util": "^29.4.1", + "semver": "^7.3.5", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "dependencies": { + "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, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "jest-snapshot": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.4.1.tgz", + "integrity": "sha512-l4iV8EjGgQWVz3ee/LR9sULDk2pCkqb71bjvlqn+qp90lFwpnulHj4ZBT8nm1hA1C5wowXLc7MGnw321u0tsYA==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.4.1", + "@jest/transform": "^29.4.1", + "@jest/types": "^29.4.1", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.4.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.4.1", + "jest-get-type": "^29.2.0", + "jest-haste-map": "^29.4.1", + "jest-matcher-utils": "^29.4.1", + "jest-message-util": "^29.4.1", + "jest-util": "^29.4.1", + "natural-compare": "^1.4.0", + "pretty-format": "^29.4.1", + "semver": "^7.3.5" + }, + "dependencies": { + "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, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "jest-util": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.4.1.tgz", + "integrity": "sha512-bQy9FPGxVutgpN4VRc0hk6w7Hx/m6L53QxpDreTZgJd9gfx/AV2MjyPde9tGyZRINAUrSv57p2inGBu2dRLmkQ==", + "dev": true, + "requires": { + "@jest/types": "^29.4.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.4.1.tgz", + "integrity": "sha512-qNZXcZQdIQx4SfUB/atWnI4/I2HUvhz8ajOSYUu40CSmf9U5emil8EDHgE7M+3j9/pavtk3knlZBDsgFvv/SWw==", + "dev": true, + "requires": { + "@jest/types": "^29.4.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.2.0", + "leven": "^3.1.0", + "pretty-format": "^29.4.1" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.4.1.tgz", + "integrity": "sha512-vFOzflGFs27nU6h8dpnVRER3O2rFtL+VMEwnG0H3KLHcllLsU8y9DchSh0AL/Rg5nN1/wSiQ+P4ByMGpuybaVw==", + "dev": true, + "requires": { + "@jest/test-result": "^29.4.1", + "@jest/types": "^29.4.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.4.1", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.4.1.tgz", + "integrity": "sha512-O9doU/S1EBe+yp/mstQ0VpPwpv0Clgn68TkNwGxL6/usX/KUW9Arnn4ag8C3jc6qHcXznhsT5Na1liYzAsuAbQ==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.4.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "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, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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 + }, + "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, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "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 + }, + "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 + }, + "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, + "requires": { + "p-locate": "^4.1.0" + } + }, + "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 + }, + "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, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "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 + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "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 + }, + "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" + } + }, + "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 + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "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 + }, + "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-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-releases": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.9.tgz", + "integrity": "sha512-2xfmOrRkGogbTK9R6Leda0DGiXeY3p2NJpy4+gNCffdUvV6mdEJnaDEic1i3Ec2djAo8jWYoJMR5PB0MSMpxUA==", + "dev": true + }, + "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 + }, + "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, + "requires": { + "path-key": "^3.0.0" + } + }, + "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, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "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, + "requires": { + "p-try": "^2.0.0" + } + }, + "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, + "requires": { + "p-limit": "^2.2.0" + } + }, + "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 + }, + "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, + "requires": { + "@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" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true + }, + "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, + "requires": { + "find-up": "^4.0.0" + } + }, + "pretty-format": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.1.tgz", + "integrity": "sha512-dt/Z761JUVsrIKaY215o1xQJBGlSmTx/h4cSqXqjHLnU1+Kt+mavVE7UgqJJO5ukx5HjSswHfmXz4LjS2oIJfg==", + "dev": true, + "requires": { + "@jest/schemas": "^29.4.0", + "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 + } + } + }, + "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, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "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 + }, + "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 + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "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, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "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 + }, + "resolve.exports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.0.tgz", + "integrity": "sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "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, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "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 + }, + "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 + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "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==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "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" + }, + "dependencies": { + "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 + } + } + }, + "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, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "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, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "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, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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" + } + }, + "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 + }, + "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, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "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 + }, + "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" + } + }, + "ts-jest": { + "version": "29.0.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.5.tgz", + "integrity": "sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==", + "dev": true, + "requires": { + "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.x", + "yargs-parser": "^21.0.1" + }, + "dependencies": { + "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, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "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, + "requires": { + "@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" + } + }, + "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 + }, + "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 + }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "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 + }, + "v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "dependencies": { + "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 + } + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "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 + }, + "write-file-atomic": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.0.tgz", + "integrity": "sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "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 + } + } +} diff --git a/opensearch-service-domain-cdk/package.json b/opensearch-service-domain-cdk/package.json new file mode 100644 index 000000000..e5fd32f86 --- /dev/null +++ b/opensearch-service-domain-cdk/package.json @@ -0,0 +1,27 @@ +{ + "name": "opensearch-service-domain-cdk", + "version": "0.1.0", + "bin": { + "opensearch-service-domain-cdk": "bin/opensearch-service-domain-cdk.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "^29.2.5", + "@types/node": "18.11.18", + "jest": "^29.3.1", + "ts-jest": "^29.0.3", + "aws-cdk": "2.62.2", + "ts-node": "^10.9.1", + "typescript": "~4.9.4" + }, + "dependencies": { + "aws-cdk-lib": "2.62.2", + "constructs": "^10.0.0", + "source-map-support": "^0.5.21" + } +} diff --git a/opensearch-service-domain-cdk/test/opensearch-service-domain-cdk.test.ts b/opensearch-service-domain-cdk/test/opensearch-service-domain-cdk.test.ts new file mode 100644 index 000000000..acd72f707 --- /dev/null +++ b/opensearch-service-domain-cdk/test/opensearch-service-domain-cdk.test.ts @@ -0,0 +1,17 @@ +// import * as cdk from 'aws-cdk-lib'; +// import { Template } from 'aws-cdk-lib/assertions'; +// import * as OpensearchServiceDomainCdk from '../lib/opensearch-service-domain-cdk-stack'; + +// example test. To run these tests, uncomment this file along with the +// example resource in lib/opensearch-service-domain-cdk-stack.ts +test('SQS Queue Created', () => { +// const app = new cdk.App(); +// // WHEN +// const stack = new OpensearchServiceDomainCdk.OpensearchServiceDomainCdkStack(app, 'MyTestStack'); +// // THEN +// const template = Template.fromStack(stack); + +// template.hasResourceProperties('AWS::SQS::Queue', { +// VisibilityTimeout: 300 +// }); +}); diff --git a/opensearch-service-domain-cdk/tsconfig.json b/opensearch-service-domain-cdk/tsconfig.json new file mode 100644 index 000000000..fc44377a1 --- /dev/null +++ b/opensearch-service-domain-cdk/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020" + ], + "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 be3be3bda5c8dd9c9c95e483ee3f516acd61ffbc Mon Sep 17 00:00:00 2001 From: Tanner Lewis Date: Thu, 2 Feb 2023 10:50:18 -0500 Subject: [PATCH 2/8] Remove unrelated test Signed-off-by: Tanner Lewis --- .../test/opensearch-service-domain-cdk.test.ts | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 opensearch-service-domain-cdk/test/opensearch-service-domain-cdk.test.ts diff --git a/opensearch-service-domain-cdk/test/opensearch-service-domain-cdk.test.ts b/opensearch-service-domain-cdk/test/opensearch-service-domain-cdk.test.ts deleted file mode 100644 index acd72f707..000000000 --- a/opensearch-service-domain-cdk/test/opensearch-service-domain-cdk.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -// import * as cdk from 'aws-cdk-lib'; -// import { Template } from 'aws-cdk-lib/assertions'; -// import * as OpensearchServiceDomainCdk from '../lib/opensearch-service-domain-cdk-stack'; - -// example test. To run these tests, uncomment this file along with the -// example resource in lib/opensearch-service-domain-cdk-stack.ts -test('SQS Queue Created', () => { -// const app = new cdk.App(); -// // WHEN -// const stack = new OpensearchServiceDomainCdk.OpensearchServiceDomainCdkStack(app, 'MyTestStack'); -// // THEN -// const template = Template.fromStack(stack); - -// template.hasResourceProperties('AWS::SQS::Queue', { -// VisibilityTimeout: 300 -// }); -}); From 535f4ed49f1907bc029e17b2c72a1e5be521a5f4 Mon Sep 17 00:00:00 2001 From: Tanner Lewis Date: Wed, 8 Feb 2023 22:23:44 -0500 Subject: [PATCH 3/8] MIGRATIONS-876: Add all options for Domain CDK as configurable from context --- opensearch-service-domain-cdk/README.md | 49 ++++- opensearch-service-domain-cdk/bin/app.ts | 6 +- .../cdk.context.json | 51 ++++- .../opensearch-service-domain-cdk-stack.ts | 185 +++++++++++++++++- .../lib/stack-composer.ts | 175 +++++++++++++++-- 5 files changed, 426 insertions(+), 40 deletions(-) diff --git a/opensearch-service-domain-cdk/README.md b/opensearch-service-domain-cdk/README.md index 2edca139a..29aa55fda 100644 --- a/opensearch-service-domain-cdk/README.md +++ b/opensearch-service-domain-cdk/README.md @@ -2,6 +2,10 @@ ### Getting Started +If this is your first time using CDK in this region, will need to `cdk bootstrap` to setup required CDK resources for deployment + +Also ensure you have configured the desired [AWS credentials](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_prerequisites), as these will dictate the region and account used for deployment + ### Deploying your Domain Stack Before deploying your Domain stack you should fill in any desired context parameters that will dictate the composition of your OpenSearch Service Domain @@ -10,10 +14,53 @@ This can be accomplished by simply filling in the values in the `cdk.context.jso As well as by passing these context options as options in the CDK CLI ``` -cdk deploy --c domainName='cdk-os-service-domain' --c engineVersion='OS_1_3_6' --c dataNodeType='r6g.large.search' --c dataNodeCount=1 +cdk deploy --c domainName='cdk-os-service-domain' --c engineVersion="OS_1_3_6" --c dataNodeType="r6g.large.search" --c dataNodeCount=1 ``` * Note that these context parameters can also be passed to `cdk synth` and `cdk bootstrap` commands + +### Configuration Options + +The available configuration options are listed below. The vast majority of these options do not need to be provided, with only `domainName` and `engineVersion` being required. +All non-required options can be removed from the `cdk.context.json` (or not passed by the CLI) or provided as an empty string `""`, in each of these cases the option will be allocated with the CDK Domain default value + +Additional context on some of these options, can also be found in the Domain construct [documentation](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_opensearchservice.Domain.html) + +**It should be noted that limited testing has been conducted solely in the us-east-1 region, and some items like instance-type might be biased** + +| Name | Required | Type | Example | Description | +|------------------------------------|----------|--------|-----------------------------------------------------------------------------|:----------------------------------------------| +| domainName | true | string | cdk-os-service-domain | Name to use for the OpenSearch Service Domain | + +#### Future options +These options are not currently achievable by the CDK Domain construct alone, although possible through CloudFormation or the AWS SDK, and are planned to be added to this code base +``` +"coldStorageEnabled": "X", +"anonymousAuthEnabled": "X", +"anonymousAuthDisableDate": "X", +"samlEnabled": "X", +"samlIdentityProviderEntityId": "X", +"samlIdentityProviderMetadataContent": "X", +"samlMasterBackendRole": "X", +"samlMasterUserName": "X", +"samlRolesKey": "X", +"samlSessionTimeoutMinutes": "X", +"samlSubjectKey": "X", +"ebsThroughput": "X", +"tags": "X", +``` + + +Some configuration options (listed below) which enable/disable specific features do not exist in the current native CDK Domain construct. These options are inferred based on the presence or absence of related fields (i.e. if dedicatedMasterNodeCount is set to 1 it is +inferred that dedicated master nodes should be enabled). These options are normally disabled by default, allowing for this inference. +``` +"dedicatedMasterNodeEnabled": "X", +"warmNodeEnabled": "X", +"fineGrainedAccessControlEnabled": "X", +"internalUserDatabaseEnabled": "X", +"cognitoEnabled": "X", +"customEndpointEnabled": "X", +``` ### Useful CDK commands * `npm run build` compile typescript to js diff --git a/opensearch-service-domain-cdk/bin/app.ts b/opensearch-service-domain-cdk/bin/app.ts index 34ab40274..7c69dabcc 100644 --- a/opensearch-service-domain-cdk/bin/app.ts +++ b/opensearch-service-domain-cdk/bin/app.ts @@ -4,7 +4,11 @@ import * as cdk from 'aws-cdk-lib'; import {StackComposer} from "../lib/stack-composer"; const app = new cdk.App(); +const stage = "dev" +const account = process.env.CDK_DEFAULT_ACCOUNT +const region = process.env.CDK_DEFAULT_REGION new StackComposer(app, { - env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, + env: { account: account, region: region }, + stackName: `OpenSearchServiceDomainCDKStack-${stage}-${region}`, description: "This stack contains resources to create/manage an OpenSearch Service domain" }); \ No newline at end of file diff --git a/opensearch-service-domain-cdk/cdk.context.json b/opensearch-service-domain-cdk/cdk.context.json index 0173cc121..027db50c9 100644 --- a/opensearch-service-domain-cdk/cdk.context.json +++ b/opensearch-service-domain-cdk/cdk.context.json @@ -1,12 +1,47 @@ { "domainName": "cdk-os-service-domain", "engineVersion": "OS_1.3", - "dataNodeType": "r6g.large.search", - "dataNodeCount": 1, - "dedicatedMasterNodeType": "r6g.large.search", - "dedicatedMasterNodeCount": 3, - "warmNodeType": "ultrawarm1.medium.search", - "warmNodeCount": 2, - "coldStorageEnabled": false, - "zoneAwarenessAvailabilityZoneCount": 1 + "dataNodeType": "", + "dataNodeCount": "", + "dedicatedManagerNodeType": "", + "dedicatedManagerNodeCount": "", + "warmNodeType": "", + "warmNodeCount": "", + "zoneAwarenessEnabled": "", + "zoneAwarenessAvailabilityZoneCount": "", + "accessPolicies": "", + "advancedOptions": "", + "useUnsignedBasicAuth": "", + "fineGrainedManagerUserARN": "", + "fineGrainedManagerUserName": "", + "fineGrainedManagerUserSecretManagerKeyARN": "", + "cognitoIdentityPoolId": "", + "cognitoRoleARN": "", + "cognitoUserPoolId": "", + "customEndpointDomainName": "", + "customEndpointCertificateARN": "", + "customEndpointHostedZoneId": "", + "enforceHTTPS": "", + "tlsSecurityPolicy": "", + "ebsEnabled": "", + "ebsIops": "", + "ebsVolumeSize": "", + "ebsVolumeType": "", + "encryptionAtRestEnabled": "", + "encryptionAtRestKmsKeyARN": "", + "loggingAppLogEnabled": "", + "loggingAppLogGroupARN": "", + "loggingAuditLogEnabled": "", + "loggingAuditLogGroupARN": "", + "loggingSlowIndexLogEnabled": "", + "loggingSlowIndexLogGroupARN": "", + "loggingSlowSearchLogEnabled": "", + "loggingSlowSearchLogGroupARN": "", + "nodeToNodeEncryptionEnabled": "", + "snapshotAutomatedStartHour": "", + "vpcId": "", + "vpcSecurityGroupIds": "", + "vpcSubnetIds": "", + "enableVersionUpgrade": "", + "domainRemovalPolicy": "" } diff --git a/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts b/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts index fa45b8c4f..9d7342528 100644 --- a/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts +++ b/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts @@ -1,18 +1,71 @@ import { Construct } from 'constructs'; -import { EbsDeviceVolumeType } from "aws-cdk-lib/aws-ec2"; -import { Domain, EngineVersion } from "aws-cdk-lib/aws-opensearchservice"; -import {Stack, StackProps} from "aws-cdk-lib"; +import { + EbsDeviceVolumeType, + ISecurityGroup, + ISubnet, + IVpc, + SecurityGroup, + Subnet, + SubnetSelection, + Vpc +} from "aws-cdk-lib/aws-ec2"; +import {CustomEndpointOptions, Domain, EngineVersion, TLSSecurityPolicy} from "aws-cdk-lib/aws-opensearchservice"; +import {RemovalPolicy, SecretValue, Stack, StackProps} from "aws-cdk-lib"; +import {IKey, Key} from "aws-cdk-lib/aws-kms"; +import {IRole, PolicyStatement, Role} from "aws-cdk-lib/aws-iam"; +import {CognitoOptions} from "aws-cdk-lib/aws-opensearchservice/lib/domain"; +import {Certificate, ICertificate} from "aws-cdk-lib/aws-certificatemanager"; +import {HostedZone, IHostedZone} from "aws-cdk-lib/aws-route53"; +import {ILogGroup, LogGroup} from "aws-cdk-lib/aws-logs"; +import {Secret} from "aws-cdk-lib/aws-secretsmanager"; export interface opensearchServiceDomainCdkProps extends StackProps{ readonly version: EngineVersion, - readonly domainName?: string, + readonly domainName: string, + readonly advancedOptions?: { [key: string]: (string) }, + readonly accessPolicies?: PolicyStatement[], + readonly useUnsignedBasicAuth?: boolean, readonly dataNodeInstanceType?: string, readonly dataNodes?: number, - readonly masterNodeInstanceType?: string, - readonly masterNodes?: number, + readonly dedicatedManagerNodeType?: string, + readonly dedicatedManagerNodeCount?: number, readonly warmInstanceType?: string, readonly warmNodes?: number + readonly zoneAwarenessEnabled?: boolean, + readonly zoneAwarenessAvailabilityZoneCount?: number, + readonly fineGrainedManagerUserARN?: string, + readonly fineGrainedManagerUserName?: string, + readonly fineGrainedManagerUserSecretManagerKeyARN?: string, + readonly cognitoIdentityPoolId?: string, + readonly cognitoRoleARN?: string, + readonly cognitoUserPoolId?: string, + readonly nodeToNodeEncryptionEnabled?: boolean, + readonly encryptionAtRestEnabled?: boolean, + readonly encryptionAtRestKmsKeyARN?: string, + readonly customEndpointDomainName?: string, + readonly customEndpointCertificateARN?: string, + readonly customEndpointHostedZoneId?: string, + readonly enforceHTTPS?: boolean, + readonly tlsSecurityPolicy?: TLSSecurityPolicy, + readonly ebsEnabled?: boolean, + readonly ebsIops?: number, + readonly ebsVolumeSize?: number, + readonly ebsVolumeType?: EbsDeviceVolumeType, + readonly appLogEnabled?: boolean, + readonly appLogGroup?: string, + readonly auditLogEnabled?: boolean, + readonly auditLogGroup?: string, + readonly slowIndexLogEnabled?: boolean, + readonly slowIndexLogGroup?: string, + readonly slowSearchLogEnabled?: boolean, + readonly slowSearchLogGroup?: string, + readonly snapshotAutomatedStartHour?: number, + readonly vpcId?: string, + readonly vpcSubnetIds?: string[], + readonly vpcSecurityGroupIds?: string[], + readonly enableVersionUpgrade?: boolean, + readonly domainRemovalPolicy?: RemovalPolicy } @@ -22,17 +75,131 @@ export class OpensearchServiceDomainCdkStack extends Stack { // The code that defines your stack goes here + // Retrieve existing account resources if defined + const earKmsKey: IKey|undefined = props.encryptionAtRestKmsKeyARN && props.encryptionAtRestEnabled ? + Key.fromKeyArn(this, "earKey", props.encryptionAtRestKmsKeyARN) : undefined + + const managerUserSecret: SecretValue|undefined = props.fineGrainedManagerUserSecretManagerKeyARN ? + Secret.fromSecretCompleteArn(this, "managerSecret", props.fineGrainedManagerUserSecretManagerKeyARN).secretValue : undefined + + const cognitoRole: IRole|undefined = props.cognitoRoleARN ? + Role.fromRoleArn(this, "cognitoRole", props.cognitoRoleARN) : undefined + + const endpointCert: ICertificate|undefined = props.customEndpointCertificateARN ? + Certificate.fromCertificateArn(this, "endpointCert", props.customEndpointCertificateARN) : undefined + + const hostedZone: IHostedZone|undefined = props.customEndpointHostedZoneId ? + HostedZone.fromHostedZoneId(this, "hostedZone", props.customEndpointHostedZoneId) : undefined + + const appLG: ILogGroup|undefined = props.appLogGroup && props.appLogEnabled ? + LogGroup.fromLogGroupArn(this, "appLogGroup", props.appLogGroup) : undefined + + const auditLG: ILogGroup|undefined = props.auditLogGroup && props.auditLogEnabled ? + LogGroup.fromLogGroupArn(this, "auditLogGroup", props.auditLogGroup) : undefined + + const slowIndexLG: ILogGroup|undefined = props.slowIndexLogGroup && props.slowIndexLogEnabled ? + LogGroup.fromLogGroupArn(this, "slowIndexLogGroup", props.slowIndexLogGroup) : undefined + + const slowSearchLG: ILogGroup|undefined = props.slowSearchLogGroup && props.slowSearchLogEnabled ? + LogGroup.fromLogGroupArn(this, "slowSearchLogGroup", props.slowSearchLogGroup) : undefined + + const vpc: IVpc|undefined = props.vpcId ? + Vpc.fromLookup(this, "domainVPC", {vpcId: props.vpcId}) : undefined + + let vpcSubnets: SubnetSelection[]|undefined = undefined + if (props.vpcSubnetIds && vpc) { + const subnetIds = props.vpcSubnetIds + let subnetArray: ISubnet[] = [] + for (let i = 0; i < subnetIds.length; i++) { + subnetArray.push(Subnet.fromSubnetId(this, "subnet-" + i, subnetIds[i])) + } + const vpcSubnet = {subnets: subnetArray} + vpcSubnets = [vpcSubnet] + } + + let vpcSecurityGroups: ISecurityGroup[]|undefined = undefined + if (props.vpcSecurityGroupIds && vpc) { + const securityGroupIds = props.vpcSecurityGroupIds + let securityGroupArray: ISecurityGroup[] = [] + for (let i = 0; i < securityGroupIds.length; i++) { + securityGroupArray.push(SecurityGroup.fromLookupById(this, "security-group-" + i, securityGroupIds[i])) + } + vpcSecurityGroups = securityGroupArray + } + + // Map structures that require specific fields + let cognitoOptions: CognitoOptions|undefined = undefined + if (props.cognitoIdentityPoolId && cognitoRole && props.cognitoUserPoolId) { + cognitoOptions = { + identityPoolId: props.cognitoIdentityPoolId, + role: cognitoRole, + userPoolId: props.cognitoUserPoolId + } + } + + let endpointOptions: CustomEndpointOptions|undefined = undefined + if (props.customEndpointDomainName) { + endpointOptions = { + domainName: props.customEndpointDomainName, + certificate: endpointCert, + hostedZone: hostedZone + } + } + const domain = new Domain(this, 'Domain', { version: props.version, domainName: props.domainName, + advancedOptions: props.advancedOptions, + accessPolicies: props.accessPolicies, + useUnsignedBasicAuth: props.useUnsignedBasicAuth, capacity: { dataNodeInstanceType: props.dataNodeInstanceType, dataNodes: props.dataNodes, - masterNodeInstanceType: props.masterNodeInstanceType, - masterNodes: props.masterNodes, + masterNodeInstanceType: props.dedicatedManagerNodeType, + masterNodes: props.dedicatedManagerNodeCount, warmInstanceType: props.warmInstanceType, warmNodes: props.warmNodes - } + }, + zoneAwareness: { + enabled: props.zoneAwarenessEnabled, + availabilityZoneCount: props.zoneAwarenessAvailabilityZoneCount + }, + fineGrainedAccessControl: { + masterUserArn: props.fineGrainedManagerUserARN, + masterUserName: props.fineGrainedManagerUserName, + masterUserPassword: managerUserSecret + }, + cognitoDashboardsAuth: cognitoOptions, + nodeToNodeEncryption: props.nodeToNodeEncryptionEnabled, + encryptionAtRest: { + enabled: props.encryptionAtRestEnabled, + kmsKey: earKmsKey + }, + customEndpoint: endpointOptions, + enforceHttps: props.enforceHTTPS, + tlsSecurityPolicy: props.tlsSecurityPolicy, + ebs: { + enabled: props.ebsEnabled, + iops: props.ebsIops, + volumeSize: props.ebsVolumeSize, + volumeType: props.ebsVolumeType + }, + automatedSnapshotStartHour: props.snapshotAutomatedStartHour, + logging: { + appLogEnabled: props.appLogEnabled, + appLogGroup: appLG, + auditLogEnabled: props.auditLogEnabled, + auditLogGroup: auditLG, + slowIndexLogEnabled: props.slowIndexLogEnabled, + slowIndexLogGroup: slowIndexLG, + slowSearchLogEnabled: props.slowSearchLogEnabled, + slowSearchLogGroup: slowSearchLG + }, + vpc: vpc, + vpcSubnets: vpcSubnets, + securityGroups: vpcSecurityGroups, + enableVersionUpgrade: props.enableVersionUpgrade, + removalPolicy: props.domainRemovalPolicy }); } } diff --git a/opensearch-service-domain-cdk/lib/stack-composer.ts b/opensearch-service-domain-cdk/lib/stack-composer.ts index 7df1126ea..a422c4632 100644 --- a/opensearch-service-domain-cdk/lib/stack-composer.ts +++ b/opensearch-service-domain-cdk/lib/stack-composer.ts @@ -1,7 +1,9 @@ -import { Construct } from 'constructs'; -import { Stack, StackProps } from 'aws-cdk-lib'; +import {Construct} from 'constructs'; +import {RemovalPolicy, StackProps} from 'aws-cdk-lib'; import {OpensearchServiceDomainCdkStack} from "./opensearch-service-domain-cdk-stack"; -import {EngineVersion} from "aws-cdk-lib/aws-opensearchservice"; +import {EngineVersion, TLSSecurityPolicy} from "aws-cdk-lib/aws-opensearchservice"; +import {EbsDeviceVolumeType} from "aws-cdk-lib/aws-ec2"; +import {PolicyStatement} from "aws-cdk-lib/aws-iam"; export class StackComposer { @@ -9,15 +11,59 @@ export class StackComposer { let version: EngineVersion - const domainName: string = scope.node.tryGetContext('domainName') - const dataNodeType: string = scope.node.tryGetContext('dataNodeType') - const dataNodeCount: number = scope.node.tryGetContext('dataNodeCount') - const dedicatedMasterNodeType: string = scope.node.tryGetContext('dedicatedMasterNodeType') - const dedicatedMasterNodeCount: number = scope.node.tryGetContext('dedicatedMasterNodeCount') - const warmNodeType: string = scope.node.tryGetContext('warmNodeType') - const warmNodeCount: number = scope.node.tryGetContext('warmNodeCount') + const domainName = getContextForType('domainName', 'string') + const dataNodeType = getContextForType('dataNodeType', 'string') + const dataNodeCount = getContextForType('dataNodeCount', 'number') + const dedicatedManagerNodeType = getContextForType('dedicatedManagerNodeType', 'string') + const dedicatedManagerNodeCount = getContextForType('dedicatedManagerNodeCount', 'number') + const warmNodeType = getContextForType('warmNodeType', 'string') + const warmNodeCount = getContextForType('warmNodeCount', 'number') + const zoneAwarenessEnabled = getContextForType('zoneAwarenessEnabled', 'boolean') + const zoneAwarenessAvailabilityZoneCount = getContextForType('zoneAwarenessAvailabilityZoneCount', 'number') + const advancedOptions = getContextForType('advancedOptions', 'object') + const useUnsignedBasicAuth = getContextForType('useUnsignedBasicAuth', 'boolean') + const fineGrainedManagerUserARN = getContextForType('fineGrainedManagerUserARN', 'string') + const fineGrainedManagerUserName = getContextForType('fineGrainedManagerUserName', 'string') + const fineGrainedManagerUserSecretManagerKeyARN = getContextForType('fineGrainedManagerUserSecretManagerKeyARN', 'string') + const cognitoIdentityPoolId = getContextForType('cognitoIdentityPoolId', 'string') + const cognitoRoleARN = getContextForType('cognitoRoleARN', 'string') + const cognitoUserPoolId = getContextForType('cognitoUserPoolId', 'string') + const customEndpointDomainName = getContextForType('customEndpointDomainName', 'string') + const customEndpointCertificateARN = getContextForType('customEndpointCertificateARN', 'string') + const customEndpointHostedZoneId = getContextForType('customEndpointHostedZoneId', 'string') + const enforceHTTPS = getContextForType('enforceHTTPS', 'boolean') + const noneToNodeEncryptionEnabled = getContextForType('nodeToNodeEncryptionEnabled', 'boolean') + const encryptionAtRestEnabled = getContextForType('encryptionAtRestEnabled', 'boolean') + const encryptionAtRestKmsKeyARN = getContextForType("encryptionAtRestKmsKeyARN", 'string') + const ebsEnabled = getContextForType('ebsEnabled', 'boolean') + const ebsIops = getContextForType('ebsIops', 'number') + const ebsVolumeSize = getContextForType('ebsVolumeSize', 'number') + const loggingAppLogEnabled = getContextForType('loggingAppLogEnabled', 'boolean') + const loggingAppLogGroupARN = getContextForType('loggingAppLogGroupARN', 'string') + const loggingAuditLogEnabled = getContextForType('loggingAuditLogEnabled', 'boolean') + const loggingAuditLogGroupARN = getContextForType('loggingAuditLogGroupARN', 'string') + const loggingSlowIndexLogEnabled = getContextForType('loggingSlowIndexLogEnabled', 'boolean') + const loggingSlowIndexLogGroupARN = getContextForType('loggingSlowIndexLogGroupARN', 'string') + const loggingSlowSearchLogEnabled = getContextForType('loggingSlowSearchLogEnabled', 'boolean') + const loggingSlowSearchLogGroupARN = getContextForType('loggingSlowSearchLogGroupARN', 'string') + const snapshotAutomatedStartHour = getContextForType('snapshotAutomatedStartHour', 'number') + const vpcId = getContextForType('vpcId', 'string') + const vpcSecurityGroupIds = getContextForType('vpcSecurityGroupIds', 'object') + const vpcSubnetIds = getContextForType('vpcSubnetIds', 'object') + const enableVersionUpgrade = getContextForType('enableVersionUpgrade', 'boolean') - const engineVersion: string = scope.node.tryGetContext('engineVersion') + // Check for partially configured features + if ((cognitoIdentityPoolId || cognitoRoleARN || cognitoUserPoolId) && + !(cognitoIdentityPoolId && cognitoRoleARN && cognitoUserPoolId)) { + throw new Error("All Cognito values [cognitoIdentityPoolId, cognitoRoleARN, cognitoUserPoolId] are " + + "required if using Cognito authentication") + } + + if ((customEndpointCertificateARN || customEndpointHostedZoneId) && !(customEndpointDomainName)) { + throw new Error("The 'customEndpointDomainName' value is required when configuring a custom endpoint") + } + + const engineVersion = getContextForType('engineVersion', 'string') if (engineVersion.startsWith("OS_")) { // Will accept a period delimited version string (i.e. 1.3) and return a proper EngineVersion version = EngineVersion.openSearch(engineVersion.substring(3)) @@ -29,20 +75,107 @@ export class StackComposer { throw new Error("Engine version is not present or does not match the expected format, i.e. OS_1.3 or ES_7.9") } - const opensearchStack = new OpensearchServiceDomainCdkStack(scope, 'OpenSearchServiceDomainCDKStack', { + const accessPolicyJson = getContextForType('accessPolicies', 'object') + const accessPolicies = accessPolicyJson ? parseAccessPolicies(accessPolicyJson) : undefined + + const tlsSecurityPolicyName = getContextForType('tlsSecurityPolicy', 'string') + const tlsSecurityPolicy: TLSSecurityPolicy|undefined = tlsSecurityPolicyName ? TLSSecurityPolicy[tlsSecurityPolicyName as keyof typeof TLSSecurityPolicy] : undefined + if (tlsSecurityPolicyName && !tlsSecurityPolicy) { + throw new Error("Provided tlsSecurityPolicy does not match a selectable option, i.e. 'TLS_1_2'") + } + + const ebsVolumeTypeName = getContextForType('ebsVolumeType', 'string') + const ebsVolumeType: EbsDeviceVolumeType|undefined = ebsVolumeTypeName ? EbsDeviceVolumeType[ebsVolumeTypeName as keyof typeof EbsDeviceVolumeType] : undefined + if (ebsVolumeTypeName && !ebsVolumeType) { + throw new Error("Provided ebsVolumeType does not match a selectable option, i.e. 'GP3'") + } + + const domainRemovalPolicyName = getContextForType('domainRemovalPolicy', 'string') + const domainRemovalPolicy = domainRemovalPolicyName ? RemovalPolicy[domainRemovalPolicyName as keyof typeof RemovalPolicy] : undefined + if (domainRemovalPolicyName && !domainRemovalPolicy) { + throw new Error("Provided domainRemovalPolicy does not match a selectable option, i.e. 'RETAIN'") + } + + const opensearchStack = new OpensearchServiceDomainCdkStack(scope, 'opensearchDomainStack', { version: version, - domainName: getValidOrUndef(domainName), - dataNodeInstanceType: getValidOrUndef(dataNodeType), - dataNodes: getValidOrUndef(dataNodeCount), - masterNodeInstanceType: getValidOrUndef(dedicatedMasterNodeType), - masterNodes: getValidOrUndef(dedicatedMasterNodeCount), - warmInstanceType: getValidOrUndef(warmNodeType), - warmNodes: getValidOrUndef(warmNodeCount), + domainName: domainName, + advancedOptions: advancedOptions, + accessPolicies: accessPolicies, + dataNodeInstanceType: dataNodeType, + dataNodes: dataNodeCount, + dedicatedManagerNodeType: dedicatedManagerNodeType, + dedicatedManagerNodeCount: dedicatedManagerNodeCount, + warmInstanceType: warmNodeType, + warmNodes: warmNodeCount, + zoneAwarenessEnabled: zoneAwarenessEnabled, + zoneAwarenessAvailabilityZoneCount: zoneAwarenessAvailabilityZoneCount, + useUnsignedBasicAuth: useUnsignedBasicAuth, + fineGrainedManagerUserARN: fineGrainedManagerUserARN, + fineGrainedManagerUserName: fineGrainedManagerUserName, + fineGrainedManagerUserSecretManagerKeyARN: fineGrainedManagerUserSecretManagerKeyARN, + cognitoIdentityPoolId: cognitoIdentityPoolId, + cognitoRoleARN: cognitoRoleARN, + cognitoUserPoolId: cognitoUserPoolId, + customEndpointDomainName: customEndpointDomainName, + customEndpointCertificateARN: customEndpointCertificateARN, + customEndpointHostedZoneId: customEndpointHostedZoneId, + enforceHTTPS: enforceHTTPS, + tlsSecurityPolicy: tlsSecurityPolicy, + nodeToNodeEncryptionEnabled: noneToNodeEncryptionEnabled, + encryptionAtRestEnabled: encryptionAtRestEnabled, + encryptionAtRestKmsKeyARN: encryptionAtRestKmsKeyARN, + ebsEnabled: ebsEnabled, + ebsIops: ebsIops, + ebsVolumeSize: ebsVolumeSize, + ebsVolumeType: ebsVolumeType, + appLogEnabled: loggingAppLogEnabled, + appLogGroup: loggingAppLogGroupARN, + auditLogEnabled: loggingAuditLogEnabled, + auditLogGroup: loggingAuditLogGroupARN, + slowIndexLogEnabled: loggingSlowIndexLogEnabled, + slowIndexLogGroup: loggingSlowIndexLogGroupARN, + slowSearchLogEnabled: loggingSlowSearchLogEnabled, + slowSearchLogGroup: loggingSlowSearchLogGroupARN, + snapshotAutomatedStartHour: snapshotAutomatedStartHour, + vpcId: vpcId, + vpcSecurityGroupIds: vpcSecurityGroupIds, + vpcSubnetIds: vpcSubnetIds, + enableVersionUpgrade: enableVersionUpgrade, + domainRemovalPolicy: domainRemovalPolicy, ...props, }); - function getValidOrUndef(value: any):any { - return !value ? undefined : value + function getContextForType(optionName: string, expectedType: string): any { + const option = scope.node.tryGetContext(optionName) + // Falsy check modified to not include values we want to retain + if (option !== false && option !== 0 && !option) { + return undefined + } + // Values provided by the CLI will always be represented as a string and need to be parsed + if (typeof option === 'string') { + if (expectedType === 'number') { + return parseInt(option) + } + if (expectedType === 'boolean' || expectedType === 'object') { + return JSON.parse(option) + } + } + // Values provided by the cdk.context.json should be of the desired type + if (typeof option !== expectedType) { + console.warn(`Type provided by cdk.context.json for ${optionName} was ${typeof option} but expected ${expectedType}`) + } + return option + } + + function parseAccessPolicies(jsonObject: any): PolicyStatement[] { + let accessPolicies: PolicyStatement[] = [] + const statements = jsonObject['Statement'] + for (let i = 0; i < statements.length; i++) { + const statement = PolicyStatement.fromJson(statements[i]) + accessPolicies.push(statement) + } + return accessPolicies } + } } \ No newline at end of file From 97dd8e448b809defb06c5c2c1072e19eba1afae7 Mon Sep 17 00:00:00 2001 From: Tanner Lewis Date: Fri, 10 Feb 2023 09:44:44 -0500 Subject: [PATCH 4/8] MIGRATIONS-876: Updated readme with options Signed-off-by: Tanner Lewis --- opensearch-service-domain-cdk/README.md | 50 +++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/opensearch-service-domain-cdk/README.md b/opensearch-service-domain-cdk/README.md index 29aa55fda..b27fb05bb 100644 --- a/opensearch-service-domain-cdk/README.md +++ b/opensearch-service-domain-cdk/README.md @@ -28,9 +28,53 @@ Additional context on some of these options, can also be found in the Domain con **It should be noted that limited testing has been conducted solely in the us-east-1 region, and some items like instance-type might be biased** -| Name | Required | Type | Example | Description | -|------------------------------------|----------|--------|-----------------------------------------------------------------------------|:----------------------------------------------| -| domainName | true | string | cdk-os-service-domain | Name to use for the OpenSearch Service Domain | +| Name | Required | Type | Example | Description | +|-------------------------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| engineVersion | true | string | "OS_1.3" | | +| domainName | true | string | "cdk-os-service-domain" | Name to use for the OpenSearch Service Domain | +| dataNodeType | false | string | "r6g.large.search" | | +| dataNodeCount | false | number | 1 | | +| dedicatedManagerNodeType | false | string | "r6g.large.search" | | +| dedicatedManagerNodeCount | false | number | 3 | | +| warmNodeType | false | string | "ultrawarm1.medium.search" | | +| warmNodeCount | false | number | 3 | | +| zoneAwarenessEnabled | false | boolean | true | | +| zoneAwarenessAvailabilityZoneCount | false | number | 2 | | +| accessPolicies | false | JSON | {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"arn:aws:iam::123456789123:user/test-user"},"Action":"es:ESHttp*","Resource":"arn:aws:es:us-east-1:123456789123:domain/cdk-os-service-domain/*"}]} | | +| advancedOptions | false | JSON | {"indices.fielddata.cache.size": "77"} | | +| useUnsignedBasicAuth | false | boolean | false | | +| fineGrainedManagerUserARN | false | string | "arn:aws:iam::123456789123:user/test-user" | Fine grained access control also requires nodeToNodeEncryptionEnabled and encryptionAtRestEnabled to be enabled.
Either fineGrainedMasterUserARN or fineGrainedMasterUserName should be enabled, but not both. | +| fineGrainedManagerUserName | false | string | "admin" | | +| fineGrainedManagerUserSecretManagerKeyARN | false | string | "arn:aws:secretsmanager:us-east-1:123456789123:secret:master-user-os-pass-123abc" | | +| cognitoIdentityPoolId | false | string | "us-east-1:123abc45-7e09-4f32-a343-1cb57f4700f7" | | +| cognitoRoleARN | false | string | "arn:aws:iam::123456789123:role/Cognito_testidentitypool_Auth_Role" | | +| cognitoUserPoolId | false | string | "us-east-1_123abc456" | | +| customEndpointDomainName | false | string | "example.com" | | +| customEndpointCertificateARN | false | string | "arn:aws:acm:us-east-1:123456789123:certificate/12345678-1234-1234-1234-123456789012" | | +| customEndpointHostedZoneId | false | string | "Z09411642VVTRWNBSI52M" | | +| enforceHTTPS | false | boolean | true | | +| tlsSecurityPolicy | false | string | "TLS_1_2" | | +| ebsEnabled | false | boolean | true | Some instance types (i.e. r6gd) require that EBS be disabled | +| ebsIops | false | number | 4000 | | +| ebsVolumeSize | false | number | 15 | | +| ebsVolumeType | false | string | "GP3" | | +| encryptionAtRestEnabled | false | boolean | true | | +| encryptionAtRestKmsKeyARN | false | string | "arn:aws:kms:us-east-1:123456789123:key/abc123de-4888-4fa7-a508-3811e2d49fc3" | If encryptionAtRestEnabled is enabled and this value is not provided, the default KMS key for OpenSearch Service will be used | +| loggingAppLogEnabled | false | boolean | true | | +| loggingAppLogGroupARN | false | string | "arn:aws:logs:us-east-1:123456789123:log-group:test-log-group:*" | If not provided and enabled, a CloudWatch log group will be created | +| loggingAuditLogEnabled | false | boolean | true | | +| loggingAuditLogGroupARN | false | string | "arn:aws:logs:us-east-1:123456789123:log-group:test-log-group:*" | If not provided and enabled, a CloudWatch log group will be created | +| loggingSlowIndexLogEnabled | false | boolean | true | | +| loggingSlowIndexLogGroupARN | false | string | "arn:aws:logs:us-east-1:123456789123:log-group:test-log-group:*" | If not provided and enabled, a CloudWatch log group will be created | +| loggingSlowSearchLogEnabled | false | boolean | true | | +| loggingSlowSearchLogGroupARN | false | string | "arn:aws:logs:us-east-1:123456789123:log-group:test-log-group:*" | If not provided and enabled, a CloudWatch log group will be created | +| nodeToNodeEncryptionEnabled | false | boolean | true | | +| snapshotAutomatedStartHour | false | number | 10 | Only applicable to Elasticsearch versions below 5.3 | +| vpcId | false | string | "vpc-123456789abcdefgh" | | +| vpcSecurityGroupIds | false | string array | ["sg-123456789abcdefgh", "sg-223456789abcdefgh"] | | +| vpcSubnetIds | false | string array | ["subnet-123456789abcdefgh", "subnet-223456789abcdefgh"] | | +| enableVersionUpgrade | false | boolean | true | | +| domainRemovalPolicy | false | string | "RETAIN" | | #### Future options These options are not currently achievable by the CDK Domain construct alone, although possible through CloudFormation or the AWS SDK, and are planned to be added to this code base From 1cac51d7869a8883e7178fe29525428a214db985 Mon Sep 17 00:00:00 2001 From: Tanner Lewis Date: Mon, 13 Feb 2023 12:08:29 -0500 Subject: [PATCH 5/8] MIGRATIONS-876: Minor updates for PR comments Signed-off-by: Tanner Lewis --- opensearch-service-domain-cdk/README.md | 55 ++++++++----------- opensearch-service-domain-cdk/bin/app.ts | 6 +- .../cdk.context.json | 7 --- .../opensearch-service-domain-cdk-stack.ts | 45 +-------------- .../lib/stack-composer.ts | 36 ++---------- 5 files changed, 33 insertions(+), 116 deletions(-) diff --git a/opensearch-service-domain-cdk/README.md b/opensearch-service-domain-cdk/README.md index b27fb05bb..7728f1a0f 100644 --- a/opensearch-service-domain-cdk/README.md +++ b/opensearch-service-domain-cdk/README.md @@ -10,13 +10,16 @@ Also ensure you have configured the desired [AWS credentials](https://docs.aws.a Before deploying your Domain stack you should fill in any desired context parameters that will dictate the composition of your OpenSearch Service Domain -This can be accomplished by simply filling in the values in the `cdk.context.json` +This can be accomplished by providing these options in the `cdk.context.json` -As well as by passing these context options as options in the CDK CLI +As well as by passing the context options you want to change as options in the CDK CLI ``` cdk deploy --c domainName='cdk-os-service-domain' --c engineVersion="OS_1_3_6" --c dataNodeType="r6g.large.search" --c dataNodeCount=1 ``` -* Note that these context parameters can also be passed to `cdk synth` and `cdk bootstrap` commands +* Note that these context parameters can also be passed to `cdk synth` and `cdk bootstrap` commands to simulate similar scenarios + +Depending on your use-case, you may choose to provide options from both the `cdk.context.json` and the CDK CLI, in which case +the options passed by the CLI will overwrite the existing option you have in the `cdk.context.json`, if that option is defined there ### Configuration Options @@ -24,6 +27,9 @@ cdk deploy --c domainName='cdk-os-service-domain' --c engineVersion="OS_1_3_6" - The available configuration options are listed below. The vast majority of these options do not need to be provided, with only `domainName` and `engineVersion` being required. All non-required options can be removed from the `cdk.context.json` (or not passed by the CLI) or provided as an empty string `""`, in each of these cases the option will be allocated with the CDK Domain default value +Users are encouraged to customize the deployment by changing the CDK TypeScript as needed. The configuration-by-context option that is depicted here is primarily provided for testing/development purposes, +and users may find it easier to adjust the TS here rather than say wrangling a complex JSON object through a context option + Additional context on some of these options, can also be found in the Domain construct [documentation](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_opensearchservice.Domain.html) **It should be noted that limited testing has been conducted solely in the us-east-1 region, and some items like instance-type might be biased** @@ -46,12 +52,6 @@ Additional context on some of these options, can also be found in the Domain con | fineGrainedManagerUserARN | false | string | "arn:aws:iam::123456789123:user/test-user" | Fine grained access control also requires nodeToNodeEncryptionEnabled and encryptionAtRestEnabled to be enabled.
Either fineGrainedMasterUserARN or fineGrainedMasterUserName should be enabled, but not both. | | fineGrainedManagerUserName | false | string | "admin" | | | fineGrainedManagerUserSecretManagerKeyARN | false | string | "arn:aws:secretsmanager:us-east-1:123456789123:secret:master-user-os-pass-123abc" | | -| cognitoIdentityPoolId | false | string | "us-east-1:123abc45-7e09-4f32-a343-1cb57f4700f7" | | -| cognitoRoleARN | false | string | "arn:aws:iam::123456789123:role/Cognito_testidentitypool_Auth_Role" | | -| cognitoUserPoolId | false | string | "us-east-1_123abc456" | | -| customEndpointDomainName | false | string | "example.com" | | -| customEndpointCertificateARN | false | string | "arn:aws:acm:us-east-1:123456789123:certificate/12345678-1234-1234-1234-123456789012" | | -| customEndpointHostedZoneId | false | string | "Z09411642VVTRWNBSI52M" | | | enforceHTTPS | false | boolean | true | | | tlsSecurityPolicy | false | string | "TLS_1_2" | | | ebsEnabled | false | boolean | true | Some instance types (i.e. r6gd) require that EBS be disabled | @@ -73,38 +73,27 @@ Additional context on some of these options, can also be found in the Domain con | vpcId | false | string | "vpc-123456789abcdefgh" | | | vpcSecurityGroupIds | false | string array | ["sg-123456789abcdefgh", "sg-223456789abcdefgh"] | | | vpcSubnetIds | false | string array | ["subnet-123456789abcdefgh", "subnet-223456789abcdefgh"] | | -| enableVersionUpgrade | false | boolean | true | | | domainRemovalPolicy | false | string | "RETAIN" | | -#### Future options -These options are not currently achievable by the CDK Domain construct alone, although possible through CloudFormation or the AWS SDK, and are planned to be added to this code base -``` -"coldStorageEnabled": "X", -"anonymousAuthEnabled": "X", -"anonymousAuthDisableDate": "X", -"samlEnabled": "X", -"samlIdentityProviderEntityId": "X", -"samlIdentityProviderMetadataContent": "X", -"samlMasterBackendRole": "X", -"samlMasterUserName": "X", -"samlRolesKey": "X", -"samlSessionTimeoutMinutes": "X", -"samlSubjectKey": "X", -"ebsThroughput": "X", -"tags": "X", -``` - - -Some configuration options (listed below) which enable/disable specific features do not exist in the current native CDK Domain construct. These options are inferred based on the presence or absence of related fields (i.e. if dedicatedMasterNodeCount is set to 1 it is +Some configuration options available in other solutions (listed below) which enable/disable specific features do not exist in the current native CDK Domain construct. These options are inferred based on the presence or absence of related fields (i.e. if dedicatedMasterNodeCount is set to 1 it is inferred that dedicated master nodes should be enabled). These options are normally disabled by default, allowing for this inference. ``` "dedicatedMasterNodeEnabled": "X", "warmNodeEnabled": "X", "fineGrainedAccessControlEnabled": "X", -"internalUserDatabaseEnabled": "X", -"cognitoEnabled": "X", -"customEndpointEnabled": "X", +"internalUserDatabaseEnabled": "X" +``` + +### Tearing down CDK Stack +To remove the stack which gets created during deployment, which contains our created resources like our Domain and any other resources created from enabled +features (such as a CloudWatch log group), we can execute ``` +cdk destroy +``` +Note that the default retention policy for the OpenSearch Domain is to RETAIN this resource when the stack is deleted, and in order to delete the Domain +on stack deletion the `domainRemovalPolicy` would need to be set to `DESTROY`. Otherwise, the Domain can be manually deleted through the AWS console or +through other means such as the AWS CLI. + ### Useful CDK commands * `npm run build` compile typescript to js diff --git a/opensearch-service-domain-cdk/bin/app.ts b/opensearch-service-domain-cdk/bin/app.ts index 7c69dabcc..7c1e31b94 100644 --- a/opensearch-service-domain-cdk/bin/app.ts +++ b/opensearch-service-domain-cdk/bin/app.ts @@ -1,10 +1,10 @@ #!/usr/bin/env node import 'source-map-support/register'; -import * as cdk from 'aws-cdk-lib'; +import {App} from 'aws-cdk-lib'; import {StackComposer} from "../lib/stack-composer"; -const app = new cdk.App(); -const stage = "dev" +const app = new App(); +const stage = process.env.CDK_DEPLOYMENT_STAGE const account = process.env.CDK_DEFAULT_ACCOUNT const region = process.env.CDK_DEFAULT_REGION new StackComposer(app, { diff --git a/opensearch-service-domain-cdk/cdk.context.json b/opensearch-service-domain-cdk/cdk.context.json index 027db50c9..449d87cba 100644 --- a/opensearch-service-domain-cdk/cdk.context.json +++ b/opensearch-service-domain-cdk/cdk.context.json @@ -15,12 +15,6 @@ "fineGrainedManagerUserARN": "", "fineGrainedManagerUserName": "", "fineGrainedManagerUserSecretManagerKeyARN": "", - "cognitoIdentityPoolId": "", - "cognitoRoleARN": "", - "cognitoUserPoolId": "", - "customEndpointDomainName": "", - "customEndpointCertificateARN": "", - "customEndpointHostedZoneId": "", "enforceHTTPS": "", "tlsSecurityPolicy": "", "ebsEnabled": "", @@ -42,6 +36,5 @@ "vpcId": "", "vpcSecurityGroupIds": "", "vpcSubnetIds": "", - "enableVersionUpgrade": "", "domainRemovalPolicy": "" } diff --git a/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts b/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts index 9d7342528..20a6cee60 100644 --- a/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts +++ b/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts @@ -9,13 +9,10 @@ import { SubnetSelection, Vpc } from "aws-cdk-lib/aws-ec2"; -import {CustomEndpointOptions, Domain, EngineVersion, TLSSecurityPolicy} from "aws-cdk-lib/aws-opensearchservice"; +import {Domain, EngineVersion, TLSSecurityPolicy} from "aws-cdk-lib/aws-opensearchservice"; import {RemovalPolicy, SecretValue, Stack, StackProps} from "aws-cdk-lib"; import {IKey, Key} from "aws-cdk-lib/aws-kms"; -import {IRole, PolicyStatement, Role} from "aws-cdk-lib/aws-iam"; -import {CognitoOptions} from "aws-cdk-lib/aws-opensearchservice/lib/domain"; -import {Certificate, ICertificate} from "aws-cdk-lib/aws-certificatemanager"; -import {HostedZone, IHostedZone} from "aws-cdk-lib/aws-route53"; +import {PolicyStatement} from "aws-cdk-lib/aws-iam"; import {ILogGroup, LogGroup} from "aws-cdk-lib/aws-logs"; import {Secret} from "aws-cdk-lib/aws-secretsmanager"; @@ -37,15 +34,9 @@ export interface opensearchServiceDomainCdkProps extends StackProps{ readonly fineGrainedManagerUserARN?: string, readonly fineGrainedManagerUserName?: string, readonly fineGrainedManagerUserSecretManagerKeyARN?: string, - readonly cognitoIdentityPoolId?: string, - readonly cognitoRoleARN?: string, - readonly cognitoUserPoolId?: string, readonly nodeToNodeEncryptionEnabled?: boolean, readonly encryptionAtRestEnabled?: boolean, readonly encryptionAtRestKmsKeyARN?: string, - readonly customEndpointDomainName?: string, - readonly customEndpointCertificateARN?: string, - readonly customEndpointHostedZoneId?: string, readonly enforceHTTPS?: boolean, readonly tlsSecurityPolicy?: TLSSecurityPolicy, readonly ebsEnabled?: boolean, @@ -64,7 +55,6 @@ export interface opensearchServiceDomainCdkProps extends StackProps{ readonly vpcId?: string, readonly vpcSubnetIds?: string[], readonly vpcSecurityGroupIds?: string[], - readonly enableVersionUpgrade?: boolean, readonly domainRemovalPolicy?: RemovalPolicy } @@ -82,15 +72,6 @@ export class OpensearchServiceDomainCdkStack extends Stack { const managerUserSecret: SecretValue|undefined = props.fineGrainedManagerUserSecretManagerKeyARN ? Secret.fromSecretCompleteArn(this, "managerSecret", props.fineGrainedManagerUserSecretManagerKeyARN).secretValue : undefined - const cognitoRole: IRole|undefined = props.cognitoRoleARN ? - Role.fromRoleArn(this, "cognitoRole", props.cognitoRoleARN) : undefined - - const endpointCert: ICertificate|undefined = props.customEndpointCertificateARN ? - Certificate.fromCertificateArn(this, "endpointCert", props.customEndpointCertificateARN) : undefined - - const hostedZone: IHostedZone|undefined = props.customEndpointHostedZoneId ? - HostedZone.fromHostedZoneId(this, "hostedZone", props.customEndpointHostedZoneId) : undefined - const appLG: ILogGroup|undefined = props.appLogGroup && props.appLogEnabled ? LogGroup.fromLogGroupArn(this, "appLogGroup", props.appLogGroup) : undefined @@ -127,25 +108,6 @@ export class OpensearchServiceDomainCdkStack extends Stack { vpcSecurityGroups = securityGroupArray } - // Map structures that require specific fields - let cognitoOptions: CognitoOptions|undefined = undefined - if (props.cognitoIdentityPoolId && cognitoRole && props.cognitoUserPoolId) { - cognitoOptions = { - identityPoolId: props.cognitoIdentityPoolId, - role: cognitoRole, - userPoolId: props.cognitoUserPoolId - } - } - - let endpointOptions: CustomEndpointOptions|undefined = undefined - if (props.customEndpointDomainName) { - endpointOptions = { - domainName: props.customEndpointDomainName, - certificate: endpointCert, - hostedZone: hostedZone - } - } - const domain = new Domain(this, 'Domain', { version: props.version, domainName: props.domainName, @@ -169,13 +131,11 @@ export class OpensearchServiceDomainCdkStack extends Stack { masterUserName: props.fineGrainedManagerUserName, masterUserPassword: managerUserSecret }, - cognitoDashboardsAuth: cognitoOptions, nodeToNodeEncryption: props.nodeToNodeEncryptionEnabled, encryptionAtRest: { enabled: props.encryptionAtRestEnabled, kmsKey: earKmsKey }, - customEndpoint: endpointOptions, enforceHttps: props.enforceHTTPS, tlsSecurityPolicy: props.tlsSecurityPolicy, ebs: { @@ -198,7 +158,6 @@ export class OpensearchServiceDomainCdkStack extends Stack { vpc: vpc, vpcSubnets: vpcSubnets, securityGroups: vpcSecurityGroups, - enableVersionUpgrade: props.enableVersionUpgrade, removalPolicy: props.domainRemovalPolicy }); } diff --git a/opensearch-service-domain-cdk/lib/stack-composer.ts b/opensearch-service-domain-cdk/lib/stack-composer.ts index a422c4632..c71b7c1c9 100644 --- a/opensearch-service-domain-cdk/lib/stack-composer.ts +++ b/opensearch-service-domain-cdk/lib/stack-composer.ts @@ -25,12 +25,6 @@ export class StackComposer { const fineGrainedManagerUserARN = getContextForType('fineGrainedManagerUserARN', 'string') const fineGrainedManagerUserName = getContextForType('fineGrainedManagerUserName', 'string') const fineGrainedManagerUserSecretManagerKeyARN = getContextForType('fineGrainedManagerUserSecretManagerKeyARN', 'string') - const cognitoIdentityPoolId = getContextForType('cognitoIdentityPoolId', 'string') - const cognitoRoleARN = getContextForType('cognitoRoleARN', 'string') - const cognitoUserPoolId = getContextForType('cognitoUserPoolId', 'string') - const customEndpointDomainName = getContextForType('customEndpointDomainName', 'string') - const customEndpointCertificateARN = getContextForType('customEndpointCertificateARN', 'string') - const customEndpointHostedZoneId = getContextForType('customEndpointHostedZoneId', 'string') const enforceHTTPS = getContextForType('enforceHTTPS', 'boolean') const noneToNodeEncryptionEnabled = getContextForType('nodeToNodeEncryptionEnabled', 'boolean') const encryptionAtRestEnabled = getContextForType('encryptionAtRestEnabled', 'boolean') @@ -50,18 +44,7 @@ export class StackComposer { const vpcId = getContextForType('vpcId', 'string') const vpcSecurityGroupIds = getContextForType('vpcSecurityGroupIds', 'object') const vpcSubnetIds = getContextForType('vpcSubnetIds', 'object') - const enableVersionUpgrade = getContextForType('enableVersionUpgrade', 'boolean') - // Check for partially configured features - if ((cognitoIdentityPoolId || cognitoRoleARN || cognitoUserPoolId) && - !(cognitoIdentityPoolId && cognitoRoleARN && cognitoUserPoolId)) { - throw new Error("All Cognito values [cognitoIdentityPoolId, cognitoRoleARN, cognitoUserPoolId] are " + - "required if using Cognito authentication") - } - - if ((customEndpointCertificateARN || customEndpointHostedZoneId) && !(customEndpointDomainName)) { - throw new Error("The 'customEndpointDomainName' value is required when configuring a custom endpoint") - } const engineVersion = getContextForType('engineVersion', 'string') if (engineVersion.startsWith("OS_")) { @@ -81,19 +64,19 @@ export class StackComposer { const tlsSecurityPolicyName = getContextForType('tlsSecurityPolicy', 'string') const tlsSecurityPolicy: TLSSecurityPolicy|undefined = tlsSecurityPolicyName ? TLSSecurityPolicy[tlsSecurityPolicyName as keyof typeof TLSSecurityPolicy] : undefined if (tlsSecurityPolicyName && !tlsSecurityPolicy) { - throw new Error("Provided tlsSecurityPolicy does not match a selectable option, i.e. 'TLS_1_2'") + throw new Error("Provided tlsSecurityPolicy does not match a selectable option, for reference https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_opensearchservice.TLSSecurityPolicy.html") } const ebsVolumeTypeName = getContextForType('ebsVolumeType', 'string') const ebsVolumeType: EbsDeviceVolumeType|undefined = ebsVolumeTypeName ? EbsDeviceVolumeType[ebsVolumeTypeName as keyof typeof EbsDeviceVolumeType] : undefined if (ebsVolumeTypeName && !ebsVolumeType) { - throw new Error("Provided ebsVolumeType does not match a selectable option, i.e. 'GP3'") + throw new Error("Provided ebsVolumeType does not match a selectable option, for reference https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.EbsDeviceVolumeType.html") } const domainRemovalPolicyName = getContextForType('domainRemovalPolicy', 'string') const domainRemovalPolicy = domainRemovalPolicyName ? RemovalPolicy[domainRemovalPolicyName as keyof typeof RemovalPolicy] : undefined if (domainRemovalPolicyName && !domainRemovalPolicy) { - throw new Error("Provided domainRemovalPolicy does not match a selectable option, i.e. 'RETAIN'") + throw new Error("Provided domainRemovalPolicy does not match a selectable option, for reference https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.RemovalPolicy.html") } const opensearchStack = new OpensearchServiceDomainCdkStack(scope, 'opensearchDomainStack', { @@ -113,12 +96,6 @@ export class StackComposer { fineGrainedManagerUserARN: fineGrainedManagerUserARN, fineGrainedManagerUserName: fineGrainedManagerUserName, fineGrainedManagerUserSecretManagerKeyARN: fineGrainedManagerUserSecretManagerKeyARN, - cognitoIdentityPoolId: cognitoIdentityPoolId, - cognitoRoleARN: cognitoRoleARN, - cognitoUserPoolId: cognitoUserPoolId, - customEndpointDomainName: customEndpointDomainName, - customEndpointCertificateARN: customEndpointCertificateARN, - customEndpointHostedZoneId: customEndpointHostedZoneId, enforceHTTPS: enforceHTTPS, tlsSecurityPolicy: tlsSecurityPolicy, nodeToNodeEncryptionEnabled: noneToNodeEncryptionEnabled, @@ -140,14 +117,13 @@ export class StackComposer { vpcId: vpcId, vpcSecurityGroupIds: vpcSecurityGroupIds, vpcSubnetIds: vpcSubnetIds, - enableVersionUpgrade: enableVersionUpgrade, domainRemovalPolicy: domainRemovalPolicy, ...props, }); function getContextForType(optionName: string, expectedType: string): any { const option = scope.node.tryGetContext(optionName) - // Falsy check modified to not include values we want to retain + // Filter out invalid or missing options by setting undefined (empty strings, null, undefined, NaN) if (option !== false && option !== 0 && !option) { return undefined } @@ -162,12 +138,12 @@ export class StackComposer { } // Values provided by the cdk.context.json should be of the desired type if (typeof option !== expectedType) { - console.warn(`Type provided by cdk.context.json for ${optionName} was ${typeof option} but expected ${expectedType}`) + throw new Error(`Type provided by cdk.context.json for ${optionName} was ${typeof option} but expected ${expectedType}`) } return option } - function parseAccessPolicies(jsonObject: any): PolicyStatement[] { + function parseAccessPolicies(jsonObject: { [x: string]: any; }): PolicyStatement[] { let accessPolicies: PolicyStatement[] = [] const statements = jsonObject['Statement'] for (let i = 0; i < statements.length; i++) { From 55d140f955df02ae1ad65b4bdb8499c9d4868fcc Mon Sep 17 00:00:00 2001 From: Tanner Lewis Date: Mon, 13 Feb 2023 17:15:00 -0500 Subject: [PATCH 6/8] MIGRATIONS-876: Additional documentation and reduced scope Signed-off-by: Tanner Lewis --- opensearch-service-domain-cdk/README.md | 60 ++++++++++---- .../cdk.context.json | 40 ---------- .../default-values.json | 4 + .../opensearch-service-domain-cdk-stack.ts | 78 ++----------------- .../lib/stack-composer.ts | 60 ++++++-------- opensearch-service-domain-cdk/tsconfig.json | 1 + 6 files changed, 80 insertions(+), 163 deletions(-) delete mode 100644 opensearch-service-domain-cdk/cdk.context.json create mode 100644 opensearch-service-domain-cdk/default-values.json diff --git a/opensearch-service-domain-cdk/README.md b/opensearch-service-domain-cdk/README.md index 7728f1a0f..d640dbaa3 100644 --- a/opensearch-service-domain-cdk/README.md +++ b/opensearch-service-domain-cdk/README.md @@ -6,11 +6,13 @@ If this is your first time using CDK in this region, will need to `cdk bootstrap Also ensure you have configured the desired [AWS credentials](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_prerequisites), as these will dictate the region and account used for deployment +A `CDK_DEPLOYMENT_STAGE` environment variable should also be set to assist in naming resources and preventing collisions + ### Deploying your Domain Stack Before deploying your Domain stack you should fill in any desired context parameters that will dictate the composition of your OpenSearch Service Domain -This can be accomplished by providing these options in the `cdk.context.json` +This can be accomplished by providing these options in a `cdk.context.json` file As well as by passing the context options you want to change as options in the CDK CLI ``` @@ -18,14 +20,17 @@ cdk deploy --c domainName='cdk-os-service-domain' --c engineVersion="OS_1_3_6" - ``` * Note that these context parameters can also be passed to `cdk synth` and `cdk bootstrap` commands to simulate similar scenarios -Depending on your use-case, you may choose to provide options from both the `cdk.context.json` and the CDK CLI, in which case -the options passed by the CLI will overwrite the existing option you have in the `cdk.context.json`, if that option is defined there +Depending on your use-case, you may choose to provide options from both the `cdk.context.json` and the CDK CLI, in which case it is important +to know the precedence level for context values. The below order shows these levels with values placed in the `cdk.context.json` having the most importance +1. Created `cdk.context.json` in base directory +2. CDK CLI passed context values +3. Existing `default-values.json` in base directory ### Configuration Options The available configuration options are listed below. The vast majority of these options do not need to be provided, with only `domainName` and `engineVersion` being required. -All non-required options can be removed from the `cdk.context.json` (or not passed by the CLI) or provided as an empty string `""`, in each of these cases the option will be allocated with the CDK Domain default value +All non-required options can be provided as an empty string `""` or simply not included, and in each of these cases the option will be allocated with the CDK Domain default value Users are encouraged to customize the deployment by changing the CDK TypeScript as needed. The configuration-by-context option that is depicted here is primarily provided for testing/development purposes, and users may find it easier to adjust the TS here rather than say wrangling a complex JSON object through a context option @@ -44,10 +49,7 @@ Additional context on some of these options, can also be found in the Domain con | dedicatedManagerNodeCount | false | number | 3 | | | warmNodeType | false | string | "ultrawarm1.medium.search" | | | warmNodeCount | false | number | 3 | | -| zoneAwarenessEnabled | false | boolean | true | | -| zoneAwarenessAvailabilityZoneCount | false | number | 2 | | | accessPolicies | false | JSON | {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"arn:aws:iam::123456789123:user/test-user"},"Action":"es:ESHttp*","Resource":"arn:aws:es:us-east-1:123456789123:domain/cdk-os-service-domain/*"}]} | | -| advancedOptions | false | JSON | {"indices.fielddata.cache.size": "77"} | | | useUnsignedBasicAuth | false | boolean | false | | | fineGrainedManagerUserARN | false | string | "arn:aws:iam::123456789123:user/test-user" | Fine grained access control also requires nodeToNodeEncryptionEnabled and encryptionAtRestEnabled to be enabled.
Either fineGrainedMasterUserARN or fineGrainedMasterUserName should be enabled, but not both. | | fineGrainedManagerUserName | false | string | "admin" | | @@ -61,20 +63,44 @@ Additional context on some of these options, can also be found in the Domain con | encryptionAtRestEnabled | false | boolean | true | | | encryptionAtRestKmsKeyARN | false | string | "arn:aws:kms:us-east-1:123456789123:key/abc123de-4888-4fa7-a508-3811e2d49fc3" | If encryptionAtRestEnabled is enabled and this value is not provided, the default KMS key for OpenSearch Service will be used | | loggingAppLogEnabled | false | boolean | true | | -| loggingAppLogGroupARN | false | string | "arn:aws:logs:us-east-1:123456789123:log-group:test-log-group:*" | If not provided and enabled, a CloudWatch log group will be created | -| loggingAuditLogEnabled | false | boolean | true | | -| loggingAuditLogGroupARN | false | string | "arn:aws:logs:us-east-1:123456789123:log-group:test-log-group:*" | If not provided and enabled, a CloudWatch log group will be created | -| loggingSlowIndexLogEnabled | false | boolean | true | | -| loggingSlowIndexLogGroupARN | false | string | "arn:aws:logs:us-east-1:123456789123:log-group:test-log-group:*" | If not provided and enabled, a CloudWatch log group will be created | -| loggingSlowSearchLogEnabled | false | boolean | true | | -| loggingSlowSearchLogGroupARN | false | string | "arn:aws:logs:us-east-1:123456789123:log-group:test-log-group:*" | If not provided and enabled, a CloudWatch log group will be created | +| loggingAppLogGroupARN | false | string | "arn:aws:logs:us-east-1:123456789123:log-group:test-log-group:*" | If not provided and logs are enabled, a CloudWatch log group will be created | | nodeToNodeEncryptionEnabled | false | boolean | true | | -| snapshotAutomatedStartHour | false | number | 10 | Only applicable to Elasticsearch versions below 5.3 | | vpcId | false | string | "vpc-123456789abcdefgh" | | -| vpcSecurityGroupIds | false | string array | ["sg-123456789abcdefgh", "sg-223456789abcdefgh"] | | -| vpcSubnetIds | false | string array | ["subnet-123456789abcdefgh", "subnet-223456789abcdefgh"] | | | domainRemovalPolicy | false | string | "RETAIN" | | + +A template `cdk.context.json` to be used to fill in these values is below: +``` +{ + "engineVersion": "", + "domainName": "", + "dataNodeType": "", + "dataNodeCount": "", + "dedicatedManagerNodeType": "", + "dedicatedManagerNodeCount": "", + "warmNodeType": "", + "warmNodeCount": "", + "accessPolicies": "", + "useUnsignedBasicAuth": "", + "fineGrainedManagerUserARN": "", + "fineGrainedManagerUserName": "", + "fineGrainedManagerUserSecretManagerKeyARN": "", + "enforceHTTPS": "", + "tlsSecurityPolicy": "", + "ebsEnabled": "", + "ebsIops": "", + "ebsVolumeSize": "", + "ebsVolumeType": "", + "encryptionAtRestEnabled": "", + "encryptionAtRestKmsKeyARN": "", + "loggingAppLogEnabled": "", + "loggingAppLogGroupARN": "", + "nodeToNodeEncryptionEnabled": "", + "vpcId": "", + "domainRemovalPolicy": "" +} + +``` Some configuration options available in other solutions (listed below) which enable/disable specific features do not exist in the current native CDK Domain construct. These options are inferred based on the presence or absence of related fields (i.e. if dedicatedMasterNodeCount is set to 1 it is inferred that dedicated master nodes should be enabled). These options are normally disabled by default, allowing for this inference. ``` diff --git a/opensearch-service-domain-cdk/cdk.context.json b/opensearch-service-domain-cdk/cdk.context.json deleted file mode 100644 index 449d87cba..000000000 --- a/opensearch-service-domain-cdk/cdk.context.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "domainName": "cdk-os-service-domain", - "engineVersion": "OS_1.3", - "dataNodeType": "", - "dataNodeCount": "", - "dedicatedManagerNodeType": "", - "dedicatedManagerNodeCount": "", - "warmNodeType": "", - "warmNodeCount": "", - "zoneAwarenessEnabled": "", - "zoneAwarenessAvailabilityZoneCount": "", - "accessPolicies": "", - "advancedOptions": "", - "useUnsignedBasicAuth": "", - "fineGrainedManagerUserARN": "", - "fineGrainedManagerUserName": "", - "fineGrainedManagerUserSecretManagerKeyARN": "", - "enforceHTTPS": "", - "tlsSecurityPolicy": "", - "ebsEnabled": "", - "ebsIops": "", - "ebsVolumeSize": "", - "ebsVolumeType": "", - "encryptionAtRestEnabled": "", - "encryptionAtRestKmsKeyARN": "", - "loggingAppLogEnabled": "", - "loggingAppLogGroupARN": "", - "loggingAuditLogEnabled": "", - "loggingAuditLogGroupARN": "", - "loggingSlowIndexLogEnabled": "", - "loggingSlowIndexLogGroupARN": "", - "loggingSlowSearchLogEnabled": "", - "loggingSlowSearchLogGroupARN": "", - "nodeToNodeEncryptionEnabled": "", - "snapshotAutomatedStartHour": "", - "vpcId": "", - "vpcSecurityGroupIds": "", - "vpcSubnetIds": "", - "domainRemovalPolicy": "" -} diff --git a/opensearch-service-domain-cdk/default-values.json b/opensearch-service-domain-cdk/default-values.json new file mode 100644 index 000000000..30eae4dcd --- /dev/null +++ b/opensearch-service-domain-cdk/default-values.json @@ -0,0 +1,4 @@ +{ + "engineVersion": "OS_1.3", + "domainName": "cdk-os-service-domain" +} \ No newline at end of file diff --git a/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts b/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts index 20a6cee60..bff352895 100644 --- a/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts +++ b/opensearch-service-domain-cdk/lib/opensearch-service-domain-cdk-stack.ts @@ -1,14 +1,5 @@ import { Construct } from 'constructs'; -import { - EbsDeviceVolumeType, - ISecurityGroup, - ISubnet, - IVpc, - SecurityGroup, - Subnet, - SubnetSelection, - Vpc -} from "aws-cdk-lib/aws-ec2"; +import {EbsDeviceVolumeType, IVpc, Vpc} from "aws-cdk-lib/aws-ec2"; import {Domain, EngineVersion, TLSSecurityPolicy} from "aws-cdk-lib/aws-opensearchservice"; import {RemovalPolicy, SecretValue, Stack, StackProps} from "aws-cdk-lib"; import {IKey, Key} from "aws-cdk-lib/aws-kms"; @@ -20,41 +11,29 @@ import {Secret} from "aws-cdk-lib/aws-secretsmanager"; export interface opensearchServiceDomainCdkProps extends StackProps{ readonly version: EngineVersion, readonly domainName: string, - readonly advancedOptions?: { [key: string]: (string) }, - readonly accessPolicies?: PolicyStatement[], - readonly useUnsignedBasicAuth?: boolean, readonly dataNodeInstanceType?: string, readonly dataNodes?: number, readonly dedicatedManagerNodeType?: string, readonly dedicatedManagerNodeCount?: number, readonly warmInstanceType?: string, readonly warmNodes?: number - readonly zoneAwarenessEnabled?: boolean, - readonly zoneAwarenessAvailabilityZoneCount?: number, + readonly accessPolicies?: PolicyStatement[], + readonly useUnsignedBasicAuth?: boolean, readonly fineGrainedManagerUserARN?: string, readonly fineGrainedManagerUserName?: string, readonly fineGrainedManagerUserSecretManagerKeyARN?: string, - readonly nodeToNodeEncryptionEnabled?: boolean, - readonly encryptionAtRestEnabled?: boolean, - readonly encryptionAtRestKmsKeyARN?: string, readonly enforceHTTPS?: boolean, readonly tlsSecurityPolicy?: TLSSecurityPolicy, readonly ebsEnabled?: boolean, readonly ebsIops?: number, readonly ebsVolumeSize?: number, readonly ebsVolumeType?: EbsDeviceVolumeType, + readonly encryptionAtRestEnabled?: boolean, + readonly encryptionAtRestKmsKeyARN?: string, readonly appLogEnabled?: boolean, readonly appLogGroup?: string, - readonly auditLogEnabled?: boolean, - readonly auditLogGroup?: string, - readonly slowIndexLogEnabled?: boolean, - readonly slowIndexLogGroup?: string, - readonly slowSearchLogEnabled?: boolean, - readonly slowSearchLogGroup?: string, - readonly snapshotAutomatedStartHour?: number, + readonly nodeToNodeEncryptionEnabled?: boolean, readonly vpcId?: string, - readonly vpcSubnetIds?: string[], - readonly vpcSecurityGroupIds?: string[], readonly domainRemovalPolicy?: RemovalPolicy } @@ -75,43 +54,13 @@ export class OpensearchServiceDomainCdkStack extends Stack { const appLG: ILogGroup|undefined = props.appLogGroup && props.appLogEnabled ? LogGroup.fromLogGroupArn(this, "appLogGroup", props.appLogGroup) : undefined - const auditLG: ILogGroup|undefined = props.auditLogGroup && props.auditLogEnabled ? - LogGroup.fromLogGroupArn(this, "auditLogGroup", props.auditLogGroup) : undefined - - const slowIndexLG: ILogGroup|undefined = props.slowIndexLogGroup && props.slowIndexLogEnabled ? - LogGroup.fromLogGroupArn(this, "slowIndexLogGroup", props.slowIndexLogGroup) : undefined - - const slowSearchLG: ILogGroup|undefined = props.slowSearchLogGroup && props.slowSearchLogEnabled ? - LogGroup.fromLogGroupArn(this, "slowSearchLogGroup", props.slowSearchLogGroup) : undefined - const vpc: IVpc|undefined = props.vpcId ? Vpc.fromLookup(this, "domainVPC", {vpcId: props.vpcId}) : undefined - let vpcSubnets: SubnetSelection[]|undefined = undefined - if (props.vpcSubnetIds && vpc) { - const subnetIds = props.vpcSubnetIds - let subnetArray: ISubnet[] = [] - for (let i = 0; i < subnetIds.length; i++) { - subnetArray.push(Subnet.fromSubnetId(this, "subnet-" + i, subnetIds[i])) - } - const vpcSubnet = {subnets: subnetArray} - vpcSubnets = [vpcSubnet] - } - - let vpcSecurityGroups: ISecurityGroup[]|undefined = undefined - if (props.vpcSecurityGroupIds && vpc) { - const securityGroupIds = props.vpcSecurityGroupIds - let securityGroupArray: ISecurityGroup[] = [] - for (let i = 0; i < securityGroupIds.length; i++) { - securityGroupArray.push(SecurityGroup.fromLookupById(this, "security-group-" + i, securityGroupIds[i])) - } - vpcSecurityGroups = securityGroupArray - } const domain = new Domain(this, 'Domain', { version: props.version, domainName: props.domainName, - advancedOptions: props.advancedOptions, accessPolicies: props.accessPolicies, useUnsignedBasicAuth: props.useUnsignedBasicAuth, capacity: { @@ -122,10 +71,6 @@ export class OpensearchServiceDomainCdkStack extends Stack { warmInstanceType: props.warmInstanceType, warmNodes: props.warmNodes }, - zoneAwareness: { - enabled: props.zoneAwarenessEnabled, - availabilityZoneCount: props.zoneAwarenessAvailabilityZoneCount - }, fineGrainedAccessControl: { masterUserArn: props.fineGrainedManagerUserARN, masterUserName: props.fineGrainedManagerUserName, @@ -144,20 +89,11 @@ export class OpensearchServiceDomainCdkStack extends Stack { volumeSize: props.ebsVolumeSize, volumeType: props.ebsVolumeType }, - automatedSnapshotStartHour: props.snapshotAutomatedStartHour, logging: { appLogEnabled: props.appLogEnabled, - appLogGroup: appLG, - auditLogEnabled: props.auditLogEnabled, - auditLogGroup: auditLG, - slowIndexLogEnabled: props.slowIndexLogEnabled, - slowIndexLogGroup: slowIndexLG, - slowSearchLogEnabled: props.slowSearchLogEnabled, - slowSearchLogGroup: slowSearchLG + appLogGroup: appLG }, vpc: vpc, - vpcSubnets: vpcSubnets, - securityGroups: vpcSecurityGroups, removalPolicy: props.domainRemovalPolicy }); } diff --git a/opensearch-service-domain-cdk/lib/stack-composer.ts b/opensearch-service-domain-cdk/lib/stack-composer.ts index c71b7c1c9..6a3696994 100644 --- a/opensearch-service-domain-cdk/lib/stack-composer.ts +++ b/opensearch-service-domain-cdk/lib/stack-composer.ts @@ -1,16 +1,19 @@ -import {Construct} from 'constructs'; -import {RemovalPolicy, StackProps} from 'aws-cdk-lib'; +import {Construct} from "constructs"; +import {RemovalPolicy, Stack, StackProps} from "aws-cdk-lib"; import {OpensearchServiceDomainCdkStack} from "./opensearch-service-domain-cdk-stack"; import {EngineVersion, TLSSecurityPolicy} from "aws-cdk-lib/aws-opensearchservice"; import {EbsDeviceVolumeType} from "aws-cdk-lib/aws-ec2"; import {PolicyStatement} from "aws-cdk-lib/aws-iam"; +import * as defaultValuesJson from "../default-values.json" export class StackComposer { + public stacks: Stack[] = []; constructor(scope: Construct, props: StackProps) { let version: EngineVersion + const defaultValues: { [x: string]: (string); } = defaultValuesJson const domainName = getContextForType('domainName', 'string') const dataNodeType = getContextForType('dataNodeType', 'string') const dataNodeCount = getContextForType('dataNodeCount', 'number') @@ -18,40 +21,31 @@ export class StackComposer { const dedicatedManagerNodeCount = getContextForType('dedicatedManagerNodeCount', 'number') const warmNodeType = getContextForType('warmNodeType', 'string') const warmNodeCount = getContextForType('warmNodeCount', 'number') - const zoneAwarenessEnabled = getContextForType('zoneAwarenessEnabled', 'boolean') - const zoneAwarenessAvailabilityZoneCount = getContextForType('zoneAwarenessAvailabilityZoneCount', 'number') - const advancedOptions = getContextForType('advancedOptions', 'object') const useUnsignedBasicAuth = getContextForType('useUnsignedBasicAuth', 'boolean') const fineGrainedManagerUserARN = getContextForType('fineGrainedManagerUserARN', 'string') const fineGrainedManagerUserName = getContextForType('fineGrainedManagerUserName', 'string') const fineGrainedManagerUserSecretManagerKeyARN = getContextForType('fineGrainedManagerUserSecretManagerKeyARN', 'string') const enforceHTTPS = getContextForType('enforceHTTPS', 'boolean') - const noneToNodeEncryptionEnabled = getContextForType('nodeToNodeEncryptionEnabled', 'boolean') - const encryptionAtRestEnabled = getContextForType('encryptionAtRestEnabled', 'boolean') - const encryptionAtRestKmsKeyARN = getContextForType("encryptionAtRestKmsKeyARN", 'string') const ebsEnabled = getContextForType('ebsEnabled', 'boolean') const ebsIops = getContextForType('ebsIops', 'number') const ebsVolumeSize = getContextForType('ebsVolumeSize', 'number') + const encryptionAtRestEnabled = getContextForType('encryptionAtRestEnabled', 'boolean') + const encryptionAtRestKmsKeyARN = getContextForType("encryptionAtRestKmsKeyARN", 'string') const loggingAppLogEnabled = getContextForType('loggingAppLogEnabled', 'boolean') const loggingAppLogGroupARN = getContextForType('loggingAppLogGroupARN', 'string') - const loggingAuditLogEnabled = getContextForType('loggingAuditLogEnabled', 'boolean') - const loggingAuditLogGroupARN = getContextForType('loggingAuditLogGroupARN', 'string') - const loggingSlowIndexLogEnabled = getContextForType('loggingSlowIndexLogEnabled', 'boolean') - const loggingSlowIndexLogGroupARN = getContextForType('loggingSlowIndexLogGroupARN', 'string') - const loggingSlowSearchLogEnabled = getContextForType('loggingSlowSearchLogEnabled', 'boolean') - const loggingSlowSearchLogGroupARN = getContextForType('loggingSlowSearchLogGroupARN', 'string') - const snapshotAutomatedStartHour = getContextForType('snapshotAutomatedStartHour', 'number') + const noneToNodeEncryptionEnabled = getContextForType('nodeToNodeEncryptionEnabled', 'boolean') const vpcId = getContextForType('vpcId', 'string') - const vpcSecurityGroupIds = getContextForType('vpcSecurityGroupIds', 'object') - const vpcSubnetIds = getContextForType('vpcSubnetIds', 'object') + if (!domainName) { + throw new Error("Domain name is not present and is a required field") + } const engineVersion = getContextForType('engineVersion', 'string') - if (engineVersion.startsWith("OS_")) { + if (engineVersion && engineVersion.startsWith("OS_")) { // Will accept a period delimited version string (i.e. 1.3) and return a proper EngineVersion version = EngineVersion.openSearch(engineVersion.substring(3)) } - else if (engineVersion.startsWith("ES_")) { + else if (engineVersion && engineVersion.startsWith("ES_")) { version = EngineVersion.elasticsearch(engineVersion.substring(3)) } else { @@ -82,47 +76,43 @@ export class StackComposer { const opensearchStack = new OpensearchServiceDomainCdkStack(scope, 'opensearchDomainStack', { version: version, domainName: domainName, - advancedOptions: advancedOptions, - accessPolicies: accessPolicies, dataNodeInstanceType: dataNodeType, dataNodes: dataNodeCount, dedicatedManagerNodeType: dedicatedManagerNodeType, dedicatedManagerNodeCount: dedicatedManagerNodeCount, warmInstanceType: warmNodeType, warmNodes: warmNodeCount, - zoneAwarenessEnabled: zoneAwarenessEnabled, - zoneAwarenessAvailabilityZoneCount: zoneAwarenessAvailabilityZoneCount, + accessPolicies: accessPolicies, useUnsignedBasicAuth: useUnsignedBasicAuth, fineGrainedManagerUserARN: fineGrainedManagerUserARN, fineGrainedManagerUserName: fineGrainedManagerUserName, fineGrainedManagerUserSecretManagerKeyARN: fineGrainedManagerUserSecretManagerKeyARN, enforceHTTPS: enforceHTTPS, tlsSecurityPolicy: tlsSecurityPolicy, - nodeToNodeEncryptionEnabled: noneToNodeEncryptionEnabled, - encryptionAtRestEnabled: encryptionAtRestEnabled, - encryptionAtRestKmsKeyARN: encryptionAtRestKmsKeyARN, ebsEnabled: ebsEnabled, ebsIops: ebsIops, ebsVolumeSize: ebsVolumeSize, ebsVolumeType: ebsVolumeType, + encryptionAtRestEnabled: encryptionAtRestEnabled, + encryptionAtRestKmsKeyARN: encryptionAtRestKmsKeyARN, appLogEnabled: loggingAppLogEnabled, appLogGroup: loggingAppLogGroupARN, - auditLogEnabled: loggingAuditLogEnabled, - auditLogGroup: loggingAuditLogGroupARN, - slowIndexLogEnabled: loggingSlowIndexLogEnabled, - slowIndexLogGroup: loggingSlowIndexLogGroupARN, - slowSearchLogEnabled: loggingSlowSearchLogEnabled, - slowSearchLogGroup: loggingSlowSearchLogGroupARN, - snapshotAutomatedStartHour: snapshotAutomatedStartHour, + nodeToNodeEncryptionEnabled: noneToNodeEncryptionEnabled, vpcId: vpcId, - vpcSecurityGroupIds: vpcSecurityGroupIds, - vpcSubnetIds: vpcSubnetIds, domainRemovalPolicy: domainRemovalPolicy, ...props, }); + this.stacks.push(opensearchStack) + function getContextForType(optionName: string, expectedType: string): any { const option = scope.node.tryGetContext(optionName) + + // If no context is provided and a default value exists, use it + if (option === undefined && defaultValues[optionName]) { + return defaultValues[optionName] + } + // Filter out invalid or missing options by setting undefined (empty strings, null, undefined, NaN) if (option !== false && option !== 0 && !option) { return undefined diff --git a/opensearch-service-domain-cdk/tsconfig.json b/opensearch-service-domain-cdk/tsconfig.json index fc44377a1..4c83b7c87 100644 --- a/opensearch-service-domain-cdk/tsconfig.json +++ b/opensearch-service-domain-cdk/tsconfig.json @@ -5,6 +5,7 @@ "lib": [ "es2020" ], + "resolveJsonModule": true, "declaration": true, "strict": true, "noImplicitAny": true, From 525118b9d7e3c6928dc91f9f67ea956d42b84a06 Mon Sep 17 00:00:00 2001 From: Tanner Lewis Date: Tue, 14 Feb 2023 15:05:40 -0500 Subject: [PATCH 7/8] MIGRATIONS-876: Minor updates and README adjust Signed-off-by: Tanner Lewis --- opensearch-service-domain-cdk/README.md | 28 +++++++------------ opensearch-service-domain-cdk/bin/app.ts | 2 +- .../default-values.json | 2 +- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/opensearch-service-domain-cdk/README.md b/opensearch-service-domain-cdk/README.md index d640dbaa3..3300c54df 100644 --- a/opensearch-service-domain-cdk/README.md +++ b/opensearch-service-domain-cdk/README.md @@ -6,11 +6,10 @@ If this is your first time using CDK in this region, will need to `cdk bootstrap Also ensure you have configured the desired [AWS credentials](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_prerequisites), as these will dictate the region and account used for deployment -A `CDK_DEPLOYMENT_STAGE` environment variable should also be set to assist in naming resources and preventing collisions +A `CDK_DEPLOYMENT_STAGE` environment variable should also be set to assist in naming resources and preventing collisions. Typically, this would be set to values such as `dev`, `gamma`, or `PROD` and will be used to distinguish AWS resources for a given region and deployment stage. For example the CloudFormation stack may be named like `OpenSearchServiceDomain-dev-us-east-1` ### Deploying your Domain Stack -Before deploying your Domain stack you should fill in any desired context parameters that will dictate the composition -of your OpenSearch Service Domain +Before deploying your Domain stack you should fill in any desired context parameters that will dictate the composition of your OpenSearch Service Domain This can be accomplished by providing these options in a `cdk.context.json` file @@ -20,20 +19,17 @@ cdk deploy --c domainName='cdk-os-service-domain' --c engineVersion="OS_1_3_6" - ``` * Note that these context parameters can also be passed to `cdk synth` and `cdk bootstrap` commands to simulate similar scenarios -Depending on your use-case, you may choose to provide options from both the `cdk.context.json` and the CDK CLI, in which case it is important -to know the precedence level for context values. The below order shows these levels with values placed in the `cdk.context.json` having the most importance -1. Created `cdk.context.json` in base directory +Depending on your use-case, you may choose to provide options from both the `cdk.context.json` and the CDK CLI, in which case it is important to know the precedence level for context values. The below order shows these levels with values placed in the `cdk.context.json` having the most importance +1. Created `cdk.context.json` in top level directory 2. CDK CLI passed context values -3. Existing `default-values.json` in base directory +3. Existing `default-values.json` in top level directory ### Configuration Options -The available configuration options are listed below. The vast majority of these options do not need to be provided, with only `domainName` and `engineVersion` being required. -All non-required options can be provided as an empty string `""` or simply not included, and in each of these cases the option will be allocated with the CDK Domain default value +The available configuration options are listed below. The vast majority of these options do not need to be provided, with only `domainName` and `engineVersion` being required. All non-required options can be provided as an empty string `""` or simply not included, and in each of these cases the option will be allocated with the CDK Domain default value -Users are encouraged to customize the deployment by changing the CDK TypeScript as needed. The configuration-by-context option that is depicted here is primarily provided for testing/development purposes, -and users may find it easier to adjust the TS here rather than say wrangling a complex JSON object through a context option +Users are encouraged to customize the deployment by changing the CDK TypeScript as needed. The configuration-by-context option that is depicted here is primarily provided for testing/development purposes, and users may find it easier to adjust the TS here rather than say wrangling a complex JSON object through a context option Additional context on some of these options, can also be found in the Domain construct [documentation](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_opensearchservice.Domain.html) @@ -101,8 +97,7 @@ A template `cdk.context.json` to be used to fill in these values is below: } ``` -Some configuration options available in other solutions (listed below) which enable/disable specific features do not exist in the current native CDK Domain construct. These options are inferred based on the presence or absence of related fields (i.e. if dedicatedMasterNodeCount is set to 1 it is -inferred that dedicated master nodes should be enabled). These options are normally disabled by default, allowing for this inference. +Some configuration options available in other solutions (listed below) which enable/disable specific features do not exist in the current native CDK Domain construct. These options are inferred based on the presence or absence of related fields (i.e. if dedicatedMasterNodeCount is set to 1 it is inferred that dedicated master nodes should be enabled). These options are normally disabled by default, allowing for this inference. ``` "dedicatedMasterNodeEnabled": "X", "warmNodeEnabled": "X", @@ -111,14 +106,11 @@ inferred that dedicated master nodes should be enabled). These options are norma ``` ### Tearing down CDK Stack -To remove the stack which gets created during deployment, which contains our created resources like our Domain and any other resources created from enabled -features (such as a CloudWatch log group), we can execute +To remove the stack which gets created during deployment, which contains our created resources like our Domain and any other resources created from enabled features (such as a CloudWatch log group), we can execute ``` cdk destroy ``` -Note that the default retention policy for the OpenSearch Domain is to RETAIN this resource when the stack is deleted, and in order to delete the Domain -on stack deletion the `domainRemovalPolicy` would need to be set to `DESTROY`. Otherwise, the Domain can be manually deleted through the AWS console or -through other means such as the AWS CLI. +Note that the default retention policy for the OpenSearch Domain is to RETAIN this resource when the stack is deleted, and in order to delete the Domain on stack deletion the `domainRemovalPolicy` would need to be set to `DESTROY`. Otherwise, the Domain can be manually deleted through the AWS console or through other means such as the AWS CLI. ### Useful CDK commands diff --git a/opensearch-service-domain-cdk/bin/app.ts b/opensearch-service-domain-cdk/bin/app.ts index 7c1e31b94..c391d6187 100644 --- a/opensearch-service-domain-cdk/bin/app.ts +++ b/opensearch-service-domain-cdk/bin/app.ts @@ -9,6 +9,6 @@ const account = process.env.CDK_DEFAULT_ACCOUNT const region = process.env.CDK_DEFAULT_REGION new StackComposer(app, { env: { account: account, region: region }, - stackName: `OpenSearchServiceDomainCDKStack-${stage}-${region}`, + stackName: `OSServiceDomainCDKStack-${stage}-${region}`, description: "This stack contains resources to create/manage an OpenSearch Service domain" }); \ No newline at end of file diff --git a/opensearch-service-domain-cdk/default-values.json b/opensearch-service-domain-cdk/default-values.json index 30eae4dcd..539873991 100644 --- a/opensearch-service-domain-cdk/default-values.json +++ b/opensearch-service-domain-cdk/default-values.json @@ -1,4 +1,4 @@ { "engineVersion": "OS_1.3", - "domainName": "cdk-os-service-domain" + "domainName": "os-service-domain" } \ No newline at end of file From b887998098525d9b35b657e77e008ee7a03867ff Mon Sep 17 00:00:00 2001 From: Tanner Lewis Date: Tue, 14 Feb 2023 16:30:21 -0500 Subject: [PATCH 8/8] MIGRATIONS-876: Update README Signed-off-by: Tanner Lewis --- opensearch-service-domain-cdk/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opensearch-service-domain-cdk/README.md b/opensearch-service-domain-cdk/README.md index 3300c54df..9996cf706 100644 --- a/opensearch-service-domain-cdk/README.md +++ b/opensearch-service-domain-cdk/README.md @@ -20,9 +20,9 @@ cdk deploy --c domainName='cdk-os-service-domain' --c engineVersion="OS_1_3_6" - * Note that these context parameters can also be passed to `cdk synth` and `cdk bootstrap` commands to simulate similar scenarios Depending on your use-case, you may choose to provide options from both the `cdk.context.json` and the CDK CLI, in which case it is important to know the precedence level for context values. The below order shows these levels with values placed in the `cdk.context.json` having the most importance -1. Created `cdk.context.json` in top level directory +1. Created `cdk.context.json` in the same directory as this README 2. CDK CLI passed context values -3. Existing `default-values.json` in top level directory +3. Existing `default-values.json` in the same directory as this README ### Configuration Options