From 63a3909063637ca2306a718a10e35e54881f570e Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Fri, 13 Jan 2023 11:49:25 +0100 Subject: [PATCH 01/56] fix: hardcoded cdk version in `publish_layer.yaml` (#1232) --- .github/workflows/publish_layer.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish_layer.yaml b/.github/workflows/publish_layer.yaml index 5f5baf9a45..25142f11b8 100644 --- a/.github/workflows/publish_layer.yaml +++ b/.github/workflows/publish_layer.yaml @@ -43,7 +43,7 @@ jobs: echo "RELEASE_TAG_VERSION=${RELEASE_TAG_VERSION:1}" >> $GITHUB_ENV - name: install cdk and deps run: | - npm install -g aws-cdk@2.29.0 + npm install -g aws-cdk@2.55.0 cdk --version - name: install deps run: | From 0116c86bbd935c99507f97d0eb6d757f3720771b Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Fri, 13 Jan 2023 12:04:15 +0100 Subject: [PATCH 02/56] chore: bump hardcoded version in `reusable_deploy_layer_stack.yml` (#1234) --- .github/workflows/reusable_deploy_layer_stack.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable_deploy_layer_stack.yml b/.github/workflows/reusable_deploy_layer_stack.yml index 3de0d8aba2..ef75a81c8d 100644 --- a/.github/workflows/reusable_deploy_layer_stack.yml +++ b/.github/workflows/reusable_deploy_layer_stack.yml @@ -65,7 +65,7 @@ jobs: node-version: "18" - name: install cdk and deps run: | - npm install -g aws-cdk@2.29.0 + npm install -g aws-cdk@2.55.0 cdk --version - name: install deps run: | From b856e61589e331d06d3cda7343a33ac9e6de0bc5 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Fri, 13 Jan 2023 12:16:54 +0100 Subject: [PATCH 03/56] docs: update layer version to 7 (#1235) --- docs/index.md | 90 +++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/docs/index.md b/docs/index.md index ef585e68a1..22664c98b9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,7 +26,7 @@ You can use Powertools in both TypeScript and JavaScript code bases. Powertools is available in the following formats: -* **Lambda Layer**: [**arn:aws:lambda:{region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:6**](#){: .copyMe}:clipboard: +* **Lambda Layer**: [**arn:aws:lambda:{region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7**](#){: .copyMe}:clipboard: * **npm**: **`npm install @aws-lambda-powertools/tracer @aws-lambda-powertools/metrics @aws-lambda-powertools/logger`** ### Lambda Layer @@ -37,25 +37,25 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: ??? note "Note: Click to expand and copy any regional Lambda Layer ARN" - | Region | Layer ARN - |--------------------------- | --------------------------- - | `us-east-1` | [arn:aws:lambda:us-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `us-east-2` | [arn:aws:lambda:us-east-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `us-west-1` | [arn:aws:lambda:us-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `us-west-2` | [arn:aws:lambda:us-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `ap-south-1` | [arn:aws:lambda:ap-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `eu-central-1` | [arn:aws:lambda:eu-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `eu-west-1` | [arn:aws:lambda:eu-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `eu-west-2` | [arn:aws:lambda:eu-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `eu-west-3` | [arn:aws:lambda:eu-west-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `eu-north-1` | [arn:aws:lambda:eu-north-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `ca-central-1` | [arn:aws:lambda:ca-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: - | `sa-east-1` | [arn:aws:lambda:sa-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:6](#){: .copyMe}:clipboard: + | Region | Layer ARN | + | ---------------- | ----------------------------------------------------------------------------------------------------------- | + | `us-east-1` | [arn:aws:lambda:us-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `us-east-2` | [arn:aws:lambda:us-east-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `us-west-1` | [arn:aws:lambda:us-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `us-west-2` | [arn:aws:lambda:us-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `ap-south-1` | [arn:aws:lambda:ap-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `eu-central-1` | [arn:aws:lambda:eu-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `eu-west-1` | [arn:aws:lambda:eu-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `eu-west-2` | [arn:aws:lambda:eu-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `eu-west-3` | [arn:aws:lambda:eu-west-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `eu-north-1` | [arn:aws:lambda:eu-north-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `ca-central-1` | [arn:aws:lambda:ca-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `sa-east-1` | [arn:aws:lambda:sa-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | ??? note "Note: Click to expand and copy code snippets for popular frameworks" @@ -66,7 +66,7 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: Type: AWS::Serverless::Function Properties: Layers: - - !Sub arn:aws:lambda:${AWS::Region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:6 + - !Sub arn:aws:lambda:${AWS::Region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7 ``` If you use `esbuild` to bundle your code, make sure to exclude `@aws-lambda-powertools` from being bundled since the packages will be already present the Layer: @@ -97,7 +97,7 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: hello: handler: lambda_function.lambda_handler layers: - - arn:aws:lambda:${aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:6 + - arn:aws:lambda:${aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7 ``` If you use `esbuild` to bundle your code, make sure to exclude `@aws-lambda-powertools` from being bundled since the packages will be already present the Layer: @@ -129,7 +129,7 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: const powertoolsLayer = lambda.LayerVersion.fromLayerVersionArn( this, 'PowertoolsLayer', - `arn:aws:lambda:${cdk.Stack.of(this).region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:6` + `arn:aws:lambda:${cdk.Stack.of(this).region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7` ); new lambda.Function(this, 'Function', { @@ -181,7 +181,7 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: role = ... handler = "index.handler" runtime = "nodejs16.x" - layers = ["arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:6"] + layers = ["arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7"] source_code_hash = filebase64sha256("lambda_function_payload.zip") } ``` @@ -199,7 +199,7 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: const lambdaFunction = new aws.lambda.Function("function", { layers: [ - pulumi.interpolate`arn:aws:lambda:${aws.getRegionOutput().name}:094274105915:layer:AWSLambdaPowertoolsTypeScript:6` + pulumi.interpolate`arn:aws:lambda:${aws.getRegionOutput().name}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7` ], code: new pulumi.asset.FileArchive("lambda_function_payload.zip"), tracingConfig: { @@ -223,7 +223,7 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: ? Do you want to configure advanced settings? Yes ... ? Do you want to enable Lambda layers for this function? Yes - ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:6 + ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7 ❯ amplify push -y # Updating an existing function and add the layer @@ -233,13 +233,13 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: - Name: ? Which setting do you want to update? Lambda layers configuration ? Do you want to enable Lambda layers for this function? Yes - ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:6 + ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7 ? Do you want to edit the local lambda function now? No ``` === "Get the Layer .zip contents" ```bash title="AWS CLI" - aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:6 --region {region} + aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7 --region {region} ``` The pre-signed URL to download this Lambda Layer will be within `Location` key. @@ -272,29 +272,29 @@ If instead you want to see Powertools for TypeScript in a slightly more complex Core utilities such as Tracing, Logging, and Metrics will be available across all Lambda Powertools languages. Additional utilities are subjective to each language ecosystem and customer demand. -| Utility | Description | -| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -| [Tracer](./core/tracer.md) | Decorators and utilities to trace Lambda function handlers, and both synchronous and asynchronous functions | -| [Logger](./core/logger.md) | Structured logging made easier, and a middleware to enrich structured logging with key Lambda context details | -| [Metrics](./core/metrics.md) | Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF) | +| Utility | Description | +| ---------------------------- | ------------------------------------------------------------------------------------------------------------- | +| [Tracer](./core/tracer.md) | Decorators and utilities to trace Lambda function handlers, and both synchronous and asynchronous functions | +| [Logger](./core/logger.md) | Structured logging made easier, and a middleware to enrich structured logging with key Lambda context details | +| [Metrics](./core/metrics.md) | Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF) | ## Environment variables ???+ info Explicit parameters take precedence over environment variables -| Environment variable | Description | Utility | Default | -|----------------------------------------------|----------------------------------------------------------------------------------------|---------------------------|-----------------------| -| **POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | All | `service_undefined` | -| **POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics) | `default_namespace` | -| **POWERTOOLS_TRACE_ENABLED** | Explicitly disables tracing | [Tracer](./core/tracer) | `true` | -| **POWERTOOLS_TRACER_CAPTURE_RESPONSE** | Captures Lambda or method return as metadata. | [Tracer](./core/tracer) | `true` | -| **POWERTOOLS_TRACER_CAPTURE_ERROR** | Captures Lambda or method exception as metadata. | [Tracer](./core/tracer) | `true` | -| **POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS** | Captures HTTP(s) requests as segments. | [Tracer](./core/tracer) | `true` | -| **POWERTOOLS_LOGGER_LOG_EVENT** | Logs incoming event | [Logger](./core/logger) | `false` | -| **POWERTOOLS_LOGGER_SAMPLE_RATE** | Debug log sampling | [Logger](./core/logger) | `0` | -| **POWERTOOLS_DEV** | Increase JSON indentation to ease debugging when running functions locally or in a non-production environment | [Logger](./core/logger) | `false` | -| **LOG_LEVEL** | Sets logging level | [Logger](./core/logger) | `INFO` | +| Environment variable | Description | Utility | Default | +| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------- | ------------------- | +| **POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | All | `service_undefined` | +| **POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics) | `default_namespace` | +| **POWERTOOLS_TRACE_ENABLED** | Explicitly disables tracing | [Tracer](./core/tracer) | `true` | +| **POWERTOOLS_TRACER_CAPTURE_RESPONSE** | Captures Lambda or method return as metadata. | [Tracer](./core/tracer) | `true` | +| **POWERTOOLS_TRACER_CAPTURE_ERROR** | Captures Lambda or method exception as metadata. | [Tracer](./core/tracer) | `true` | +| **POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS** | Captures HTTP(s) requests as segments. | [Tracer](./core/tracer) | `true` | +| **POWERTOOLS_LOGGER_LOG_EVENT** | Logs incoming event | [Logger](./core/logger) | `false` | +| **POWERTOOLS_LOGGER_SAMPLE_RATE** | Debug log sampling | [Logger](./core/logger) | `0` | +| **POWERTOOLS_DEV** | Increase JSON indentation to ease debugging when running functions locally or in a non-production environment | [Logger](./core/logger) | `false` | +| **LOG_LEVEL** | Sets logging level | [Logger](./core/logger) | `INFO` | Each Utility page provides information on example values and allowed values From 3620bb205ff81db6a97df0f6a1d75c963d271d90 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Fri, 13 Jan 2023 15:57:05 +0100 Subject: [PATCH 04/56] docs: remove deprecation notice from docs (#1237) --- docs/overrides/main.html | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/overrides/main.html b/docs/overrides/main.html index 540387c9a7..0d556b6813 100644 --- a/docs/overrides/main.html +++ b/docs/overrides/main.html @@ -5,13 +5,4 @@ Click here to go to latest. -{% endblock %} - -{% block announce %} -Version 1.5.0 will be the last version to support Node.js 12.
Node.js 12 has reached end-of-life and the -corresponding -AWS Lambda runtime will be deprecated at the end of March 2023. Please upgrade to Node.js 14 or later.
-Click here to read the full announcement, or join our Discord server to chime in the discussion. {% endblock %} \ No newline at end of file From f3e5ed70c7e5baa3f3aa15428e8d6cb56b096f26 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Fri, 13 Jan 2023 16:55:31 +0100 Subject: [PATCH 05/56] fix(parameters): Tokenize attribute names in `DynamoDBProvider` (#1239) * docs: fixed install command * build: restored @aws-sdk/util-base64-node as dependency * fix: projection names * tests: fixed unit tests after changes in prev commit --- docs/utilities/parameters.md | 2 +- package-lock.json | 30 ++++++++-- packages/parameters/package.json | 2 +- packages/parameters/src/BaseProvider.ts | 2 +- .../src/dynamodb/DynamoDBProvider.ts | 14 ++++- .../tests/unit/BaseProvider.test.ts | 2 +- .../tests/unit/DynamoDBProvider.test.ts | 56 +++++++++++++++---- 7 files changed, 85 insertions(+), 23 deletions(-) diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 9c659e1293..c9d43e7ccc 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -43,7 +43,7 @@ Depending on the provider you want to use, install the library and the correspon === "DynamoDBProvider" ```bash - npm install @aws-lambda-powertools/parameters @aws-sdk/client-dynamodb + npm install @aws-lambda-powertools/parameters @aws-sdk/client-dynamodb @aws-sdk/util-dynamodb ``` ???+ tip diff --git a/package-lock.json b/package-lock.json index 5988ec4416..0fef505458 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1862,6 +1862,19 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/util-base64-node": { + "version": "3.209.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.209.0.tgz", + "integrity": "sha512-U6pjb6uF/BameQLmzoSrqeiTxu5otwwGV7fO+TyE/3SJm/lyIsBaO+wr0qsoK0ae1VqggR+KCsUG13pWhdltpw==", + "deprecated": "The package @aws-sdk/util-base64-node has been renamed to @aws-sdk/util-base64. Please install the renamed package.", + "dependencies": { + "@aws-sdk/util-buffer-from": "3.208.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/util-body-length-browser": { "version": "3.188.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.188.0.tgz", @@ -17240,7 +17253,7 @@ "version": "1.5.0", "license": "MIT-0", "dependencies": { - "@aws-sdk/util-base64": "^3.208.0" + "@aws-sdk/util-base64-node": "^3.209.0" }, "devDependencies": { "@aws-sdk/client-appconfigdata": "^3.241.0", @@ -17532,11 +17545,11 @@ "version": "file:packages/parameters", "requires": { "@aws-sdk/client-appconfigdata": "^3.241.0", - "@aws-sdk/client-dynamodb": "*", + "@aws-sdk/client-dynamodb": "^3.245.0", "@aws-sdk/client-secrets-manager": "^3.238.0", "@aws-sdk/client-ssm": "^3.244.0", - "@aws-sdk/util-base64": "^3.208.0", - "@aws-sdk/util-dynamodb": "*", + "@aws-sdk/util-base64-node": "^3.209.0", + "@aws-sdk/util-dynamodb": "^3.245.0", "aws-sdk-client-mock": "^2.0.1", "aws-sdk-client-mock-jest": "^2.0.1" }, @@ -18872,6 +18885,15 @@ "tslib": "^2.3.1" } }, + "@aws-sdk/util-base64-node": { + "version": "3.209.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.209.0.tgz", + "integrity": "sha512-U6pjb6uF/BameQLmzoSrqeiTxu5otwwGV7fO+TyE/3SJm/lyIsBaO+wr0qsoK0ae1VqggR+KCsUG13pWhdltpw==", + "requires": { + "@aws-sdk/util-buffer-from": "3.208.0", + "tslib": "^2.3.1" + } + }, "@aws-sdk/util-body-length-browser": { "version": "3.188.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.188.0.tgz", diff --git a/packages/parameters/package.json b/packages/parameters/package.json index 55afcf9166..bc1bf31e9b 100644 --- a/packages/parameters/package.json +++ b/packages/parameters/package.json @@ -60,6 +60,6 @@ "aws-sdk-client-mock-jest": "^2.0.1" }, "dependencies": { - "@aws-sdk/util-base64": "^3.208.0" + "@aws-sdk/util-base64-node": "^3.209.0" } } diff --git a/packages/parameters/src/BaseProvider.ts b/packages/parameters/src/BaseProvider.ts index 2d993c71a2..3e4b2a1474 100644 --- a/packages/parameters/src/BaseProvider.ts +++ b/packages/parameters/src/BaseProvider.ts @@ -1,4 +1,4 @@ -import { fromBase64 } from '@aws-sdk/util-base64'; +import { fromBase64 } from '@aws-sdk/util-base64-node'; import { GetOptions } from './GetOptions'; import { GetMultipleOptions } from './GetMultipleOptions'; import { ExpirableValue } from './ExpirableValue'; diff --git a/packages/parameters/src/dynamodb/DynamoDBProvider.ts b/packages/parameters/src/dynamodb/DynamoDBProvider.ts index f99ab90651..7fb5152553 100644 --- a/packages/parameters/src/dynamodb/DynamoDBProvider.ts +++ b/packages/parameters/src/dynamodb/DynamoDBProvider.ts @@ -49,7 +49,10 @@ class DynamoDBProvider extends BaseProvider { ...(options?.sdkOptions || {}), TableName: this.tableName, Key: marshall({ [this.keyAttr]: name }), - ProjectionExpression: this.valueAttr, + ProjectionExpression: '#value', + ExpressionAttributeNames: { + '#value': this.valueAttr, + } }; const result = await this.client.send(new GetItemCommand(sdkOptions)); @@ -63,9 +66,14 @@ class DynamoDBProvider extends BaseProvider { const sdkOptions: QueryCommandInput = { ...(options?.sdkOptions || {}), TableName: this.tableName, - KeyConditionExpression: `${this.keyAttr} = :key`, + KeyConditionExpression: '#key = :key', ExpressionAttributeValues: marshall({ ':key': path }), - ProjectionExpression: `${this.sortAttr}, ${this.valueAttr}`, + ExpressionAttributeNames: { + '#key': this.keyAttr, + '#sk': this.sortAttr, + '#value': this.valueAttr, + }, + ProjectionExpression: '#sk, #value', }; const paginationOptions: PaginationConfiguration = { client: this.client, diff --git a/packages/parameters/tests/unit/BaseProvider.test.ts b/packages/parameters/tests/unit/BaseProvider.test.ts index bc64b55150..3b8f029d64 100644 --- a/packages/parameters/tests/unit/BaseProvider.test.ts +++ b/packages/parameters/tests/unit/BaseProvider.test.ts @@ -9,7 +9,7 @@ import { GetParameterError, TransformParameterError } from '../../src'; -import { toBase64 } from '@aws-sdk/util-base64'; +import { toBase64 } from '@aws-sdk/util-base64-node'; const encoder = new TextEncoder(); diff --git a/packages/parameters/tests/unit/DynamoDBProvider.test.ts b/packages/parameters/tests/unit/DynamoDBProvider.test.ts index 2ecbd4dd44..05dae38d47 100644 --- a/packages/parameters/tests/unit/DynamoDBProvider.test.ts +++ b/packages/parameters/tests/unit/DynamoDBProvider.test.ts @@ -59,7 +59,10 @@ describe('Class: DynamoDBProvider', () => { Key: marshall({ id: parameterName, }), - ProjectionExpression: 'value', + ExpressionAttributeNames: { + '#value': 'value', + }, + ProjectionExpression: '#value', }); expect(parameter).toEqual(parameterValue); @@ -91,7 +94,10 @@ describe('Class: DynamoDBProvider', () => { Key: marshall({ key: parameterName, }), - ProjectionExpression: 'val', + ExpressionAttributeNames: { + '#value': 'val', + }, + ProjectionExpression: '#value', }); expect(parameter).toEqual(parameterValue); @@ -125,7 +131,10 @@ describe('Class: DynamoDBProvider', () => { Key: marshall({ id: parameterName, }), - ProjectionExpression: 'value', + ExpressionAttributeNames: { + '#value': 'value', + }, + ProjectionExpression: '#value', ConsistentRead: true, }); expect(parameter).toEqual(parameterValue); @@ -164,7 +173,10 @@ describe('Class: DynamoDBProvider', () => { Key: marshall({ id: parameterName, }), - ProjectionExpression: 'value', + ExpressionAttributeNames: { + '#value': 'value', + }, + ProjectionExpression: '#value', }); }); @@ -206,11 +218,16 @@ describe('Class: DynamoDBProvider', () => { // Assess expect(client).toReceiveCommandWith(QueryCommand, { TableName: 'test-table', - KeyConditionExpression: `id = :key`, + KeyConditionExpression: `#key = :key`, ExpressionAttributeValues: marshall({ ':key': parameterPath, }), - ProjectionExpression: 'sk, value', + ExpressionAttributeNames: { + '#key': 'id', + '#sk': 'sk', + '#value': 'value' + }, + ProjectionExpression: '#sk, #value', }); expect(parameters).toEqual({ a: 'parameter-a', @@ -256,11 +273,16 @@ describe('Class: DynamoDBProvider', () => { // Assess expect(client).toReceiveCommandWith(QueryCommand, { TableName: 'test-table', - KeyConditionExpression: `key = :key`, + KeyConditionExpression: `#key = :key`, ExpressionAttributeValues: marshall({ ':key': parameterPath, }), - ProjectionExpression: 'sort, val', + ExpressionAttributeNames: { + '#key': 'key', + '#sk': 'sort', + '#value': 'val' + }, + ProjectionExpression: '#sk, #value', }); expect(parameters).toEqual({ a: 'parameter-a', @@ -308,11 +330,16 @@ describe('Class: DynamoDBProvider', () => { // Assess expect(client).toReceiveCommandWith(QueryCommand, { TableName: 'test-table', - KeyConditionExpression: `id = :key`, + KeyConditionExpression: `#key = :key`, ExpressionAttributeValues: marshall({ ':key': parameterPath, }), - ProjectionExpression: 'sk, value', + ExpressionAttributeNames: { + '#key': 'id', + '#sk': 'sk', + '#value': 'value' + }, + ProjectionExpression: '#sk, #value', ConsistentRead: true, }); expect(parameters).toEqual({ @@ -419,11 +446,16 @@ describe('Class: DynamoDBProvider', () => { // Assess expect(client).toReceiveCommandWith(QueryCommand, { TableName: 'test-table', - KeyConditionExpression: `id = :key`, + KeyConditionExpression: `#key = :key`, ExpressionAttributeValues: marshall({ ':key': parameterPath, }), - ProjectionExpression: 'sk, value', + ExpressionAttributeNames: { + '#key': 'id', + '#sk': 'sk', + '#value': 'value' + }, + ProjectionExpression: '#sk, #value', ConsistentRead: true, Limit: 10, }); From fd8de2727bb67056932651be67f433d9253e4a37 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 16 Jan 2023 10:22:14 +0100 Subject: [PATCH 06/56] chore: added @middy/core license to proejct third-party licenses (#1249) --- LICENSE-THIRD-PARTY | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/LICENSE-THIRD-PARTY b/LICENSE-THIRD-PARTY index 4adf7eeb2f..98a63dfc1f 100644 --- a/LICENSE-THIRD-PARTY +++ b/LICENSE-THIRD-PARTY @@ -55,3 +55,29 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +****************************** + +@middy/core +3.6.2 +MIT License + +Copyright (c) 2017-2023 [Luciano Mammino](https://github.com/lmammino), [will Farrell](https://github.com/willfarrell) and the [Middy team](https://github.com/middyjs/middy/graphs/contributors) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file From 53a170546e9b944e91c8070e38d5ad702d01714a Mon Sep 17 00:00:00 2001 From: Niko Achilles Kokkinos Date: Mon, 16 Jan 2023 11:23:47 +0200 Subject: [PATCH 07/56] docs: correct link to Powertools for Java #1246 (#1247) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 22664c98b9..54fbecb549 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,7 +8,7 @@ A suite of utilities for AWS Lambda functions running on the Node.js runtime, to You can use Powertools in both TypeScript and JavaScript code bases. ???+ tip - Powertools is also available for [Python](https://awslabs.github.io/aws-lambda-powertools-python/){target="_blank"}, [Java](https://awslabs.github.io/aws-lambda-powertools-java/latest/){target="_blank"}, and [.NET](https://awslabs.github.io/aws-lambda-powertools-dotnet/){target="_blank"} + Powertools is also available for [Python](https://awslabs.github.io/aws-lambda-powertools-python/){target="_blank"}, [Java](https://awslabs.github.io/aws-lambda-powertools-java/){target="_blank"}, and [.NET](https://awslabs.github.io/aws-lambda-powertools-dotnet/){target="_blank"} ??? hint "Support this project by becoming a reference customer, sharing your work, or using Layers :heart:" From 439c794a98c3a024bb272b55de34a5e8c14b220d Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 16 Jan 2023 11:10:11 +0100 Subject: [PATCH 08/56] chore(docs): add `docs/snippets` to npm workspace (#1251) * chore: add docs/snippets folder to npm workspace * chore: added mkdocs plugin to exclude node_modules dir from docs * chore: housekeeping --- docs/Dockerfile | 2 +- docs/requirements.txt | 3 +- docs/snippets/package.json | 32 + mkdocs.yml | 4 + package-lock.json | 1306 +++--------------------------------- package.json | 9 +- 6 files changed, 146 insertions(+), 1210 deletions(-) create mode 100644 docs/snippets/package.json diff --git a/docs/Dockerfile b/docs/Dockerfile index 7084973a1f..40c4a3e4b4 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,2 +1,2 @@ FROM squidfunk/mkdocs-material -RUN pip install mkdocs-git-revision-date-plugin mkdocs-glightbox +RUN pip install mkdocs-git-revision-date-plugin==0.3.2 mkdocs-exclude==1.0.2 diff --git a/docs/requirements.txt b/docs/requirements.txt index 8a6ea54a95..9332acafa3 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,4 @@ mike==1.1.2 mkdocs-material==9.0.2 -mkdocs-git-revision-date-plugin==0.3.2 \ No newline at end of file +mkdocs-git-revision-date-plugin==0.3.2 +mkdocs-exclude==1.0.2 \ No newline at end of file diff --git a/docs/snippets/package.json b/docs/snippets/package.json new file mode 100644 index 0000000000..33aeb1247c --- /dev/null +++ b/docs/snippets/package.json @@ -0,0 +1,32 @@ +{ + "name": "docs", + "version": "0.0.1", + "description": "A collection code snippets for the AWS Lambda Powertools for TypeScript docs", + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com" + }, + "scripts": { + "test": "echo 'Not Applicable'", + "test:e2e": "echo 'Not Applicable'", + "build": "echo 'Not Applicable'", + "lint": "echo 'Not Applicable'", + "lint-fix": "echo 'Not Applicable'" + }, + "license": "MIT-0", + "repository": { + "type": "git", + "url": "git+https://github.com/awslabs/aws-lambda-powertools-typescript.git" + }, + "bugs": { + "url": "https://github.com/awslabs/aws-lambda-powertools-typescript/issues" + }, + "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript#readme", + "devDependencies": { + "@aws-sdk/client-appconfigdata": "^3.245.0", + "@aws-sdk/client-dynamodb": "^3.245.0", + "@aws-sdk/client-secrets-manager": "^3.250.0", + "@aws-sdk/client-ssm": "^3.245.0", + "@aws-sdk/util-dynamodb": "^3.245.0" + } +} \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 8bdae60ca0..bab9c616bb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -79,6 +79,10 @@ copyright: Copyright © 2023 Amazon Web Services plugins: - git-revision-date - search + - exclude: + glob: + - snippets/node_modules/* + - snippets/package.json extra_css: - stylesheets/extra.css diff --git a/package-lock.json b/package-lock.json index 0fef505458..015b32f41f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,8 @@ "packages/metrics", "packages/tracer", "packages/parameters", - "packages/idempotency" + "packages/idempotency", + "docs/snippets" ], "dependencies": { "hosted-git-info": "^6.1.1" @@ -58,6 +59,30 @@ "node": ">=14" } }, + "docs/snippets": { + "name": "docs", + "version": "0.0.1", + "license": "MIT-0", + "devDependencies": { + "@aws-sdk/client-appconfigdata": "^3.245.0", + "@aws-sdk/client-dynamodb": "^3.245.0", + "@aws-sdk/client-secrets-manager": "^3.250.0", + "@aws-sdk/client-ssm": "^3.245.0", + "@aws-sdk/util-dynamodb": "^3.245.0" + } + }, + "docs/snippets/node_modules/@aws-sdk/util-dynamodb": { + "version": "3.245.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.245.0.tgz", + "integrity": "sha512-Wx06Ey92DRRSQTFfpQs1DkHSoajcwVu2TLWTg07yHXgGxdi6EveJPNqh111ot5yzHGmzPIHas/IPZe3hDttzcw==", + "dev": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -347,150 +372,16 @@ } }, "node_modules/@aws-sdk/client-appconfigdata": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-appconfigdata/-/client-appconfigdata-3.241.0.tgz", - "integrity": "sha512-5AVfSc6ZQ17dF0cdIrmpEe0H6vmGumBvsFGn89e2clSPtqqtsFDpBeAsS5jCMbxO9pakkCM0RPAh+sci6MnlYg==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/client-sts": "3.241.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-node": "3.241.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.241.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/client-sso": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.241.0.tgz", - "integrity": "sha512-Jm4HR+RYAqKMEYZvvWaq0NYUKKonyInOeubObXH4BLXZpmUBSdYCSjjLdNJY3jkQoxbDVPVMIurVNh5zT5SMRw==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.241.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.241.0.tgz", - "integrity": "sha512-/Ml2QBGpGfUEeBrPzBZhSTBkHuXFD2EAZEIHGCBH4tKaURDI6/FoGI8P1Rl4BzoFt+II/Cr91Eox6YT9EwChsQ==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.241.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/client-sts": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.241.0.tgz", - "integrity": "sha512-vmlG8cJzRf8skCtTJbA2wBvD2c3NQ5gZryzJvTKDS06KzBzcEpnjlLseuTekcnOiRNekbFUX5hRu5Zj3N2ReLg==", + "version": "3.245.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-appconfigdata/-/client-appconfigdata-3.245.0.tgz", + "integrity": "sha512-XxyUEEYcL6xvFUnrd10sVtmWaqYTHwn9IfScqyqz2K8wrAnbHPeaWc197UulqhIXYfz11foOAEleiUBKM1kHdw==", "dev": true, "dependencies": { "@aws-crypto/sha256-browser": "2.0.0", "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/client-sts": "3.245.0", "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-node": "3.241.0", + "@aws-sdk/credential-provider-node": "3.245.0", "@aws-sdk/fetch-http-handler": "3.226.0", "@aws-sdk/hash-node": "3.226.0", "@aws-sdk/invalid-dependency": "3.226.0", @@ -500,7 +391,6 @@ "@aws-sdk/middleware-logger": "3.226.0", "@aws-sdk/middleware-recursion-detection": "3.226.0", "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-sdk-sts": "3.226.0", "@aws-sdk/middleware-serde": "3.226.0", "@aws-sdk/middleware-signing": "3.226.0", "@aws-sdk/middleware-stack": "3.226.0", @@ -516,195 +406,18 @@ "@aws-sdk/util-body-length-node": "3.208.0", "@aws-sdk/util-defaults-mode-browser": "3.234.0", "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.241.0", + "@aws-sdk/util-endpoints": "3.245.0", "@aws-sdk/util-retry": "3.229.0", "@aws-sdk/util-user-agent-browser": "3.226.0", "@aws-sdk/util-user-agent-node": "3.226.0", "@aws-sdk/util-utf8-browser": "3.188.0", "@aws-sdk/util-utf8-node": "3.208.0", - "fast-xml-parser": "4.0.11", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/config-resolver": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.234.0.tgz", - "integrity": "sha512-uZxy4wzllfvgCQxVc+Iqhde0NGAnfmV2hWR6ejadJaAFTuYNvQiRg9IqJy3pkyDPqXySiJ8Bom5PoJfgn55J/A==", - "dev": true, - "dependencies": { - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.241.0.tgz", - "integrity": "sha512-CI+mu6h74Kzmscw35TvNkc/wYHsHPGAwP7humSHoWw53H9mVw21Ggft/dT1iFQQZWQ8BNXkzuXlNo1IlqwMgOA==", - "dev": true, - "dependencies": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.241.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.241.0.tgz", - "integrity": "sha512-08zPQcD5o9brQmzEipWHeHgU85aQcEF8MWLfpeyjO6e1/l7ysQ35NsS+PYtv77nLpGCx/X+ZuW/KXWoRrbw77w==", - "dev": true, - "dependencies": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.241.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.241.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", "tslib": "^2.3.1" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.241.0.tgz", - "integrity": "sha512-6Bjd6eEIrVomRTrPrM4dlxusQm+KMJ9hLYKECCpFkwDKIK+pTgZNLRtQdalHyzwneHJPdimrm8cOv1kUQ8hPoA==", - "dev": true, - "dependencies": { - "@aws-sdk/client-sso": "3.241.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/token-providers": "3.241.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/middleware-retry": { - "version": "3.235.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.235.0.tgz", - "integrity": "sha512-50WHbJGpD3SNp9763MAlHqIhXil++JdQbKejNpHg7HsJne/ao3ub+fDOfx//mMBjpzBV25BGd5UlfL6blrClSg==", - "dev": true, - "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/service-error-classification": "3.229.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "tslib": "^2.3.1", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/smithy-client": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.234.0.tgz", - "integrity": "sha512-8AtR/k4vsFvjXeQbIzq/Wy7Nbk48Ou0wUEeVYPHWHPSU8QamFWORkOwmKtKMfHAyZvmqiAPeQqHFkq+UJhWyyQ==", - "dev": true, - "dependencies": { - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/token-providers": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.241.0.tgz", - "integrity": "sha512-79okvuOS7V559OIL/RalIPG98wzmWxeFOChFnbEjn2pKOyGQ6FJRwLPYZaVRtNdAtnkBNgRpmFq9dX843QxhtQ==", - "dev": true, - "dependencies": { - "@aws-sdk/client-sso-oidc": "3.241.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/util-defaults-mode-browser": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.234.0.tgz", - "integrity": "sha512-IHMKXjTbOD8XMz5+2oCOsVP94BYb9YyjXdns0aAXr2NAo7k2+RCzXQ2DebJXppGda1F6opFutoKwyVSN0cmbMw==", - "dev": true, - "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "bowser": "^2.11.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/util-defaults-mode-node": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.234.0.tgz", - "integrity": "sha512-UGjQ+OjBYYhxFVtUY+jtr0ZZgzZh6OHtYwRhFt8IHewJXFCfZTyfsbX20szBj5y1S4HRIUJ7cwBLIytTqMbI5w==", - "dev": true, - "dependencies": { - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/@aws-sdk/util-endpoints": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.241.0.tgz", - "integrity": "sha512-jVf8bKrN22Ey0xLmj75sL7EUvm5HFpuOMkXsZkuXycKhCwLBcEUWlvtJYtRjOU1zScPQv9GMJd2QXQglp34iOQ==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-appconfigdata/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@aws-sdk/client-dynamodb": { "version": "3.245.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.245.0.tgz", @@ -754,18 +467,6 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/util-endpoints": { - "version": "3.245.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.245.0.tgz", - "integrity": "sha512-UNOFquB1tKx+8RT8n82Zb5tIwDyZHVPBg/m0LB0RsLETjr6krien5ASpqWezsXKIR1hftN9uaxN4bvf2dZrWHg==", - "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@aws-sdk/client-dynamodb/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -775,151 +476,16 @@ } }, "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.238.0.tgz", - "integrity": "sha512-J3lmceh2txILEh8tWf4ldp8ThAh2WJBSa1t8oJhW58KuE+1a7cwOpgWO6MsSvOrGtp7qzTr8GMBUf7KLdv7wOg==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/client-sts": "3.238.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-node": "3.238.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/client-sso": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.238.0.tgz", - "integrity": "sha512-KHJJWP7hBDa9KLYiU5+hOb+3AAba93PhWebXkpKyQ/Bs+e7ECCreyLCwuME6uWTV01NDuFDpwZ6zUMpyNIcP6Q==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.238.0.tgz", - "integrity": "sha512-kazcA2Kp+cXQRtaZi5/T5YFfU9J3nzu1tXJsh0xAm+J3S9LS1ertY1bSX6KBed2xuxx2mfum8JRqli0TJad/pA==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/client-sts": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.238.0.tgz", - "integrity": "sha512-jQNwHqxWUGvWCN4o8KUFYQES8r41Oobu7x1KZOMrPhPxy27FUcDjBq/h85VoD2/AZlETSCZLiCnKV3KBh5pT5w==", + "version": "3.250.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.250.0.tgz", + "integrity": "sha512-CB4OcFJPpAuW3rRXBxgOtF9RXHBixEvJSDkh8RsCAamt37Fw61+e+CsdXixSWA4rzQA+3PQuts7zlwhcxobbJg==", "dev": true, "dependencies": { "@aws-crypto/sha256-browser": "2.0.0", "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/client-sts": "3.245.0", "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-node": "3.238.0", + "@aws-sdk/credential-provider-node": "3.245.0", "@aws-sdk/fetch-http-handler": "3.226.0", "@aws-sdk/hash-node": "3.226.0", "@aws-sdk/invalid-dependency": "3.226.0", @@ -929,7 +495,6 @@ "@aws-sdk/middleware-logger": "3.226.0", "@aws-sdk/middleware-recursion-detection": "3.226.0", "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-sdk-sts": "3.226.0", "@aws-sdk/middleware-serde": "3.226.0", "@aws-sdk/middleware-signing": "3.226.0", "@aws-sdk/middleware-stack": "3.226.0", @@ -941,122 +506,18 @@ "@aws-sdk/types": "3.226.0", "@aws-sdk/url-parser": "3.226.0", "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "fast-xml-parser": "4.0.11", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/config-resolver": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.234.0.tgz", - "integrity": "sha512-uZxy4wzllfvgCQxVc+Iqhde0NGAnfmV2hWR6ejadJaAFTuYNvQiRg9IqJy3pkyDPqXySiJ8Bom5PoJfgn55J/A==", - "dev": true, - "dependencies": { - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.238.0.tgz", - "integrity": "sha512-WmPNtIYyUasjV7VQxvPNq7ihmx0vFsiKAtjNjjakdrt5TPoma4nUYb9tIG9SuG+kcp4DJIgRLJAgZtXbCcVimg==", - "dev": true, - "dependencies": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.238.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.238.0.tgz", - "integrity": "sha512-/RN5EyGfgdIIJdFzv+O0nSaHX1/F3anQjTIBeVg8GJ+82m+bDxMdALsG+NzkYnLilN9Uhc1lSNjLBCoPa5DSEg==", - "dev": true, - "dependencies": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.238.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.238.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.238.0.tgz", - "integrity": "sha512-i70V4bFlCVYey3QARJ6XxKEg/4YuoFRnePV2oK37UHOGpEn49uXKwVZqLjzJgFHln7BPlC06cWDqrHUQIMvYrQ==", - "dev": true, - "dependencies": { - "@aws-sdk/client-sso": "3.238.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/token-providers": "3.238.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/smithy-client": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.234.0.tgz", - "integrity": "sha512-8AtR/k4vsFvjXeQbIzq/Wy7Nbk48Ou0wUEeVYPHWHPSU8QamFWORkOwmKtKMfHAyZvmqiAPeQqHFkq+UJhWyyQ==", - "dev": true, - "dependencies": { - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/token-providers": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.238.0.tgz", - "integrity": "sha512-vYUwmy0kTzA99mJCVvad+/5RDlWve/xxnppT8DJK3JIdAgskp+rULY+joVnq2NSl489UAioUnFGs57vUxi8Pog==", - "dev": true, - "dependencies": { - "@aws-sdk/client-sso-oidc": "3.238.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/util-body-length-browser": "3.188.0", + "@aws-sdk/util-body-length-node": "3.208.0", + "@aws-sdk/util-defaults-mode-browser": "3.234.0", + "@aws-sdk/util-defaults-mode-node": "3.234.0", + "@aws-sdk/util-endpoints": "3.245.0", + "@aws-sdk/util-retry": "3.229.0", + "@aws-sdk/util-user-agent-browser": "3.226.0", + "@aws-sdk/util-user-agent-node": "3.226.0", + "@aws-sdk/util-utf8-browser": "3.188.0", + "@aws-sdk/util-utf8-node": "3.208.0", + "tslib": "^2.3.1", + "uuid": "^8.3.2" }, "engines": { "node": ">=14.0.0" @@ -1120,19 +581,6 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/util-endpoints": { - "version": "3.245.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.245.0.tgz", - "integrity": "sha512-UNOFquB1tKx+8RT8n82Zb5tIwDyZHVPBg/m0LB0RsLETjr6krien5ASpqWezsXKIR1hftN9uaxN4bvf2dZrWHg==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@aws-sdk/client-ssm/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -1228,30 +676,6 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-endpoints": { - "version": "3.245.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.245.0.tgz", - "integrity": "sha512-UNOFquB1tKx+8RT8n82Zb5tIwDyZHVPBg/m0LB0RsLETjr6krien5ASpqWezsXKIR1hftN9uaxN4bvf2dZrWHg==", - "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/util-endpoints": { - "version": "3.245.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.245.0.tgz", - "integrity": "sha512-UNOFquB1tKx+8RT8n82Zb5tIwDyZHVPBg/m0LB0RsLETjr6krien5ASpqWezsXKIR1hftN9uaxN4bvf2dZrWHg==", - "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@aws-sdk/client-sts": { "version": "3.245.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.245.0.tgz", @@ -1299,18 +723,6 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-endpoints": { - "version": "3.245.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.245.0.tgz", - "integrity": "sha512-UNOFquB1tKx+8RT8n82Zb5tIwDyZHVPBg/m0LB0RsLETjr6krien5ASpqWezsXKIR1hftN9uaxN4bvf2dZrWHg==", - "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@aws-sdk/config-resolver": { "version": "3.234.0", "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.234.0.tgz", @@ -1959,10 +1371,9 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.226.0.tgz", - "integrity": "sha512-iqOkac/zLmyPBUJd7SLN0PeZMkOmlGgD5PHmmekTClOkce2eUjK9SNX1PzL73aXPoPTyhg9QGLH8uEZEQ8YUzg==", - "dev": true, + "version": "3.245.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.245.0.tgz", + "integrity": "sha512-UNOFquB1tKx+8RT8n82Zb5tIwDyZHVPBg/m0LB0RsLETjr6krien5ASpqWezsXKIR1hftN9uaxN4bvf2dZrWHg==", "dependencies": { "@aws-sdk/types": "3.226.0", "tslib": "^2.3.1" @@ -8778,6 +8189,10 @@ "node": ">=8" } }, + "node_modules/docs": { + "resolved": "docs/snippets", + "link": true + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -17208,7 +16623,7 @@ }, "packages/commons": { "name": "@aws-lambda-powertools/commons", - "version": "1.5.0", + "version": "1.5.1", "license": "MIT-0" }, "packages/idempotency": { @@ -17226,10 +16641,10 @@ }, "packages/logger": { "name": "@aws-lambda-powertools/logger", - "version": "1.5.0", + "version": "1.5.1", "license": "MIT", "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.0", + "@aws-lambda-powertools/commons": "^1.5.1", "lodash.merge": "^4.6.2" }, "devDependencies": { @@ -17238,10 +16653,10 @@ }, "packages/metrics": { "name": "@aws-lambda-powertools/metrics", - "version": "1.5.0", + "version": "1.5.1", "license": "MIT-0", "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.0" + "@aws-lambda-powertools/commons": "^1.5.1" }, "devDependencies": { "@types/promise-retry": "^1.1.3", @@ -17279,10 +16694,10 @@ }, "packages/tracer": { "name": "@aws-lambda-powertools/tracer", - "version": "1.5.0", + "version": "1.5.1", "license": "MIT-0", "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.0", + "@aws-lambda-powertools/commons": "^1.5.1", "aws-xray-sdk-core": "^3.4.0" }, "devDependencies": { @@ -17528,7 +16943,7 @@ "@aws-lambda-powertools/logger": { "version": "file:packages/logger", "requires": { - "@aws-lambda-powertools/commons": "^1.5.0", + "@aws-lambda-powertools/commons": "^1.5.1", "@types/lodash.merge": "^4.6.7", "lodash.merge": "^4.6.2" } @@ -17536,7 +16951,7 @@ "@aws-lambda-powertools/metrics": { "version": "file:packages/metrics", "requires": { - "@aws-lambda-powertools/commons": "^1.5.0", + "@aws-lambda-powertools/commons": "^1.5.1", "@types/promise-retry": "^1.1.3", "promise-retry": "^2.0.1" } @@ -17568,7 +16983,7 @@ "@aws-lambda-powertools/tracer": { "version": "file:packages/tracer", "requires": { - "@aws-lambda-powertools/commons": "^1.5.0", + "@aws-lambda-powertools/commons": "^1.5.1", "@aws-sdk/client-dynamodb": "^3.231.0", "@types/promise-retry": "^1.1.3", "aws-sdk": "^2.1276.0", @@ -17587,16 +17002,16 @@ } }, "@aws-sdk/client-appconfigdata": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-appconfigdata/-/client-appconfigdata-3.241.0.tgz", - "integrity": "sha512-5AVfSc6ZQ17dF0cdIrmpEe0H6vmGumBvsFGn89e2clSPtqqtsFDpBeAsS5jCMbxO9pakkCM0RPAh+sci6MnlYg==", + "version": "3.245.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-appconfigdata/-/client-appconfigdata-3.245.0.tgz", + "integrity": "sha512-XxyUEEYcL6xvFUnrd10sVtmWaqYTHwn9IfScqyqz2K8wrAnbHPeaWc197UulqhIXYfz11foOAEleiUBKM1kHdw==", "dev": true, "requires": { "@aws-crypto/sha256-browser": "2.0.0", "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/client-sts": "3.241.0", + "@aws-sdk/client-sts": "3.245.0", "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-node": "3.241.0", + "@aws-sdk/credential-provider-node": "3.245.0", "@aws-sdk/fetch-http-handler": "3.226.0", "@aws-sdk/hash-node": "3.226.0", "@aws-sdk/invalid-dependency": "3.226.0", @@ -17621,285 +17036,13 @@ "@aws-sdk/util-body-length-node": "3.208.0", "@aws-sdk/util-defaults-mode-browser": "3.234.0", "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.241.0", + "@aws-sdk/util-endpoints": "3.245.0", "@aws-sdk/util-retry": "3.229.0", "@aws-sdk/util-user-agent-browser": "3.226.0", "@aws-sdk/util-user-agent-node": "3.226.0", "@aws-sdk/util-utf8-browser": "3.188.0", "@aws-sdk/util-utf8-node": "3.208.0", "tslib": "^2.3.1" - }, - "dependencies": { - "@aws-sdk/client-sso": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.241.0.tgz", - "integrity": "sha512-Jm4HR+RYAqKMEYZvvWaq0NYUKKonyInOeubObXH4BLXZpmUBSdYCSjjLdNJY3jkQoxbDVPVMIurVNh5zT5SMRw==", - "dev": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.241.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/client-sso-oidc": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.241.0.tgz", - "integrity": "sha512-/Ml2QBGpGfUEeBrPzBZhSTBkHuXFD2EAZEIHGCBH4tKaURDI6/FoGI8P1Rl4BzoFt+II/Cr91Eox6YT9EwChsQ==", - "dev": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.241.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/client-sts": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.241.0.tgz", - "integrity": "sha512-vmlG8cJzRf8skCtTJbA2wBvD2c3NQ5gZryzJvTKDS06KzBzcEpnjlLseuTekcnOiRNekbFUX5hRu5Zj3N2ReLg==", - "dev": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-node": "3.241.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-sdk-sts": "3.226.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.241.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "fast-xml-parser": "4.0.11", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/config-resolver": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.234.0.tgz", - "integrity": "sha512-uZxy4wzllfvgCQxVc+Iqhde0NGAnfmV2hWR6ejadJaAFTuYNvQiRg9IqJy3pkyDPqXySiJ8Bom5PoJfgn55J/A==", - "dev": true, - "requires": { - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/credential-provider-ini": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.241.0.tgz", - "integrity": "sha512-CI+mu6h74Kzmscw35TvNkc/wYHsHPGAwP7humSHoWw53H9mVw21Ggft/dT1iFQQZWQ8BNXkzuXlNo1IlqwMgOA==", - "dev": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.241.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/credential-provider-node": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.241.0.tgz", - "integrity": "sha512-08zPQcD5o9brQmzEipWHeHgU85aQcEF8MWLfpeyjO6e1/l7ysQ35NsS+PYtv77nLpGCx/X+ZuW/KXWoRrbw77w==", - "dev": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.241.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.241.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/credential-provider-sso": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.241.0.tgz", - "integrity": "sha512-6Bjd6eEIrVomRTrPrM4dlxusQm+KMJ9hLYKECCpFkwDKIK+pTgZNLRtQdalHyzwneHJPdimrm8cOv1kUQ8hPoA==", - "dev": true, - "requires": { - "@aws-sdk/client-sso": "3.241.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/token-providers": "3.241.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/middleware-retry": { - "version": "3.235.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.235.0.tgz", - "integrity": "sha512-50WHbJGpD3SNp9763MAlHqIhXil++JdQbKejNpHg7HsJne/ao3ub+fDOfx//mMBjpzBV25BGd5UlfL6blrClSg==", - "dev": true, - "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/service-error-classification": "3.229.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "tslib": "^2.3.1", - "uuid": "^8.3.2" - } - }, - "@aws-sdk/smithy-client": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.234.0.tgz", - "integrity": "sha512-8AtR/k4vsFvjXeQbIzq/Wy7Nbk48Ou0wUEeVYPHWHPSU8QamFWORkOwmKtKMfHAyZvmqiAPeQqHFkq+UJhWyyQ==", - "dev": true, - "requires": { - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/token-providers": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.241.0.tgz", - "integrity": "sha512-79okvuOS7V559OIL/RalIPG98wzmWxeFOChFnbEjn2pKOyGQ6FJRwLPYZaVRtNdAtnkBNgRpmFq9dX843QxhtQ==", - "dev": true, - "requires": { - "@aws-sdk/client-sso-oidc": "3.241.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/util-defaults-mode-browser": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.234.0.tgz", - "integrity": "sha512-IHMKXjTbOD8XMz5+2oCOsVP94BYb9YyjXdns0aAXr2NAo7k2+RCzXQ2DebJXppGda1F6opFutoKwyVSN0cmbMw==", - "dev": true, - "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "bowser": "^2.11.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/util-defaults-mode-node": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.234.0.tgz", - "integrity": "sha512-UGjQ+OjBYYhxFVtUY+jtr0ZZgzZh6OHtYwRhFt8IHewJXFCfZTyfsbX20szBj5y1S4HRIUJ7cwBLIytTqMbI5w==", - "dev": true, - "requires": { - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/util-endpoints": { - "version": "3.241.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.241.0.tgz", - "integrity": "sha512-jVf8bKrN22Ey0xLmj75sL7EUvm5HFpuOMkXsZkuXycKhCwLBcEUWlvtJYtRjOU1zScPQv9GMJd2QXQglp34iOQ==", - "dev": true, - "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - } } }, "@aws-sdk/client-dynamodb": { @@ -17948,15 +17091,6 @@ "uuid": "^8.3.2" }, "dependencies": { - "@aws-sdk/util-endpoints": { - "version": "3.245.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.245.0.tgz", - "integrity": "sha512-UNOFquB1tKx+8RT8n82Zb5tIwDyZHVPBg/m0LB0RsLETjr6krien5ASpqWezsXKIR1hftN9uaxN4bvf2dZrWHg==", - "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -17965,16 +17099,16 @@ } }, "@aws-sdk/client-secrets-manager": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.238.0.tgz", - "integrity": "sha512-J3lmceh2txILEh8tWf4ldp8ThAh2WJBSa1t8oJhW58KuE+1a7cwOpgWO6MsSvOrGtp7qzTr8GMBUf7KLdv7wOg==", + "version": "3.250.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.250.0.tgz", + "integrity": "sha512-CB4OcFJPpAuW3rRXBxgOtF9RXHBixEvJSDkh8RsCAamt37Fw61+e+CsdXixSWA4rzQA+3PQuts7zlwhcxobbJg==", "dev": true, "requires": { "@aws-crypto/sha256-browser": "2.0.0", "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/client-sts": "3.238.0", + "@aws-sdk/client-sts": "3.245.0", "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-node": "3.238.0", + "@aws-sdk/credential-provider-node": "3.245.0", "@aws-sdk/fetch-http-handler": "3.226.0", "@aws-sdk/hash-node": "3.226.0", "@aws-sdk/invalid-dependency": "3.226.0", @@ -17999,7 +17133,7 @@ "@aws-sdk/util-body-length-node": "3.208.0", "@aws-sdk/util-defaults-mode-browser": "3.234.0", "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", + "@aws-sdk/util-endpoints": "3.245.0", "@aws-sdk/util-retry": "3.229.0", "@aws-sdk/util-user-agent-browser": "3.226.0", "@aws-sdk/util-user-agent-node": "3.226.0", @@ -18009,219 +17143,6 @@ "uuid": "^8.3.2" }, "dependencies": { - "@aws-sdk/client-sso": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.238.0.tgz", - "integrity": "sha512-KHJJWP7hBDa9KLYiU5+hOb+3AAba93PhWebXkpKyQ/Bs+e7ECCreyLCwuME6uWTV01NDuFDpwZ6zUMpyNIcP6Q==", - "dev": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/client-sso-oidc": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.238.0.tgz", - "integrity": "sha512-kazcA2Kp+cXQRtaZi5/T5YFfU9J3nzu1tXJsh0xAm+J3S9LS1ertY1bSX6KBed2xuxx2mfum8JRqli0TJad/pA==", - "dev": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/client-sts": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.238.0.tgz", - "integrity": "sha512-jQNwHqxWUGvWCN4o8KUFYQES8r41Oobu7x1KZOMrPhPxy27FUcDjBq/h85VoD2/AZlETSCZLiCnKV3KBh5pT5w==", - "dev": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-node": "3.238.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-sdk-sts": "3.226.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "fast-xml-parser": "4.0.11", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/config-resolver": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.234.0.tgz", - "integrity": "sha512-uZxy4wzllfvgCQxVc+Iqhde0NGAnfmV2hWR6ejadJaAFTuYNvQiRg9IqJy3pkyDPqXySiJ8Bom5PoJfgn55J/A==", - "dev": true, - "requires": { - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/credential-provider-ini": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.238.0.tgz", - "integrity": "sha512-WmPNtIYyUasjV7VQxvPNq7ihmx0vFsiKAtjNjjakdrt5TPoma4nUYb9tIG9SuG+kcp4DJIgRLJAgZtXbCcVimg==", - "dev": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.238.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/credential-provider-node": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.238.0.tgz", - "integrity": "sha512-/RN5EyGfgdIIJdFzv+O0nSaHX1/F3anQjTIBeVg8GJ+82m+bDxMdALsG+NzkYnLilN9Uhc1lSNjLBCoPa5DSEg==", - "dev": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.238.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.238.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/credential-provider-sso": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.238.0.tgz", - "integrity": "sha512-i70V4bFlCVYey3QARJ6XxKEg/4YuoFRnePV2oK37UHOGpEn49uXKwVZqLjzJgFHln7BPlC06cWDqrHUQIMvYrQ==", - "dev": true, - "requires": { - "@aws-sdk/client-sso": "3.238.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/token-providers": "3.238.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/smithy-client": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.234.0.tgz", - "integrity": "sha512-8AtR/k4vsFvjXeQbIzq/Wy7Nbk48Ou0wUEeVYPHWHPSU8QamFWORkOwmKtKMfHAyZvmqiAPeQqHFkq+UJhWyyQ==", - "dev": true, - "requires": { - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/token-providers": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.238.0.tgz", - "integrity": "sha512-vYUwmy0kTzA99mJCVvad+/5RDlWve/xxnppT8DJK3JIdAgskp+rULY+joVnq2NSl489UAioUnFGs57vUxi8Pog==", - "dev": true, - "requires": { - "@aws-sdk/client-sso-oidc": "3.238.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -18276,16 +17197,6 @@ "uuid": "^8.3.2" }, "dependencies": { - "@aws-sdk/util-endpoints": { - "version": "3.245.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.245.0.tgz", - "integrity": "sha512-UNOFquB1tKx+8RT8n82Zb5tIwDyZHVPBg/m0LB0RsLETjr6krien5ASpqWezsXKIR1hftN9uaxN4bvf2dZrWHg==", - "dev": true, - "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -18332,17 +17243,6 @@ "@aws-sdk/util-utf8-browser": "3.188.0", "@aws-sdk/util-utf8-node": "3.208.0", "tslib": "^2.3.1" - }, - "dependencies": { - "@aws-sdk/util-endpoints": { - "version": "3.245.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.245.0.tgz", - "integrity": "sha512-UNOFquB1tKx+8RT8n82Zb5tIwDyZHVPBg/m0LB0RsLETjr6krien5ASpqWezsXKIR1hftN9uaxN4bvf2dZrWHg==", - "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - } } }, "@aws-sdk/client-sso-oidc": { @@ -18383,17 +17283,6 @@ "@aws-sdk/util-utf8-browser": "3.188.0", "@aws-sdk/util-utf8-node": "3.208.0", "tslib": "^2.3.1" - }, - "dependencies": { - "@aws-sdk/util-endpoints": { - "version": "3.245.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.245.0.tgz", - "integrity": "sha512-UNOFquB1tKx+8RT8n82Zb5tIwDyZHVPBg/m0LB0RsLETjr6krien5ASpqWezsXKIR1hftN9uaxN4bvf2dZrWHg==", - "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - } } }, "@aws-sdk/client-sts": { @@ -18438,17 +17327,6 @@ "@aws-sdk/util-utf8-node": "3.208.0", "fast-xml-parser": "4.0.11", "tslib": "^2.3.1" - }, - "dependencies": { - "@aws-sdk/util-endpoints": { - "version": "3.245.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.245.0.tgz", - "integrity": "sha512-UNOFquB1tKx+8RT8n82Zb5tIwDyZHVPBg/m0LB0RsLETjr6krien5ASpqWezsXKIR1hftN9uaxN4bvf2dZrWHg==", - "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" - } - } } }, "@aws-sdk/config-resolver": { @@ -18960,10 +17838,9 @@ } }, "@aws-sdk/util-endpoints": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.226.0.tgz", - "integrity": "sha512-iqOkac/zLmyPBUJd7SLN0PeZMkOmlGgD5PHmmekTClOkce2eUjK9SNX1PzL73aXPoPTyhg9QGLH8uEZEQ8YUzg==", - "dev": true, + "version": "3.245.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.245.0.tgz", + "integrity": "sha512-UNOFquB1tKx+8RT8n82Zb5tIwDyZHVPBg/m0LB0RsLETjr6krien5ASpqWezsXKIR1hftN9uaxN4bvf2dZrWHg==", "requires": { "@aws-sdk/types": "3.226.0", "tslib": "^2.3.1" @@ -24392,6 +23269,27 @@ "path-type": "^4.0.0" } }, + "docs": { + "version": "file:docs/snippets", + "requires": { + "@aws-sdk/client-appconfigdata": "^3.241.0", + "@aws-sdk/client-dynamodb": "^3.245.0", + "@aws-sdk/client-secrets-manager": "^3.238.0", + "@aws-sdk/client-ssm": "^3.244.0", + "@aws-sdk/util-dynamodb": "^3.245.0" + }, + "dependencies": { + "@aws-sdk/util-dynamodb": { + "version": "3.245.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.245.0.tgz", + "integrity": "sha512-Wx06Ey92DRRSQTFfpQs1DkHSoajcwVu2TLWTg07yHXgGxdi6EveJPNqh111ot5yzHGmzPIHas/IPZe3hDttzcw==", + "dev": true, + "requires": { + "tslib": "^2.3.1" + } + } + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", diff --git a/package.json b/package.json index 456dce9a21..c7ca00b1e1 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "packages/metrics", "packages/tracer", "packages/parameters", - "packages/idempotency" + "packages/idempotency", + "docs/snippets" ], "scripts": { "init-environment": "husky install", @@ -21,8 +22,8 @@ "build": "npm run build -ws", "postversion": "git push && git push --tags", "docs-website-build-run": "npm run docs-buildDockerImage && npm run docs-runLocalDocker", - "docs-buildDockerImage": "docker build -t powertool-typescript/docs ./docs/", - "docs-runLocalDocker": "docker run --rm -it -p 8000:8000 -v ${PWD}:/docs powertool-typescript/docs", + "docs-buildDockerImage": "docker build -t powertools-typescript/docs ./docs/", + "docs-runLocalDocker": "docker run --rm -it -p 8000:8000 -v ${PWD}:/docs powertools-typescript/docs", "docs-api-build-run": "npm run docs-generateApiDoc && npx live-server api", "docs-generateApiDoc": "typedoc .", "docs-runLocalApiDoc": "npx live-server api" @@ -85,4 +86,4 @@ "dependencies": { "hosted-git-info": "^6.1.1" } -} +} \ No newline at end of file From 86d31c3f1a65de68e1c30b1f038c97b28882739a Mon Sep 17 00:00:00 2001 From: Niko Achilles Kokkinos Date: Tue, 17 Jan 2023 13:06:53 +0200 Subject: [PATCH 09/56] docs(metrics): extract Metrics code snippets in separate files (#1245) * docs(metrics): extract Metrics code snippets in separate files #1221 * fix(docs): hl_lines of multiValueMetrics code snippet --- docs/core/metrics.md | 232 ++---------------- docs/snippets/metrics/addMetadata.ts | 12 + docs/snippets/metrics/basicUsage.ts | 7 + .../captureColdStartMetricDecorator.ts | 12 + .../metrics/captureColdStartMetricMiddy.ts | 11 + docs/snippets/metrics/createMetrics.ts | 8 + docs/snippets/metrics/customDimensions.ts | 9 + docs/snippets/metrics/decorator.ts | 15 ++ docs/snippets/metrics/defaultDimensions.ts | 11 + .../metrics/defaultDimensionsDecorator.ts | 16 ++ .../metrics/defaultDimensionsMiddy.ts | 13 + docs/snippets/metrics/manual.ts | 8 + docs/snippets/metrics/middy.ts | 11 + docs/snippets/metrics/multiValueMetrics.ts | 10 + docs/snippets/metrics/sam.ts | 10 + docs/snippets/metrics/setDefaultDimensions.ts | 8 + .../singleMetricDifferentDimsDecorator.ts | 22 ++ .../metrics/singleMetricDifferentDimsMiddy.ts | 18 ++ docs/snippets/metrics/throwOnEmptyMetrics.ts | 11 + 19 files changed, 231 insertions(+), 213 deletions(-) create mode 100644 docs/snippets/metrics/addMetadata.ts create mode 100644 docs/snippets/metrics/basicUsage.ts create mode 100644 docs/snippets/metrics/captureColdStartMetricDecorator.ts create mode 100644 docs/snippets/metrics/captureColdStartMetricMiddy.ts create mode 100644 docs/snippets/metrics/createMetrics.ts create mode 100644 docs/snippets/metrics/customDimensions.ts create mode 100644 docs/snippets/metrics/decorator.ts create mode 100644 docs/snippets/metrics/defaultDimensions.ts create mode 100644 docs/snippets/metrics/defaultDimensionsDecorator.ts create mode 100644 docs/snippets/metrics/defaultDimensionsMiddy.ts create mode 100644 docs/snippets/metrics/manual.ts create mode 100644 docs/snippets/metrics/middy.ts create mode 100644 docs/snippets/metrics/multiValueMetrics.ts create mode 100644 docs/snippets/metrics/sam.ts create mode 100644 docs/snippets/metrics/setDefaultDimensions.ts create mode 100644 docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts create mode 100644 docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts create mode 100644 docs/snippets/metrics/throwOnEmptyMetrics.ts diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 661cc78de6..12f74854bd 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -51,13 +51,7 @@ The `Metrics` utility must always be instantiated outside of the Lambda handler. === "handler.ts" ```typescript hl_lines="1 3" - import { Metrics } from '@aws-lambda-powertools/metrics'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - export const handler = async (_event, _context): Promise => { - // ... - }; + --8<-- "docs/snippets/metrics/basicUsage.ts" ``` ### Utility settings @@ -81,16 +75,7 @@ The `Metrics` utility is instantiated outside of the Lambda handler. In doing th === "handler.ts" ```typescript hl_lines="1 4" - import { Metrics } from '@aws-lambda-powertools/metrics'; - - // Metrics parameters fetched from the environment variables (see template.yaml tab) - const metrics = new Metrics(); - - // You can also pass the parameters in the constructor - // const metrics = new Metrics({ - // namespace: 'serverlessAirline', - // serviceName: 'orders' - // }); + --8<-- "docs/snippets/metrics/sam.ts" ``` === "template.yml" @@ -116,28 +101,13 @@ You can create metrics using the `addMetric` method, and you can create dimensio === "Metrics" ```typescript hl_lines="6" - import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - export const handler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - metrics.publishStoredMetrics(); - }; + --8<-- "docs/snippets/metrics/createMetrics.ts" ``` === "Metrics with custom dimensions" ```typescript hl_lines="6-7" - import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - export const handler = async (_event: any, _context: any): Promise => { - metrics.addDimension('environment', 'prod'); - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - metrics.publishStoredMetrics(); - }; + --8<-- "docs/snippets/metrics/customDimensions.ts" ``` !!! tip "Autocomplete Metric Units" @@ -155,17 +125,8 @@ You can call `addMetric()` with the same name multiple times. The values will be === "addMetric() with the same name" - ```typescript hl_lines="8 10" - import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; - import { Context } from 'aws-lambda'; - - const metrics = new Metrics({ namespace:'serverlessAirline', serviceName:'orders' }); - - export const handler = async (event: any, context: Context): Promise => { - metrics.addMetric('performedActionA', MetricUnits.Count, 2); - // do something else... - metrics.addMetric('performedActionA', MetricUnits.Count, 1); - }; + ```typescript hl_lines="7 9" + --8<-- "docs/snippets/metrics/multiValueMetrics.ts" ``` === "Example CloudWatch Logs excerpt" @@ -210,17 +171,7 @@ You can add default dimensions to your metrics by passing them as parameters in === "constructor" ```typescript hl_lines="6" - import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; - - const metrics = new Metrics({ - namespace: 'serverlessAirline', - serviceName: 'orders', - defaultDimensions: { 'environment': 'prod', 'foo': 'bar' } - }); - - export const handler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - }; + --8<-- "docs/snippets/metrics/defaultDimensions.ts" ``` === "Middy middleware" @@ -230,53 +181,19 @@ You can add default dimensions to your metrics by passing them as parameters in Learn more about [its usage and lifecycle in the official Middy documentation](https://middy.js.org/docs/intro/getting-started){target="_blank"}. ```typescript hl_lines="1-2 11 13" - import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; - import middy from '@middy/core'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - const lambdaHandler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - }; - - // Wrap the handler with middy - export const handler = middy(lambdaHandler) - // Use the middleware by passing the Metrics instance as a parameter - .use(logMetrics(metrics, { defaultDimensions:{ 'environment': 'prod', 'foo': 'bar' } })); + --8<-- "docs/snippets/metrics/defaultDimensionsMiddy.ts" ``` === "setDefaultDimensions method" ```typescript hl_lines="4" - import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - metrics.setDefaultDimensions({ 'environment': 'prod', 'foo': 'bar' }); - - export const handler = async (event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - }; + --8<-- "docs/snippets/metrics/setDefaultDimensions.ts" ``` === "with logMetrics decorator" ```typescript hl_lines="9" - import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; - import { LambdaInterface } from '@aws-lambda-powertools/commons'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - const DEFAULT_DIMENSIONS = { 'environment': 'prod', 'foo': 'bar' }; - - export class Lambda implements LambdaInterface { - // Decorate your handler class method - @metrics.logMetrics({ defaultDimensions: DEFAULT_DIMENSIONS }) - public async handler(_event: any, _context: any): Promise { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - } - } - - const handlerClass = new Lambda(); - export const handler = handlerClass.handler.bind(handlerClass); // (1) + --8<-- "docs/snippets/metrics/defaultDimensionsDecorator.ts" ``` 1. Binding your handler method allows your handler to access `this` within the class methods. @@ -310,17 +227,7 @@ See below an example of how to automatically flush metrics with the Middy-compat === "handler.ts" ```typescript hl_lines="1-2 7 10-11" - import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; - import middy from '@middy/core'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - const lambdaHandler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - }; - - export const handler = middy(lambdaHandler) - .use(logMetrics(metrics)); + --8<-- "docs/snippets/metrics/middy.ts" ``` === "Example CloudWatch Logs excerpt" @@ -360,21 +267,7 @@ The `logMetrics` decorator of the metrics utility can be used when your Lambda h === "handler.ts" ```typescript hl_lines="8" - import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; - import { LambdaInterface } from '@aws-lambda-powertools/commons'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - class Lambda implements LambdaInterface { - - @metrics.logMetrics() - public async handler(_event: any, _context: any): Promise { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - } - } - - const handlerClass = new Lambda(); - export const handler = handlerClass.handler.bind(handlerClass); // (1) + --8<-- "docs/snippets/metrics/decorator.ts" ``` 1. Binding your handler method allows your handler to access `this` within the class methods. @@ -415,14 +308,7 @@ Metrics, dimensions and namespace validation still applies. === "handler.ts" ```typescript hl_lines="7" - import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - export const handler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 10); - metrics.publishStoredMetrics(); - }; + --8<-- "docs/snippets/metrics/manual.ts" ``` === "Example CloudWatch Logs excerpt" @@ -460,17 +346,7 @@ If you want to ensure that at least one metric is emitted before you flush them, === "handler.ts" ```typescript hl_lines="11" - import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; - import middy from '@middy/core'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - const lambdaHandler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - }; - - export const handler = middy(lambdaHandler) - .use(logMetrics(metrics, { throwOnEmptyMetrics: true })); + --8<-- "docs/snippets/metrics/throwOnEmptyMetrics.ts" ``` ### Capturing a cold start invocation as metric @@ -480,34 +356,13 @@ You can optionally capture cold start metrics with the `logMetrics` middleware o === "Middy Middleware" ```typescript hl_lines="11" - import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; - import middy from '@middy/core'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - const lambdaHandler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - }; - - export const handler = middy(lambdaHandler) - .use(logMetrics(metrics, { captureColdStartMetric: true })); + --8<-- "docs/snippets/metrics/captureColdStartMetricMiddy.ts" ``` === "logMetrics decorator" ```typescript hl_lines="8" - import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; - import { LambdaInterface } from '@aws-lambda-powertools/commons'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - export class MyFunction implements LambdaInterface { - - @metrics.logMetrics({ captureColdStartMetric: true }) - public async handler(_event: any, _context: any): Promise { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - } - } + --8<-- "docs/snippets/metrics/captureColdStartMetricDecorator.ts" ``` If it's a cold start invocation, this feature will: @@ -531,18 +386,7 @@ You can add high-cardinality data as part of your Metrics log with the `addMetad === "handler.ts" ```typescript hl_lines="8" - import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; - import middy from '@middy/core'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - const lambdaHandler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - metrics.addMetadata('bookingId', '7051cd10-6283-11ec-90d6-0242ac120003'); - }; - - export const handler = middy(lambdaHandler) - .use(logMetrics(metrics)); + --8<-- "docs/snippets/metrics/addMetadata.ts" ``` === "Example CloudWatch Logs excerpt" @@ -594,51 +438,13 @@ CloudWatch EMF uses the same dimensions across all your metrics. Use `singleMetr === "Middy Middleware" ```typescript hl_lines="11 13-14" - import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; - import middy from '@middy/core'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - const lambdaHandler = async (_event: any, _context: any): Promise => { - metrics.addDimension('metricUnit', 'milliseconds'); - // This metric will have the "metricUnit" dimension, and no "metricType" dimension: - metrics.addMetric('latency', MetricUnits.Milliseconds, 56); - - const singleMetric = metrics.singleMetric(); - // This metric will have the "metricType" dimension, and no "metricUnit" dimension: - singleMetric.addDimension('metricType', 'business'); - singleMetric.addMetric('orderSubmitted', MetricUnits.Count, 1); - }; - - export const handler = middy(lambdaHandler) - .use(logMetrics(metrics)); + --8<-- "docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts" ``` === "logMetrics decorator" ```typescript hl_lines="14 16-17" - import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; - import { LambdaInterface } from '@aws-lambda-powertools/commons'; - - const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); - - class Lambda implements LambdaInterface { - - @metrics.logMetrics() - public async handler(_event: any, _context: any): Promise { - metrics.addDimension('metricUnit', 'milliseconds'); - // This metric will have the "metricUnit" dimension, and no "metricType" dimension: - metrics.addMetric('latency', MetricUnits.Milliseconds, 56); - - const singleMetric = metrics.singleMetric(); - // This metric will have the "metricType" dimension, and no "metricUnit" dimension: - singleMetric.addDimension('metricType', 'business'); - singleMetric.addMetric('orderSubmitted', MetricUnits.Count, 1); - } - } - - const handlerClass = new Lambda(); - export const handler = handlerClass.handler.bind(handlerClass); // (1) + --8<-- "docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts" ``` 1. Binding your handler method allows your handler to access `this` within the class methods. \ No newline at end of file diff --git a/docs/snippets/metrics/addMetadata.ts b/docs/snippets/metrics/addMetadata.ts new file mode 100644 index 0000000000..ce56cac80d --- /dev/null +++ b/docs/snippets/metrics/addMetadata.ts @@ -0,0 +1,12 @@ +import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; +import middy from '@middy/core'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +const lambdaHandler = async (_event: any, _context: any): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetadata('bookingId', '7051cd10-6283-11ec-90d6-0242ac120003'); +}; + +export const handler = middy(lambdaHandler) + .use(logMetrics(metrics)); \ No newline at end of file diff --git a/docs/snippets/metrics/basicUsage.ts b/docs/snippets/metrics/basicUsage.ts new file mode 100644 index 0000000000..ea4d5a53ec --- /dev/null +++ b/docs/snippets/metrics/basicUsage.ts @@ -0,0 +1,7 @@ +import { Metrics } from '@aws-lambda-powertools/metrics'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +export const handler = async (_event, _context): Promise => { + // ... +}; \ No newline at end of file diff --git a/docs/snippets/metrics/captureColdStartMetricDecorator.ts b/docs/snippets/metrics/captureColdStartMetricDecorator.ts new file mode 100644 index 0000000000..3161b074cb --- /dev/null +++ b/docs/snippets/metrics/captureColdStartMetricDecorator.ts @@ -0,0 +1,12 @@ +import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { LambdaInterface } from '@aws-lambda-powertools/commons'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +export class MyFunction implements LambdaInterface { + + @metrics.logMetrics({ captureColdStartMetric: true }) + public async handler(_event: any, _context: any): Promise { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + } +} \ No newline at end of file diff --git a/docs/snippets/metrics/captureColdStartMetricMiddy.ts b/docs/snippets/metrics/captureColdStartMetricMiddy.ts new file mode 100644 index 0000000000..092e06b50b --- /dev/null +++ b/docs/snippets/metrics/captureColdStartMetricMiddy.ts @@ -0,0 +1,11 @@ +import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; +import middy from '@middy/core'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +const lambdaHandler = async (_event: any, _context: any): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +}; + +export const handler = middy(lambdaHandler) + .use(logMetrics(metrics, { captureColdStartMetric: true })); \ No newline at end of file diff --git a/docs/snippets/metrics/createMetrics.ts b/docs/snippets/metrics/createMetrics.ts new file mode 100644 index 0000000000..072966690a --- /dev/null +++ b/docs/snippets/metrics/createMetrics.ts @@ -0,0 +1,8 @@ +import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +export const handler = async (_event: any, _context: any): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.publishStoredMetrics(); +}; \ No newline at end of file diff --git a/docs/snippets/metrics/customDimensions.ts b/docs/snippets/metrics/customDimensions.ts new file mode 100644 index 0000000000..8c4dd883d7 --- /dev/null +++ b/docs/snippets/metrics/customDimensions.ts @@ -0,0 +1,9 @@ +import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +export const handler = async (_event: any, _context: any): Promise => { + metrics.addDimension('environment', 'prod'); + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.publishStoredMetrics(); +}; \ No newline at end of file diff --git a/docs/snippets/metrics/decorator.ts b/docs/snippets/metrics/decorator.ts new file mode 100644 index 0000000000..a3430414bc --- /dev/null +++ b/docs/snippets/metrics/decorator.ts @@ -0,0 +1,15 @@ +import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { LambdaInterface } from '@aws-lambda-powertools/commons'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +class Lambda implements LambdaInterface { + + @metrics.logMetrics() + public async handler(_event: any, _context: any): Promise { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + } +} + +const handlerClass = new Lambda(); +export const handler = handlerClass.handler.bind(handlerClass); // (1) \ No newline at end of file diff --git a/docs/snippets/metrics/defaultDimensions.ts b/docs/snippets/metrics/defaultDimensions.ts new file mode 100644 index 0000000000..3d48bd6bb6 --- /dev/null +++ b/docs/snippets/metrics/defaultDimensions.ts @@ -0,0 +1,11 @@ +import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; + +const metrics = new Metrics({ + namespace: 'serverlessAirline', + serviceName: 'orders', + defaultDimensions: { 'environment': 'prod', 'foo': 'bar' } +}); + +export const handler = async (_event: any, _context: any): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +}; \ No newline at end of file diff --git a/docs/snippets/metrics/defaultDimensionsDecorator.ts b/docs/snippets/metrics/defaultDimensionsDecorator.ts new file mode 100644 index 0000000000..ed2631e321 --- /dev/null +++ b/docs/snippets/metrics/defaultDimensionsDecorator.ts @@ -0,0 +1,16 @@ +import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { LambdaInterface } from '@aws-lambda-powertools/commons'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); +const DEFAULT_DIMENSIONS = { 'environment': 'prod', 'foo': 'bar' }; + +export class Lambda implements LambdaInterface { + // Decorate your handler class method + @metrics.logMetrics({ defaultDimensions: DEFAULT_DIMENSIONS }) + public async handler(_event: any, _context: any): Promise { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + } +} + +const handlerClass = new Lambda(); +export const handler = handlerClass.handler.bind(handlerClass); // (1) \ No newline at end of file diff --git a/docs/snippets/metrics/defaultDimensionsMiddy.ts b/docs/snippets/metrics/defaultDimensionsMiddy.ts new file mode 100644 index 0000000000..aedfc554e3 --- /dev/null +++ b/docs/snippets/metrics/defaultDimensionsMiddy.ts @@ -0,0 +1,13 @@ +import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; +import middy from '@middy/core'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +const lambdaHandler = async (_event: any, _context: any): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +}; + +// Wrap the handler with middy +export const handler = middy(lambdaHandler) + // Use the middleware by passing the Metrics instance as a parameter + .use(logMetrics(metrics, { defaultDimensions:{ 'environment': 'prod', 'foo': 'bar' } })); \ No newline at end of file diff --git a/docs/snippets/metrics/manual.ts b/docs/snippets/metrics/manual.ts new file mode 100644 index 0000000000..00e552a294 --- /dev/null +++ b/docs/snippets/metrics/manual.ts @@ -0,0 +1,8 @@ +import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +export const handler = async (_event: any, _context: any): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 10); + metrics.publishStoredMetrics(); +}; \ No newline at end of file diff --git a/docs/snippets/metrics/middy.ts b/docs/snippets/metrics/middy.ts new file mode 100644 index 0000000000..6af8f3827b --- /dev/null +++ b/docs/snippets/metrics/middy.ts @@ -0,0 +1,11 @@ +import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; +import middy from '@middy/core'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +const lambdaHandler = async (_event: any, _context: any): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +}; + +export const handler = middy(lambdaHandler) + .use(logMetrics(metrics)); \ No newline at end of file diff --git a/docs/snippets/metrics/multiValueMetrics.ts b/docs/snippets/metrics/multiValueMetrics.ts new file mode 100644 index 0000000000..aea850b62e --- /dev/null +++ b/docs/snippets/metrics/multiValueMetrics.ts @@ -0,0 +1,10 @@ +import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { Context } from 'aws-lambda'; + +const metrics = new Metrics({ namespace:'serverlessAirline', serviceName:'orders' }); + +export const handler = async (event: any, context: Context): Promise => { + metrics.addMetric('performedActionA', MetricUnits.Count, 2); + // do something else... + metrics.addMetric('performedActionA', MetricUnits.Count, 1); +}; \ No newline at end of file diff --git a/docs/snippets/metrics/sam.ts b/docs/snippets/metrics/sam.ts new file mode 100644 index 0000000000..5a2c910529 --- /dev/null +++ b/docs/snippets/metrics/sam.ts @@ -0,0 +1,10 @@ +import { Metrics } from '@aws-lambda-powertools/metrics'; + +// Metrics parameters fetched from the environment variables (see template.yaml tab) +const metrics = new Metrics(); + +// You can also pass the parameters in the constructor +// const metrics = new Metrics({ +// namespace: 'serverlessAirline', +// serviceName: 'orders' +// }); \ No newline at end of file diff --git a/docs/snippets/metrics/setDefaultDimensions.ts b/docs/snippets/metrics/setDefaultDimensions.ts new file mode 100644 index 0000000000..c6a5eb7d97 --- /dev/null +++ b/docs/snippets/metrics/setDefaultDimensions.ts @@ -0,0 +1,8 @@ +import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); +metrics.setDefaultDimensions({ 'environment': 'prod', 'foo': 'bar' }); + +export const handler = async (event: any, _context: any): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +}; \ No newline at end of file diff --git a/docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts b/docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts new file mode 100644 index 0000000000..84d1dde551 --- /dev/null +++ b/docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts @@ -0,0 +1,22 @@ +import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { LambdaInterface } from '@aws-lambda-powertools/commons'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +class Lambda implements LambdaInterface { + + @metrics.logMetrics() + public async handler(_event: any, _context: any): Promise { + metrics.addDimension('metricUnit', 'milliseconds'); + // This metric will have the "metricUnit" dimension, and no "metricType" dimension: + metrics.addMetric('latency', MetricUnits.Milliseconds, 56); + + const singleMetric = metrics.singleMetric(); + // This metric will have the "metricType" dimension, and no "metricUnit" dimension: + singleMetric.addDimension('metricType', 'business'); + singleMetric.addMetric('orderSubmitted', MetricUnits.Count, 1); + } +} + +const handlerClass = new Lambda(); +export const handler = handlerClass.handler.bind(handlerClass); // (1) \ No newline at end of file diff --git a/docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts b/docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts new file mode 100644 index 0000000000..c15f5f717f --- /dev/null +++ b/docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts @@ -0,0 +1,18 @@ +import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; +import middy from '@middy/core'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +const lambdaHandler = async (_event: any, _context: any): Promise => { + metrics.addDimension('metricUnit', 'milliseconds'); + // This metric will have the "metricUnit" dimension, and no "metricType" dimension: + metrics.addMetric('latency', MetricUnits.Milliseconds, 56); + + const singleMetric = metrics.singleMetric(); + // This metric will have the "metricType" dimension, and no "metricUnit" dimension: + singleMetric.addDimension('metricType', 'business'); + singleMetric.addMetric('orderSubmitted', MetricUnits.Count, 1); +}; + +export const handler = middy(lambdaHandler) + .use(logMetrics(metrics)); \ No newline at end of file diff --git a/docs/snippets/metrics/throwOnEmptyMetrics.ts b/docs/snippets/metrics/throwOnEmptyMetrics.ts new file mode 100644 index 0000000000..2b3f8bb4a4 --- /dev/null +++ b/docs/snippets/metrics/throwOnEmptyMetrics.ts @@ -0,0 +1,11 @@ +import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; +import middy from '@middy/core'; + +const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); + +const lambdaHandler = async (_event: any, _context: any): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +}; + +export const handler = middy(lambdaHandler) + .use(logMetrics(metrics, { throwOnEmptyMetrics: true })); \ No newline at end of file From fdbba32d8b3545730d242ac4fd1ef2d83cdbccce Mon Sep 17 00:00:00 2001 From: Niko Achilles Kokkinos Date: Tue, 17 Jan 2023 15:29:22 +0200 Subject: [PATCH 10/56] fix(docs): logger bringYourOwnFormatter snippet #1253 (#1254) --- .../logger/bringYourOwnFormatterClass.ts | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/docs/snippets/logger/bringYourOwnFormatterClass.ts b/docs/snippets/logger/bringYourOwnFormatterClass.ts index 0121e1bc64..b294ee0dc5 100644 --- a/docs/snippets/logger/bringYourOwnFormatterClass.ts +++ b/docs/snippets/logger/bringYourOwnFormatterClass.ts @@ -1,24 +1,37 @@ -import { Logger } from '@aws-lambda-powertools/logger'; -import { MyCompanyLogFormatter } from './utils/formatters/MyCompanyLogFormatter'; +import { LogFormatter } from "@aws-lambda-powertools/logger"; +import { + LogAttributes, + UnformattedAttributes, +} from "@aws-lambda-powertools/logger/lib/types"; -const logger = new Logger({ - logFormatter: new MyCompanyLogFormatter(), - logLevel: 'DEBUG', - serviceName: 'serverlessAirline', - sampleRateValue: 0.5, - persistentLogAttributes: { - awsAccountId: process.env.AWS_ACCOUNT_ID, - logger: { - name: '@aws-lambda-powertools/logger', - version: '0.0.1' - } - }, -}); +// Replace this line with your own type +type MyCompanyLog = LogAttributes; -export const handler = async (event, context): Promise => { +class MyCompanyLogFormatter extends LogFormatter { + public formatAttributes(attributes: UnformattedAttributes): MyCompanyLog { + return { + message: attributes.message, + service: attributes.serviceName, + environment: attributes.environment, + awsRegion: attributes.awsRegion, + correlationIds: { + awsRequestId: attributes.lambdaContext?.awsRequestId, + xRayTraceId: attributes.xRayTraceId, + }, + lambdaFunction: { + name: attributes.lambdaContext?.functionName, + arn: attributes.lambdaContext?.invokedFunctionArn, + memoryLimitInMB: attributes.lambdaContext?.memoryLimitInMB, + version: attributes.lambdaContext?.functionVersion, + coldStart: attributes.lambdaContext?.coldStart, + }, + logLevel: attributes.logLevel, + timestamp: this.formatTimestamp(attributes.timestamp), // You can extend this function + logger: { + sampleRateValue: attributes.sampleRateValue, + }, + }; + } +} - logger.addContext(context); - - logger.info('This is an INFO log', { correlationIds: { myCustomCorrelationId: 'foo-bar-baz' } }); - -}; \ No newline at end of file +export { MyCompanyLogFormatter }; From 897cda61db40088271d11cfd590ca043c2f3151d Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 17 Jan 2023 14:36:47 +0100 Subject: [PATCH 11/56] tests(parameters): end to end tests for `DynamoDBProvider` (#1244) * tests: DynamoDBProvider e2e tests * tests: DynamoDBProvider e2e tests * tests: DynamoDBProvider e2e tests * chore: added todo comments * chore: updated group comment * chore: added comments explaining tests --- packages/parameters/package.json | 8 +- packages/parameters/tests/e2e/constants.ts | 5 + ...ynamoDBProvider.class.test.functionCode.ts | 201 +++++++++ .../tests/e2e/dynamoDBProvider.class.test.ts | 419 ++++++++++++++++++ .../tests/helpers/cdkAspectGrantAccess.ts | 44 ++ .../tests/helpers/parametersUtils.ts | 22 + .../helpers/sdkMiddlewareRequestCounter.ts | 30 ++ .../parameters/tests/helpers/tinyLogger.ts | 21 + 8 files changed, 746 insertions(+), 4 deletions(-) create mode 100644 packages/parameters/tests/e2e/constants.ts create mode 100644 packages/parameters/tests/e2e/dynamoDBProvider.class.test.functionCode.ts create mode 100644 packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts create mode 100644 packages/parameters/tests/helpers/cdkAspectGrantAccess.ts create mode 100644 packages/parameters/tests/helpers/parametersUtils.ts create mode 100644 packages/parameters/tests/helpers/sdkMiddlewareRequestCounter.ts create mode 100644 packages/parameters/tests/helpers/tinyLogger.ts diff --git a/packages/parameters/package.json b/packages/parameters/package.json index bc1bf31e9b..32c9d56d69 100644 --- a/packages/parameters/package.json +++ b/packages/parameters/package.json @@ -13,9 +13,9 @@ "commit": "commit", "test": "npm run test:unit", "test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose", - "test:e2e:nodejs14x": "echo \"Not implemented\"", - "test:e2e:nodejs16x": "echo \"Not implemented\"", - "test:e2e:nodejs18x": "echo \"Not implemented\"", + "test:e2e:nodejs14x": "RUNTIME=nodejs14x jest --group=e2e", + "test:e2e:nodejs16x": "RUNTIME=nodejs16x jest --group=e2e", + "test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e", "test:e2e": "echo \"Not implemented\"", "watch": "jest --watch", "build": "tsc", @@ -62,4 +62,4 @@ "dependencies": { "@aws-sdk/util-base64-node": "^3.209.0" } -} +} \ No newline at end of file diff --git a/packages/parameters/tests/e2e/constants.ts b/packages/parameters/tests/e2e/constants.ts new file mode 100644 index 0000000000..03fb4eed52 --- /dev/null +++ b/packages/parameters/tests/e2e/constants.ts @@ -0,0 +1,5 @@ +export const RESOURCE_NAME_PREFIX = 'Parameters-E2E'; +export const ONE_MINUTE = 60 * 1000; +export const TEST_CASE_TIMEOUT = 3 * ONE_MINUTE; +export const SETUP_TIMEOUT = 5 * ONE_MINUTE; +export const TEARDOWN_TIMEOUT = 5 * ONE_MINUTE; \ No newline at end of file diff --git a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.functionCode.ts b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.functionCode.ts new file mode 100644 index 0000000000..f8d80314ae --- /dev/null +++ b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.functionCode.ts @@ -0,0 +1,201 @@ +import { Context } from 'aws-lambda'; +import { DynamoDBProvider } from '../../src/dynamodb'; +import { TinyLogger } from '../helpers/tinyLogger'; +// # TODO: Uncomment code below once #1222 is fixed +/* +import { middleware } from '../helpers/sdkMiddlewareRequestCounter'; +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +*/ + +const tableGet = process.env.TABLE_GET ?? 'my-table'; +const tableGetMultiple = process.env.TABLE_GET_MULTIPLE ?? 'my-table'; +const tableGetCustomkeys = process.env.TABLE_GET_CUSTOM_KEYS ?? 'my-table'; +const tableGetMultipleCustomkeys = process.env.TABLE_GET_MULTIPLE_CUSTOM_KEYS ?? 'my-table'; +const keyAttr = process.env.KEY_ATTR ?? 'id'; +const sortAttr = process.env.SORT_ATTR ?? 'sk'; +const valueAttr = process.env.VALUE_ATTR ?? 'value'; + +// We use a custom logger to log pure JSON objects to stdout +const logger = new TinyLogger(); + +// Provider test 1, 5, 6 +const providerGet = new DynamoDBProvider({ + tableName: tableGet, +}); +// Provider test 2, 7 +const providerGetMultiple = new DynamoDBProvider({ + tableName: tableGetMultiple, +}); +// Provider test 3 +const providerGetCustomKeys = new DynamoDBProvider({ + tableName: tableGetCustomkeys, + keyAttr, + valueAttr, +}); +// Provider 4 +const providerGetMultipleCustomKeys = new DynamoDBProvider({ + tableName: tableGetMultipleCustomkeys, + keyAttr, + sortAttr, + valueAttr, +}); +// # TODO: Uncomment code below once #1222 is fixed +/* +// Provider test 8, 9 +const customClient = new DynamoDBClient({}); +providerWithMiddleware.middlewareStack.use(middleware); +const providerWithMiddleware = new DynamoDBProvider({ + awsSdkV3Client: customClient +}); +*/ + +export const handler = async (_event: unknown, _context: Context): Promise => { + // Test 1 - get a single parameter with default options (keyAttr: 'id', valueAttr: 'value') + try { + const parameterValue = await providerGet.get('my-param'); + logger.log({ + test: 'get', + value: parameterValue + }); + } catch (err) { + logger.log({ + test: 'get', + error: err.message + }); + } + + // Test 2 - get multiple parameters with default options (keyAttr: 'id', sortAttr: 'sk', valueAttr: 'value') + try { + const parametersValues = await providerGetMultiple.getMultiple('my-params'); + logger.log({ + test: 'get-multiple', + value: parametersValues + }); + } catch (err) { + logger.log({ + test: 'get-multiple', + error: err.message + }); + } + + // Test 3 - get a single parameter with custom options (keyAttr: 'key', valueAttr: 'val') + try { + const parameterValueCustom = await providerGetCustomKeys.get('my-param'); + logger.log({ + test: 'get-custom', + value: parameterValueCustom + }); + } catch (err) { + logger.log({ + test: 'get-custom', + error: err.message + }); + } + + // Test 4 - get multiple parameters with custom options (keyAttr: 'key', sortAttr: 'sort', valueAttr: 'val') + try { + const parametersValuesCustom = await providerGetMultipleCustomKeys.getMultiple('my-params'); + logger.log({ + test: 'get-multiple-custom', + value: parametersValuesCustom + }); + } catch (err) { + logger.log({ + test: 'get-multiple-custom', + error: err.message + }); + } + + // Test 5 - get a single parameter with json transform + try { + const parameterValueJson = await providerGet.get('my-param-json', { + transform: 'json' + }); + logger.log({ + test: 'get-json-transform', + value: typeof parameterValueJson // should be object + }); + } catch (err) { + logger.log({ + test: 'get-json-transform', + error: err.message + }); + } + + // Test 6 - get a single parameter with binary transform + try { + const parameterValueBinary = await providerGet.get('my-param-binary', { + transform: 'binary' + }); + logger.log({ + test: 'get-binary-transform', + value: typeof parameterValueBinary // should be string + }); + } catch (err) { + logger.log({ + test: 'get-binary-transform', + error: err.message + }); + } + + // Test 7 - get multiple parameters with auto transform + try { + const parametersValuesAuto = await providerGetMultiple.getMultiple('my-encoded-params', { + transform: 'auto' + }); + if (!parametersValuesAuto) throw new Error('parametersValuesAuto is undefined'); + + logger.log({ + test: 'get-multiple-auto-transform', + value: + `${typeof parametersValuesAuto['config.json']},${typeof parametersValuesAuto['key.binary']}` // should be object,string + }); + } catch (err) { + logger.log({ + test: 'get-multiple-auto-transform', + error: err.message + }); + } + + // # TODO: Uncomment code below once #1222 is fixed + /** + * Test 8 - get a parameter twice, second time should be cached + * + * Should only make 1 request, we use middleware to count requests + */ + /* + try { + await providerWithMiddleware.get('my-param'); + await providerWithMiddleware.get('my-param'); + logger.log({ + test: 'get-cache-request-count', + value: middleware.requestCount + }); + } catch (err) { + logger.log({ + test: 'get-cache-request-count', + error: err.message + }); + } + */ + + /** + * Test 9 - get a parameter once more but with forceFetch = true + * + * Request count should increase to 2, we use middleware to count requests + */ + /* + try { + await providerWithMiddleware.get('my-param', { forceFetch: true }); + logger.log({ + test: 'get-force-fetch-request-count', + value: middleware.requestCount + }); + } catch (err) { + logger.log({ + test: 'get-force-fetch-request-count', + error: err.message + }); + } + */ +}; \ No newline at end of file diff --git a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts new file mode 100644 index 0000000000..ce8f7f50d4 --- /dev/null +++ b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts @@ -0,0 +1,419 @@ +/** + * Test DynamoDBProvider class + * + * @group e2e/parameters/dynamodb/class + */ +import path from 'path'; +import { Tracing } from 'aws-cdk-lib/aws-lambda'; +import { AttributeType } from 'aws-cdk-lib/aws-dynamodb'; +import { App, Stack, Aspects } from 'aws-cdk-lib'; +import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb'; +import { marshall } from '@aws-sdk/util-dynamodb'; +import { v4 } from 'uuid'; +import { + generateUniqueName, + isValidRuntimeKey, + createStackWithLambdaFunction, + invokeFunction, +} from '../../../commons/tests/utils/e2eUtils'; +import { InvocationLogs } from '../../../commons/tests/utils/InvocationLogs'; +import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; +import { ResourceAccessGranter } from '../helpers/cdkAspectGrantAccess'; +import { + RESOURCE_NAME_PREFIX, + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT +} from './constants'; +import { createDynamoDBTable } from '../helpers/parametersUtils'; + +const runtime: string = process.env.RUNTIME || 'nodejs18x'; + +if (!isValidRuntimeKey(runtime)) { + throw new Error(`Invalid runtime key value: ${runtime}`); +} + +const uuid = v4(); +const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'dynamoDBProvider'); +const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'dynamoDBProvider'); +const lambdaFunctionCodeFile = 'dynamoDBProvider.class.test.functionCode.ts'; + +const dynamoDBClient = new DynamoDBClient({}); + +const invocationCount = 1; + +// Parameters to be used by Parameters in the Lambda function +const tableGet = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'Table-Get'); +const tableGetMultiple = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'Table-GetMultiple'); +const tableGetCustomkeys = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'Table-GetCustomKeys'); +const tableGetMultipleCustomkeys = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'Table-GetMultipleCustomKeys'); +const keyAttr = 'key'; +const sortAttr = 'sort'; +const valueAttr = 'val'; + +const integTestApp = new App(); +let stack: Stack; + +/** + * This test suite deploys a CDK stack with a Lambda function and a number of DynamoDB tables. + * The function code uses the Parameters utility to retrieve values from the DynamoDB tables. + * It then logs the values to CloudWatch Logs as JSON. + * + * Once the stack is deployed, the Lambda function is invoked and the CloudWatch Logs are retrieved. + * The logs are then parsed and the values are compared to the expected values in each test case. + * + * The tables are populated with data before the Lambda function is invoked. These tables and values + * allow to test the different use cases of the DynamoDBProvider class. + * + * The tables are: + * + * - Table-Get: a table with a single partition key (id) and attribute (value) + * +-----------------+----------------------+ + * | id | value | + * +-----------------+----------------------+ + * | my-param | foo | + * | my-param-json | "{\"foo\": \"bar\"}" | + * | my-param-binary | "YmF6" | + * +-----------------+----------------------+ + * + * - Table-GetMultiple: a table with a partition key (id) and a sort key (sk) and attribute (value) + * +-------------------+---------------+----------------------+ + * | id | sk | value | + * +-------------------+---------------+----------------------+ + * | my-params | config | bar | + * | my-params | key | baz | + * | my-encoded-params | config.json | "{\"foo\": \"bar\"}" | + * | my-encoded-params | config.binary | "YmF6" | + * +-------------------+---------------+----------------------+ + * + * - Table-GetCustomKeys: a table with a single partition key (key) and attribute (val) + * +-----------------+----------------------+ + * | key | val | + * +-----------------+----------------------+ + * | my-param | foo | + * +-----------------+----------------------+ + * + * - Table-GetMultipleCustomKeys: a table with a partition key (key) and a sort key (sort) and attribute (val) + * +-------------------+---------------+----------------------+ + * | key | sort | val | + * +-------------------+---------------+----------------------+ + * | my-params | config | bar | + * | my-params | key | baz | + * +-------------------+---------------+----------------------+ + * + * The tests are: + * + * Test 1 + * Get a single parameter with default options (keyAttr: 'id', valueAttr: 'value') from table Table-Get + * + * Test 2 + * Get multiple parameters with default options (keyAttr: 'id', sortAttr: 'sk', valueAttr: 'value') from table Table-GetMultiple + * + * Test 3 + * Get a single parameter with custom options (keyAttr: 'key', valueAttr: 'val') from table Table-GetCustomKeys + * + * Test 4 + * Get multiple parameters with custom options (keyAttr: 'key', sortAttr: 'sort', valueAttr: 'val') from table Table-GetMultipleCustomKeys + * + * Test 5 + * Get a single JSON parameter with default options (keyAttr: 'id', valueAttr: 'value') and transform from table Table-Get + * + * Test 6 + * Get a single binrary parameter with default options (keyAttr: 'id', valueAttr: 'value') and transform it from table Table-Get + * + * Test 7 + * Get multiple JSON and binary parameters with default options (keyAttr: 'id', sortAttr: 'sk', valueAttr: 'value') and transform them automatically from table Table-GetMultiple + * + * Test 8 + * Get a parameter twice and check that the value is cached. This uses a custom SDK client that counts the number of calls to DynamoDB. + * + * Test 9 + * Get a cached parameter and force retrieval. This also uses the same custom SDK client that counts the number of calls to DynamoDB. + */ +describe(`parameters E2E tests (dynamoDBProvider) for runtime: ${runtime}`, () => { + + let invocationLogs: InvocationLogs[]; + + beforeAll(async () => { + // Create a stack with a Lambda function + stack = createStackWithLambdaFunction({ + app: integTestApp, + stackName: stackName, + functionName: functionName, + functionEntry: path.join(__dirname, lambdaFunctionCodeFile), + tracing: Tracing.ACTIVE, + environment: { + UUID: uuid, + + // Values(s) to be used by Parameters in the Lambda function + TABLE_GET: tableGet, + TABLE_GET_MULTIPLE: tableGetMultiple, + TABLE_GET_CUSTOM_KEYS: tableGetCustomkeys, + TABLE_GET_MULTIPLE_CUSTOM_KEYS: tableGetMultipleCustomkeys, + KEY_ATTR: keyAttr, + SORT_ATTR: sortAttr, + VALUE_ATTR: valueAttr, + }, + runtime: runtime, + }); + + // Create the DynamoDB tables + const ddbTableGet = createDynamoDBTable({ + stack, + id: 'Table-get', + tableName: tableGet, + partitionKey: { + name: 'id', + type: AttributeType.STRING + }, + }); + const ddbTableGetMultiple = createDynamoDBTable({ + stack, + id: 'Table-getMultiple', + tableName: tableGetMultiple, + partitionKey: { + name: 'id', + type: AttributeType.STRING + }, + sortKey: { + name: 'sk', + type: AttributeType.STRING + } + }); + const ddbTableGetCustomKeys = createDynamoDBTable({ + stack, + id: 'Table-getCustomKeys', + tableName: tableGetCustomkeys, + partitionKey: { + name: keyAttr, + type: AttributeType.STRING + }, + }); + const ddbTabelGetMultipleCustomKeys = createDynamoDBTable({ + stack, + id: 'Table-getMultipleCustomKeys', + tableName: tableGetMultipleCustomkeys, + partitionKey: { + name: keyAttr, + type: AttributeType.STRING + }, + sortKey: { + name: sortAttr, + type: AttributeType.STRING + }, + }); + + // Give the Lambda access to the DynamoDB tables + Aspects.of(stack).add(new ResourceAccessGranter([ + ddbTableGet, + ddbTableGetMultiple, + ddbTableGetCustomKeys, + ddbTabelGetMultipleCustomKeys, + ])); + + // Deploy the stack + await deployStack(integTestApp, stack); + + // Seed tables with test data + // Test 1 + await dynamoDBClient.send(new PutItemCommand({ + TableName: tableGet, + Item: marshall({ + id: 'my-param', + value: 'foo', + }), + })); + + // Test 2 + await dynamoDBClient.send(new PutItemCommand({ + TableName: tableGetMultiple, + Item: marshall({ + id: 'my-params', + sk: 'config', + value: 'bar', + }), + })); + await dynamoDBClient.send(new PutItemCommand({ + TableName: tableGetMultiple, + Item: marshall({ + id: 'my-params', + sk: 'key', + value: 'baz', + }), + })); + + // Test 3 + await dynamoDBClient.send(new PutItemCommand({ + TableName: tableGetCustomkeys, + Item: marshall({ + [keyAttr]: 'my-param', + [valueAttr]: 'foo', + }), + })); + + // Test 4 + await dynamoDBClient.send(new PutItemCommand({ + TableName: tableGetMultipleCustomkeys, + Item: marshall({ + [keyAttr]: 'my-params', + [sortAttr]: 'config', + [valueAttr]: 'bar', + }), + })); + await dynamoDBClient.send(new PutItemCommand({ + TableName: tableGetMultipleCustomkeys, + Item: marshall({ + [keyAttr]: 'my-params', + [sortAttr]: 'key', + [valueAttr]: 'baz', + }), + })); + + // Test 5 + await dynamoDBClient.send(new PutItemCommand({ + TableName: tableGet, + Item: marshall({ + id: 'my-param-json', + value: JSON.stringify({ foo: 'bar' }), + }), + })); + + // Test 6 + await dynamoDBClient.send(new PutItemCommand({ + TableName: tableGet, + Item: marshall({ + id: 'my-param-binary', + value: 'YmF6', // base64 encoded 'baz' + }), + })); + + // Test 7 + await dynamoDBClient.send(new PutItemCommand({ + TableName: tableGetMultiple, + Item: marshall({ + id: 'my-encoded-params', + sk: 'config.json', + value: JSON.stringify({ foo: 'bar' }), + }), + })); + await dynamoDBClient.send(new PutItemCommand({ + TableName: tableGetMultiple, + Item: marshall({ + id: 'my-encoded-params', + sk: 'key.binary', + value: 'YmF6', // base64 encoded 'baz' + }), + })); + + // Test 8 & 9 use the same items as Test 1 + + // and invoke the Lambda function + invocationLogs = await invokeFunction(functionName, invocationCount, 'SEQUENTIAL'); + + }, SETUP_TIMEOUT); + + describe('DynamoDBProvider usage', () => { + + // Test 1 - get a single parameter with default options (keyAttr: 'id', valueAttr: 'value') + it('should retrieve a single parameter', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[0]); + + expect(testLog).toStrictEqual({ + test: 'get', + value: 'foo', + }); + + }, TEST_CASE_TIMEOUT); + + // Test 2 - get multiple parameters with default options (keyAttr: 'id', sortAttr: 'sk', valueAttr: 'value') + it('should retrieve multiple parameters', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[1]); + + expect(testLog).toStrictEqual({ + test: 'get-multiple', + value: { config: 'bar', key: 'baz' }, + }); + + }, TEST_CASE_TIMEOUT); + + // Test 3 - get a single parameter with custom options (keyAttr: 'key', valueAttr: 'val') + it('should retrieve a single parameter', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[2]); + + expect(testLog).toStrictEqual({ + test: 'get-custom', + value: 'foo', + }); + + }, TEST_CASE_TIMEOUT); + + // Test 4 - get multiple parameters with custom options (keyAttr: 'key', sortAttr: 'sort', valueAttr: 'val') + it('should retrieve multiple parameters', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[3]); + + expect(testLog).toStrictEqual({ + test: 'get-multiple-custom', + value: { config: 'bar', key: 'baz' }, + }); + + }, TEST_CASE_TIMEOUT); + + // Test 5 - get a single parameter with json transform + it('should retrieve a single parameter with json transform', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[4]); + + expect(testLog).toStrictEqual({ + test: 'get-json-transform', + value: 'object', + }); + + }); + + // Test 6 - get a single parameter with binary transform + it('should retrieve a single parameter with binary transform', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[5]); + + expect(testLog).toStrictEqual({ + test: 'get-binary-transform', + value: 'string', // as opposed to Uint8Array + }); + + }); + + // Test 7 - get multiple parameters with auto transforms (json and binary) + it('should retrieve multiple parameters with auto transforms', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[6]); + + expect(testLog).toStrictEqual({ + test: 'get-multiple-auto-transform', + value: 'object,string', + }); + + }); + + // TODO: implement tests for the following cases once #1222 is merged: + // Test 8 - get a parameter twice, second time should be cached + // Test 9 - get a parameter once more but with forceFetch = true + + }); + + afterAll(async () => { + if (!process.env.DISABLE_TEARDOWN) { + await destroyStack(integTestApp, stack); + } + }, TEARDOWN_TIMEOUT); +}); diff --git a/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts b/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts new file mode 100644 index 0000000000..22f8c09e6f --- /dev/null +++ b/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts @@ -0,0 +1,44 @@ +import { IAspect } from 'aws-cdk-lib'; +import { IConstruct } from 'constructs'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { Table } from 'aws-cdk-lib/aws-dynamodb'; +import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; + +/** + * An aspect that grants access to resources to a Lambda function. + * + * In our integration tests, we dynamically generate AWS CDK stacks that contain a Lambda function. + * We want to grant access to resources to the Lambda function, but we don't know the name of the + * Lambda function at the time we create the resources. Additionally, we want to keep the code + * that creates the stacks and functions as generic as possible. + * + * This aspect allows us to grant access to specific resources to all Lambda functions in a stack + * after the stack tree has been generated and before the stack is deployed. This aspect is + * used to grant access to different resource types (DynamoDB tables, SSM parameters, etc.). + * + * @see {@link https://docs.aws.amazon.com/cdk/v2/guide/aspects.html|CDK Docs - Aspects} + */ +export class ResourceAccessGranter implements IAspect { + private readonly resources: Table[] | Secret[]; + + public constructor(tables: Table[] | Secret[]) { + this.resources = tables; + } + + public visit(node: IConstruct): void { + // See that we're dealing with a Function + if (node instanceof NodejsFunction) { + + // Grant access to the resources + this.resources.forEach((resource: Table | Secret) => { + + if (resource instanceof Table) { + resource.grantReadData(node); + } else if (resource instanceof Secret) { + resource.grantRead(node); + } + + }); + } + } +} \ No newline at end of file diff --git a/packages/parameters/tests/helpers/parametersUtils.ts b/packages/parameters/tests/helpers/parametersUtils.ts new file mode 100644 index 0000000000..3768a1fe53 --- /dev/null +++ b/packages/parameters/tests/helpers/parametersUtils.ts @@ -0,0 +1,22 @@ +import { Stack, RemovalPolicy } from 'aws-cdk-lib'; +import { Table, TableProps, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; + +export type CreateDynamoDBTableOptions = { + stack: Stack + id: string +} & TableProps; + +const createDynamoDBTable = (options: CreateDynamoDBTableOptions): Table => { + const { stack, id, ...tableProps } = options; + const props = { + billingMode: BillingMode.PAY_PER_REQUEST, + removalPolicy: RemovalPolicy.DESTROY, + ...tableProps, + }; + + return new Table(stack, id, props); +}; + +export { + createDynamoDBTable +}; \ No newline at end of file diff --git a/packages/parameters/tests/helpers/sdkMiddlewareRequestCounter.ts b/packages/parameters/tests/helpers/sdkMiddlewareRequestCounter.ts new file mode 100644 index 0000000000..f71a6173eb --- /dev/null +++ b/packages/parameters/tests/helpers/sdkMiddlewareRequestCounter.ts @@ -0,0 +1,30 @@ +/** + * Middleware to count the number of API calls made by the SDK. + * + * The AWS SDK for JavaScript v3 uses a middleware stack to manage the execution of + * operations. Middleware can be added to the stack to perform custom tasks before + * or after an operation is executed. + * + * This middleware is added to the stack to count the number of API calls (`ROUND_TRIP`) made by the SDK. + * This allows us to verify that the SDK is making the expected number of API calls and thus test that + * caching or forcing a retrieval are working as expected. + * + * @see {@link https://aws.amazon.com/blogs/developer/middleware-stack-modular-aws-sdk-js/|AWS Blog - Middleware Stack} + */ +export const middleware = { + // + counter : 0, + applyToStack: (stack) => { + // Middleware added to mark start and end of an complete API call. + stack.add( + (next, _context) => async (args) => { + // Increment counter + middleware.counter++; + + // Call next middleware + return await next(args); + }, + { tags: ['ROUND_TRIP'] } + ); + }, +}; \ No newline at end of file diff --git a/packages/parameters/tests/helpers/tinyLogger.ts b/packages/parameters/tests/helpers/tinyLogger.ts new file mode 100644 index 0000000000..0effa723ae --- /dev/null +++ b/packages/parameters/tests/helpers/tinyLogger.ts @@ -0,0 +1,21 @@ +import { Console } from 'console'; + +/** + * A tiny logger that logs to stdout and stderr. + * + * This is used to log the results of the function code during the integration tests. + * We use this instead of the global console object because we want to log pure JSON objects. + * In Node.js runtimes, AWS Lambda usually patches the global console object to inject some + * metadata like the request ID. This is not desirable in our case because we want to log pure + * JSON objects to stdout and stderr. + * + * This allows us to get the logs when invoking the function and parse them to verify that + * the function code is working as expected. + */ +export class TinyLogger { + private console = new Console({ stdout: process.stdout, stderr: process.stderr }); + + public log(message: unknown): void { + this.console.log(JSON.stringify(message)); + } +} \ No newline at end of file From 0991021a5c0e5b49bc02a36130adfac8151dd2cc Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Wed, 18 Jan 2023 12:10:17 +0100 Subject: [PATCH 12/56] chore: standardized env setup for tests (#1255) --- packages/commons/jest.config.js | 4 ++++ packages/idempotency/jest.config.js | 2 +- .../tests/helpers/populateEnvironmentVariables.ts | 4 +++- .../tests/helpers/populateEnvironmentVariables.ts | 4 +++- packages/metrics/jest.config.js | 7 +++++++ .../tests/helpers/populate-environment-variables.ts | 9 --------- .../tests/helpers/populateEnvironmentVariables.ts | 10 ++++++++++ packages/metrics/tests/unit/Metrics.test.ts | 10 ++-------- .../tests/helpers/populateEnvironmentVariables.ts | 8 +++----- packages/tracer/jest.config.js | 2 +- .../tests/helpers/populateEnvironmentVariables.ts | 4 +++- 11 files changed, 37 insertions(+), 27 deletions(-) delete mode 100644 packages/metrics/tests/helpers/populate-environment-variables.ts create mode 100644 packages/metrics/tests/helpers/populateEnvironmentVariables.ts diff --git a/packages/commons/jest.config.js b/packages/commons/jest.config.js index 6fbaae512f..c60b4837dc 100644 --- a/packages/commons/jest.config.js +++ b/packages/commons/jest.config.js @@ -1,4 +1,8 @@ module.exports = { + displayName: { + name: 'AWS Lambda Powertools utility: COMMONS', + color: 'red', + }, 'preset': 'ts-jest', 'transform': { '^.+\\.ts?$': 'ts-jest', diff --git a/packages/idempotency/jest.config.js b/packages/idempotency/jest.config.js index c33c7e13fc..aa65679554 100644 --- a/packages/idempotency/jest.config.js +++ b/packages/idempotency/jest.config.js @@ -1,7 +1,7 @@ module.exports = { displayName: { name: 'AWS Lambda Powertools utility: IDEMPOTENCY', - color: 'blue', + color: 'yellow', }, 'runner': 'groups', 'preset': 'ts-jest', diff --git a/packages/idempotency/tests/helpers/populateEnvironmentVariables.ts b/packages/idempotency/tests/helpers/populateEnvironmentVariables.ts index d38651b714..0511a61959 100644 --- a/packages/idempotency/tests/helpers/populateEnvironmentVariables.ts +++ b/packages/idempotency/tests/helpers/populateEnvironmentVariables.ts @@ -3,5 +3,7 @@ process.env._X_AMZN_TRACE_ID = '1-abcdef12-3456abcdef123456abcdef12'; process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-lambda-function'; process.env.AWS_EXECUTION_ENV = 'nodejs16.x'; process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '128'; -process.env.AWS_REGION = 'eu-west-1'; +if (process.env.AWS_REGION === undefined && process.env.CDK_DEFAULT_REGION === undefined) { + process.env.AWS_REGION = 'eu-west-1'; +} process.env._HANDLER = 'index.handler'; \ No newline at end of file diff --git a/packages/logger/tests/helpers/populateEnvironmentVariables.ts b/packages/logger/tests/helpers/populateEnvironmentVariables.ts index e808ac84b9..604904e073 100644 --- a/packages/logger/tests/helpers/populateEnvironmentVariables.ts +++ b/packages/logger/tests/helpers/populateEnvironmentVariables.ts @@ -2,7 +2,9 @@ process.env._X_AMZN_TRACE_ID = 'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1'; process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-lambda-function'; process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '128'; -process.env.AWS_REGION = 'eu-west-1'; +if (process.env.AWS_REGION === undefined && process.env.CDK_DEFAULT_REGION === undefined) { + process.env.AWS_REGION = 'eu-west-1'; +} // Powertools variables process.env.LOG_LEVEL = 'DEBUG'; diff --git a/packages/metrics/jest.config.js b/packages/metrics/jest.config.js index 118de05065..11773c6994 100644 --- a/packages/metrics/jest.config.js +++ b/packages/metrics/jest.config.js @@ -1,4 +1,8 @@ module.exports = { + displayName: { + name: 'AWS Lambda Powertools utility: METRICS', + color: 'green', + }, 'runner': 'groups', 'preset': 'ts-jest', 'transform': { @@ -33,4 +37,7 @@ module.exports = { 'json-summary', 'text', 'lcov' ], + 'setupFiles': [ + '/tests/helpers/populateEnvironmentVariables.ts' + ] }; \ No newline at end of file diff --git a/packages/metrics/tests/helpers/populate-environment-variables.ts b/packages/metrics/tests/helpers/populate-environment-variables.ts deleted file mode 100644 index 82f69ebf44..0000000000 --- a/packages/metrics/tests/helpers/populate-environment-variables.ts +++ /dev/null @@ -1,9 +0,0 @@ -const populateEnvironmentVariables = (): void => { - - process.env.POWERTOOLS_METRICS_NAMESPACE = 'hello-world'; - -}; - -export { - populateEnvironmentVariables, -}; \ No newline at end of file diff --git a/packages/metrics/tests/helpers/populateEnvironmentVariables.ts b/packages/metrics/tests/helpers/populateEnvironmentVariables.ts new file mode 100644 index 0000000000..37e985813c --- /dev/null +++ b/packages/metrics/tests/helpers/populateEnvironmentVariables.ts @@ -0,0 +1,10 @@ +// Reserved variables +process.env._X_AMZN_TRACE_ID = 'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1'; +process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-lambda-function'; +process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '128'; +if (process.env.AWS_REGION === undefined && process.env.CDK_DEFAULT_REGION === undefined) { + process.env.AWS_REGION = 'eu-west-1'; +} + +// Powertools variables +process.env.POWERTOOLS_METRICS_NAMESPACE = 'hello-world'; \ No newline at end of file diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index ef34f6b6b9..5f2bd02bf8 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -7,7 +7,6 @@ import { ContextExamples as dummyContext, Events as dummyEvent, LambdaInterface } from '@aws-lambda-powertools/commons'; import { Context, Callback } from 'aws-lambda'; import { Metrics, MetricUnits } from '../../src/'; -import { populateEnvironmentVariables } from '../helpers'; const MAX_METRICS_SIZE = 100; const MAX_DIMENSION_COUNT = 29; @@ -20,7 +19,7 @@ interface LooseObject { } describe('Class: Metrics', () => { - const originalEnvironmentVariables = process.env; + const ENVIRONMENT_VARIABLES = process.env; const context = dummyContext.helloworldContext; const event = dummyEvent.Custom.CustomEvent; @@ -29,12 +28,7 @@ describe('Class: Metrics', () => { }); beforeAll(() => { - populateEnvironmentVariables(); - }); - - afterEach(() => { - process.env = originalEnvironmentVariables; - delete process.env.POWERTOOLS_SERVICE_NAME; + process.env = { ...ENVIRONMENT_VARIABLES }; }); describe('Feature: Dimensions logging', () => { diff --git a/packages/parameters/tests/helpers/populateEnvironmentVariables.ts b/packages/parameters/tests/helpers/populateEnvironmentVariables.ts index e808ac84b9..7ff0774273 100644 --- a/packages/parameters/tests/helpers/populateEnvironmentVariables.ts +++ b/packages/parameters/tests/helpers/populateEnvironmentVariables.ts @@ -2,8 +2,6 @@ process.env._X_AMZN_TRACE_ID = 'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1'; process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-lambda-function'; process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '128'; -process.env.AWS_REGION = 'eu-west-1'; - -// Powertools variables -process.env.LOG_LEVEL = 'DEBUG'; -process.env.POWERTOOLS_SERVICE_NAME = 'hello-world'; +if (process.env.AWS_REGION === undefined && process.env.CDK_DEFAULT_REGION === undefined) { + process.env.AWS_REGION = 'eu-west-1'; +} diff --git a/packages/tracer/jest.config.js b/packages/tracer/jest.config.js index 21510dceb2..d23175955a 100644 --- a/packages/tracer/jest.config.js +++ b/packages/tracer/jest.config.js @@ -1,7 +1,7 @@ module.exports = { displayName: { name: 'AWS Lambda Powertools utility: TRACER', - color: 'cyan', + color: 'white', }, 'runner': 'groups', 'preset': 'ts-jest', diff --git a/packages/tracer/tests/helpers/populateEnvironmentVariables.ts b/packages/tracer/tests/helpers/populateEnvironmentVariables.ts index 4eb2b25522..3dd17a6aa7 100644 --- a/packages/tracer/tests/helpers/populateEnvironmentVariables.ts +++ b/packages/tracer/tests/helpers/populateEnvironmentVariables.ts @@ -3,7 +3,9 @@ process.env._X_AMZN_TRACE_ID = '1-abcdef12-3456abcdef123456abcdef12'; process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-lambda-function'; process.env.AWS_EXECUTION_ENV = 'nodejs16.x'; process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '128'; -process.env.AWS_REGION = 'eu-west-1'; +if (process.env.AWS_REGION === undefined && process.env.CDK_DEFAULT_REGION === undefined) { + process.env.AWS_REGION = 'eu-west-1'; +} process.env._HANDLER = 'index.handler'; // Powertools variables From fd5e72c76dc5ff5c0450000b7b6807e3b93235df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 11:26:38 +0100 Subject: [PATCH 13/56] build(deps): bump http-cache-semantics from 4.1.0 to 4.1.1 (#1270) Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/kornelski/http-cache-semantics/releases) - [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1) --- updated-dependencies: - dependency-name: http-cache-semantics dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 015b32f41f..4eceee618a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10094,9 +10094,9 @@ "dev": true }, "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, "node_modules/http-errors": { @@ -23272,10 +23272,10 @@ "docs": { "version": "file:docs/snippets", "requires": { - "@aws-sdk/client-appconfigdata": "^3.241.0", + "@aws-sdk/client-appconfigdata": "^3.245.0", "@aws-sdk/client-dynamodb": "^3.245.0", - "@aws-sdk/client-secrets-manager": "^3.238.0", - "@aws-sdk/client-ssm": "^3.244.0", + "@aws-sdk/client-secrets-manager": "^3.250.0", + "@aws-sdk/client-ssm": "^3.245.0", "@aws-sdk/util-dynamodb": "^3.245.0" }, "dependencies": { @@ -24771,9 +24771,9 @@ "dev": true }, "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, "http-errors": { From 3a8cfa0d6e5aaa5c2c36d97d7835dbf5287b7110 Mon Sep 17 00:00:00 2001 From: Sergei Cherniaev Date: Mon, 6 Feb 2023 17:05:05 +0300 Subject: [PATCH 14/56] feat(parameters): add support for custom AWS SDK v3 clients for providers (#1260) * feat(parameters): add support for custom sdk client for appconfig provider * feat(parameters): add support for custom sdk client for secrets manager provider * feat(parameters): add support for custom sdk client for dynamoDB provider * feat(parameters): add support for custom sdk client for SSM provider --- .../src/appconfig/AppConfigProvider.ts | 11 ++- .../src/dynamodb/DynamoDBProvider.ts | 13 ++- .../parameters/src/secrets/SecretsProvider.ts | 13 ++- packages/parameters/src/ssm/SSMProvider.ts | 16 +++- .../parameters/src/types/AppConfigProvider.ts | 46 +++++++++-- .../parameters/src/types/DynamoDBProvider.ts | 15 +++- packages/parameters/src/types/SSMProvider.ts | 15 +++- .../parameters/src/types/SecretsProvider.ts | 12 ++- .../tests/unit/AppConfigProvider.test.ts | 79 +++++++++++++++++++ .../tests/unit/DynamoDBProvider.test.ts | 79 +++++++++++++++++++ .../parameters/tests/unit/SSMProvider.test.ts | 74 +++++++++++++++++ .../tests/unit/SecretsProvider.test.ts | 74 +++++++++++++++++ 12 files changed, 427 insertions(+), 20 deletions(-) diff --git a/packages/parameters/src/appconfig/AppConfigProvider.ts b/packages/parameters/src/appconfig/AppConfigProvider.ts index 0042722aec..9e9e2b0152 100644 --- a/packages/parameters/src/appconfig/AppConfigProvider.ts +++ b/packages/parameters/src/appconfig/AppConfigProvider.ts @@ -23,7 +23,16 @@ class AppConfigProvider extends BaseProvider { */ public constructor(options: AppConfigProviderOptions) { super(); - this.client = new AppConfigDataClient(options.clientConfig || {}); + if (options?.awsSdkV3Client) { + if (options?.awsSdkV3Client instanceof AppConfigDataClient) { + this.client = options.awsSdkV3Client; + } else { + throw Error('Not a valid AppConfigDataClient provided'); + } + } else { + this.client = new AppConfigDataClient(options.clientConfig || {}); + } + if (!options?.application && !process.env['POWERTOOLS_SERVICE_NAME']) { throw new Error( 'Application name is not defined or POWERTOOLS_SERVICE_NAME is not set' diff --git a/packages/parameters/src/dynamodb/DynamoDBProvider.ts b/packages/parameters/src/dynamodb/DynamoDBProvider.ts index 7fb5152553..58f0f2456b 100644 --- a/packages/parameters/src/dynamodb/DynamoDBProvider.ts +++ b/packages/parameters/src/dynamodb/DynamoDBProvider.ts @@ -19,8 +19,17 @@ class DynamoDBProvider extends BaseProvider { public constructor(config: DynamoDBProviderOptions) { super(); - const clientConfig = config.clientConfig || {}; - this.client = new DynamoDBClient(clientConfig); + if (config?.awsSdkV3Client) { + if (config?.awsSdkV3Client instanceof DynamoDBClient) { + this.client = config.awsSdkV3Client; + } else { + throw Error('Not a valid DynamoDBClient provided'); + } + } else { + const clientConfig = config?.clientConfig || {}; + this.client = new DynamoDBClient(clientConfig); + } + this.tableName = config.tableName; if (config.keyAttr) this.keyAttr = config.keyAttr; if (config.sortAttr) this.sortAttr = config.sortAttr; diff --git a/packages/parameters/src/secrets/SecretsProvider.ts b/packages/parameters/src/secrets/SecretsProvider.ts index 4d91009610..12a6703783 100644 --- a/packages/parameters/src/secrets/SecretsProvider.ts +++ b/packages/parameters/src/secrets/SecretsProvider.ts @@ -15,8 +15,17 @@ class SecretsProvider extends BaseProvider { public constructor (config?: SecretsProviderOptions) { super(); - const clientConfig = config?.clientConfig || {}; - this.client = new SecretsManagerClient(clientConfig); + if (config?.awsSdkV3Client) { + if (config?.awsSdkV3Client instanceof SecretsManagerClient) { + this.client = config.awsSdkV3Client; + } else { + throw Error('Not a valid SecretsManagerClient provided'); + } + } else { + const clientConfig = config?.clientConfig || {}; + this.client = new SecretsManagerClient(clientConfig); + } + } public async get( diff --git a/packages/parameters/src/ssm/SSMProvider.ts b/packages/parameters/src/ssm/SSMProvider.ts index 66d7e12367..39cc0ee569 100644 --- a/packages/parameters/src/ssm/SSMProvider.ts +++ b/packages/parameters/src/ssm/SSMProvider.ts @@ -14,7 +14,7 @@ import type { GetParametersCommandOutput, } from '@aws-sdk/client-ssm'; import type { - SSMProviderOptionsInterface, + SSMProviderOptions, SSMGetMultipleOptionsInterface, SSMGetOptionsInterface, SSMGetParametersByNameOutputInterface, @@ -29,9 +29,19 @@ class SSMProvider extends BaseProvider { protected errorsKey = '_errors'; protected maxGetParametersItems = 10; - public constructor(config?: SSMProviderOptionsInterface) { + public constructor(config?: SSMProviderOptions) { super(); - this.client = new SSMClient(config?.clientConfig || {}); + + if (config?.awsSdkV3Client) { + if (config?.awsSdkV3Client instanceof SSMClient) { + this.client = config.awsSdkV3Client; + } else { + throw Error('Not a valid SSMClient provided'); + } + } else { + const clientConfig = config?.clientConfig || {}; + this.client = new SSMClient(clientConfig); + } } public async get( diff --git a/packages/parameters/src/types/AppConfigProvider.ts b/packages/parameters/src/types/AppConfigProvider.ts index c699714639..007bca8a50 100644 --- a/packages/parameters/src/types/AppConfigProvider.ts +++ b/packages/parameters/src/types/AppConfigProvider.ts @@ -1,23 +1,59 @@ import type { + AppConfigDataClient, AppConfigDataClientConfig, StartConfigurationSessionCommandInput, } from '@aws-sdk/client-appconfigdata'; import type { GetOptionsInterface } from 'types/BaseProvider'; /** - * Options for the AppConfigProvider class constructor. + * Base interface for AppConfigProviderOptions. * - * @interface AppConfigProviderOptions + * @interface * @property {string} environment - The environment ID or the environment name. * @property {string} [application] - The application ID or the application name. - * @property {AppConfigDataClientConfig} [clientConfig] - Optional configuration to pass during client initialization, e.g. AWS region. */ -interface AppConfigProviderOptions { +interface AppConfigProviderOptionsBaseInterface { environment: string application?: string +} + +/** + * Interface for AppConfigProviderOptions with clientConfig property. + * + * @interface + * @extends AppConfigProviderOptionsBaseInterface + * @property {AppConfigDataClientConfig} [clientConfig] - Optional configuration to pass during client initialization, e.g. AWS region. + * @property {never} [awsSdkV3Client] - This property should never be passed. + */ +interface AppConfigProviderOptionsWithClientConfig extends AppConfigProviderOptionsBaseInterface { clientConfig?: AppConfigDataClientConfig + awsSdkV3Client?: never +} + +/** + * Interface for AppConfigProviderOptions with awsSdkV3Client property. + * + * @interface + * @extends AppConfigProviderOptionsBaseInterface + * @property {AppConfigDataClient} [awsSdkV3Client] - Optional AWS SDK v3 client to pass during AppConfigProvider class instantiation + * @property {never} [clientConfig] - This property should never be passed. + */ +interface AppConfigProviderOptionsWithClientInstance extends AppConfigProviderOptionsBaseInterface { + awsSdkV3Client?: AppConfigDataClient + clientConfig?: never } +/** + * Options for the AppConfigProvider class constructor. + * + * @type AppConfigProviderOptions + * @property {string} environment - The environment ID or the environment name. + * @property {string} [application] - The application ID or the application name. + * @property {AppConfigDataClientConfig} [clientConfig] - Optional configuration to pass during client initialization, e.g. AWS region. Mutually exclusive with awsSdkV3Client. + * @property {AppConfigDataClient} [awsSdkV3Client] - Optional AWS SDK v3 client to pass during AppConfigProvider class instantiation. Mutually exclusive with clientConfig. + */ +type AppConfigProviderOptions = AppConfigProviderOptionsWithClientConfig | AppConfigProviderOptionsWithClientInstance; + /** * Options for the AppConfigProvider get method. * @@ -40,7 +76,7 @@ interface AppConfigGetOptionsInterface extends Omit, + extends Omit, AppConfigGetOptionsInterface {} export type { diff --git a/packages/parameters/src/types/DynamoDBProvider.ts b/packages/parameters/src/types/DynamoDBProvider.ts index a0327a9557..140bf50fd2 100644 --- a/packages/parameters/src/types/DynamoDBProvider.ts +++ b/packages/parameters/src/types/DynamoDBProvider.ts @@ -1,14 +1,25 @@ import type { GetOptionsInterface, GetMultipleOptionsInterface } from './BaseProvider'; -import type { GetItemCommandInput, QueryCommandInput, DynamoDBClientConfig } from '@aws-sdk/client-dynamodb'; +import type { DynamoDBClient, GetItemCommandInput, QueryCommandInput, DynamoDBClientConfig } from '@aws-sdk/client-dynamodb'; -interface DynamoDBProviderOptions { +interface DynamoDBProviderOptionsBaseInterface { tableName: string keyAttr?: string sortAttr?: string valueAttr?: string +} + +interface DynamoDBProviderOptionsWithClientConfig extends DynamoDBProviderOptionsBaseInterface { clientConfig?: DynamoDBClientConfig + awsSdkV3Client?: never } +interface DynamoDBProviderOptionsWithClientInstance extends DynamoDBProviderOptionsBaseInterface { + awsSdkV3Client?: DynamoDBClient + clientConfig?: never +} + +type DynamoDBProviderOptions = DynamoDBProviderOptionsWithClientConfig | DynamoDBProviderOptionsWithClientInstance; + /** * Options for the DynamoDBProvider get method. * diff --git a/packages/parameters/src/types/SSMProvider.ts b/packages/parameters/src/types/SSMProvider.ts index f1127b289e..9ce1f66e97 100644 --- a/packages/parameters/src/types/SSMProvider.ts +++ b/packages/parameters/src/types/SSMProvider.ts @@ -1,4 +1,5 @@ import type { + SSMClient, SSMClientConfig, GetParameterCommandInput, GetParametersByPathCommandInput @@ -9,10 +10,18 @@ import type { TransformOptions } from './BaseProvider'; -interface SSMProviderOptionsInterface { - clientConfig: SSMClientConfig +interface SSMProviderOptionsWithClientConfig { + clientConfig?: SSMClientConfig + awsSdkV3Client?: never } +interface SSMProviderOptionsWithClientInstance { + awsSdkV3Client?: SSMClient + clientConfig?: never +} + +type SSMProviderOptions = SSMProviderOptionsWithClientConfig | SSMProviderOptionsWithClientInstance; + /** * Options for the SSMProvider get method. * @@ -56,7 +65,7 @@ type SSMGetParametersByNameFromCacheOutputType = { }; export type { - SSMProviderOptionsInterface, + SSMProviderOptions, SSMGetOptionsInterface, SSMGetMultipleOptionsInterface, SSMGetParametersByNameOptionsInterface, diff --git a/packages/parameters/src/types/SecretsProvider.ts b/packages/parameters/src/types/SecretsProvider.ts index b936309765..757f38b18b 100644 --- a/packages/parameters/src/types/SecretsProvider.ts +++ b/packages/parameters/src/types/SecretsProvider.ts @@ -1,10 +1,18 @@ import type { GetOptionsInterface } from './BaseProvider'; -import type { SecretsManagerClientConfig, GetSecretValueCommandInput } from '@aws-sdk/client-secrets-manager'; +import type { SecretsManagerClient, SecretsManagerClientConfig, GetSecretValueCommandInput } from '@aws-sdk/client-secrets-manager'; -interface SecretsProviderOptions { +interface SecretsProviderOptionsWithClientConfig { clientConfig?: SecretsManagerClientConfig + awsSdkV3Client?: never } +interface SecretsProviderOptionsWithClientInstance { + awsSdkV3Client?: SecretsManagerClient + clientConfig?: never +} + +type SecretsProviderOptions = SecretsProviderOptionsWithClientConfig | SecretsProviderOptionsWithClientInstance; + interface SecretsGetOptionsInterface extends GetOptionsInterface { sdkOptions?: Omit, 'SecretId'> } diff --git a/packages/parameters/tests/unit/AppConfigProvider.test.ts b/packages/parameters/tests/unit/AppConfigProvider.test.ts index f0b23560df..56365d882c 100644 --- a/packages/parameters/tests/unit/AppConfigProvider.test.ts +++ b/packages/parameters/tests/unit/AppConfigProvider.test.ts @@ -21,6 +21,85 @@ describe('Class: AppConfigProvider', () => { jest.clearAllMocks(); }); + describe('Method: constructor', () => { + test('when the class instantiates without SDK client and client config it has default options', async () => { + // Prepare + const options: AppConfigProviderOptions = { + application: 'MyApp', + environment: 'MyAppProdEnv', + }; + + // Act + const provider = new AppConfigProvider(options); + + // Assess + expect(provider.client.config).toEqual( + expect.objectContaining({ + serviceId: 'AppConfigData', + }) + ); + }); + + test('when the user provides a client config in the options, the class instantiates a new client with client config options', async () => { + // Prepare + const options: AppConfigProviderOptions = { + application: 'MyApp', + environment: 'MyAppProdEnv', + clientConfig: { + serviceId: 'with-client-config', + }, + }; + + // Act + const provider = new AppConfigProvider(options); + + // Assess + expect(provider.client.config).toEqual( + expect.objectContaining({ + serviceId: 'with-client-config', + }) + ); + }); + + test('when the user provides an SDK client in the options, the class instantiates with it', async () => { + // Prepare + const awsSdkV3Client = new AppConfigDataClient({ + serviceId: 'with-custom-sdk-client', + }); + + const options: AppConfigProviderOptions = { + application: 'MyApp', + environment: 'MyAppProdEnv', + awsSdkV3Client: awsSdkV3Client, + }; + + // Act + const provider = new AppConfigProvider(options); + + // Assess + expect(provider.client.config).toEqual( + expect.objectContaining({ + serviceId: 'with-custom-sdk-client', + }) + ); + }); + + test('when the user provides NOT an SDK client in the options, it throws an error', async () => { + // Prepare + const awsSdkV3Client = {}; + const options: AppConfigProviderOptions = { + application: 'MyApp', + environment: 'MyAppProdEnv', + awsSdkV3Client: awsSdkV3Client as AppConfigDataClient, + }; + + // Act & Assess + expect(() => { + new AppConfigProvider(options); + }).toThrow(); + }); + }); + describe('Method: _get', () => { test('when called with name and options, it returns binary configuration', async () => { // Prepare diff --git a/packages/parameters/tests/unit/DynamoDBProvider.test.ts b/packages/parameters/tests/unit/DynamoDBProvider.test.ts index 05dae38d47..86ebc01890 100644 --- a/packages/parameters/tests/unit/DynamoDBProvider.test.ts +++ b/packages/parameters/tests/unit/DynamoDBProvider.test.ts @@ -6,6 +6,7 @@ import { DynamoDBProvider } from '../../src/dynamodb'; import { DynamoDBClient, GetItemCommand, QueryCommand } from '@aws-sdk/client-dynamodb'; import type { GetItemCommandInput, QueryCommandInput } from '@aws-sdk/client-dynamodb'; +import type { DynamoDBProviderOptions } from '../../src/types/DynamoDBProvider'; import { marshall } from '@aws-sdk/util-dynamodb'; import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; @@ -16,6 +17,84 @@ describe('Class: DynamoDBProvider', () => { jest.clearAllMocks(); }); + describe('Method: constructor', () => { + test('when the class instantiates without SDK client and client config it has default options', async () => { + + // Prepare + const options: DynamoDBProviderOptions = { + tableName: 'test-table', + }; + + // Act + const provider = new DynamoDBProvider(options); + + // Assess + expect(provider.client.config).toEqual( + expect.objectContaining({ + serviceId: 'DynamoDB', + }) + ); + }); + + test('when the user provides a client config in the options, the class instantiates a new client with client config options', async () => { + + // Prepare + const options: DynamoDBProviderOptions = { + tableName: 'test-table', + clientConfig: { + serviceId: 'with-client-config', + }, + }; + + // Act + const provider = new DynamoDBProvider(options); + + // Assess + expect(provider.client.config).toEqual( + expect.objectContaining({ + serviceId: 'with-client-config', + }) + ); + }); + + test('when the user provides an SDK client in the options, the class instantiates with it', async () => { + + // Prepare + const awsSdkV3Client = new DynamoDBClient({ + serviceId: 'with-custom-sdk-client', + }); + + const options: DynamoDBProviderOptions = { + tableName: 'test-table', + awsSdkV3Client: awsSdkV3Client, + }; + + // Act + const provider = new DynamoDBProvider(options); + + // Assess + expect(provider.client.config).toEqual( + expect.objectContaining({ + serviceId: 'with-custom-sdk-client', + }) + ); + }); + + test('when the user provides NOT an SDK client in the options, it throws an error', async () => { + // Prepare + const awsSdkV3Client = {}; + const options: DynamoDBProviderOptions = { + tableName: 'test-table', + awsSdkV3Client: awsSdkV3Client as DynamoDBClient, + }; + + // Act & Assess + expect(() => { + new DynamoDBProvider(options); + }).toThrow(); + }); + }); + describe('Method: _get', () => { test('when called and the sdk client returns no items, it returns undefined', async () => { diff --git a/packages/parameters/tests/unit/SSMProvider.test.ts b/packages/parameters/tests/unit/SSMProvider.test.ts index a48e6a4004..856f2debec 100644 --- a/packages/parameters/tests/unit/SSMProvider.test.ts +++ b/packages/parameters/tests/unit/SSMProvider.test.ts @@ -14,6 +14,7 @@ import type { GetParametersCommandOutput } from '@aws-sdk/client-ssm'; import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; import type { + SSMProviderOptions, SSMGetParametersByNameFromCacheOutputType, SSMGetParametersByNameOptionsInterface, SSMSplitBatchAndDecryptParametersOutputType, @@ -30,6 +31,79 @@ describe('Class: SSMProvider', () => { jest.clearAllMocks(); }); + describe('Method: constructor', () => { + test('when the class instantiates without SDK client and client config it has default options', async () => { + + // Prepare + const options: SSMProviderOptions = {}; + + // Act + const provider = new SSMProvider(options); + + // Assess + expect(provider.client.config).toEqual( + expect.objectContaining({ + serviceId: 'SSM', + }) + ); + }); + + test('when the user provides a client config in the options, the class instantiates a new client with client config options', async () => { + + // Prepare + const options: SSMProviderOptions = { + clientConfig: { + serviceId: 'with-client-config', + }, + }; + + // Act + const provider = new SSMProvider(options); + + // Assess + expect(provider.client.config).toEqual( + expect.objectContaining({ + serviceId: 'with-client-config', + }) + ); + }); + + test('when the user provides an SDK client in the options, the class instantiates with it', async () => { + + // Prepare + const awsSdkV3Client = new SSMClient({ + serviceId: 'with-custom-sdk-client', + }); + + const options: SSMProviderOptions = { + awsSdkV3Client: awsSdkV3Client, + }; + + // Act + const provider = new SSMProvider(options); + + // Assess + expect(provider.client.config).toEqual( + expect.objectContaining({ + serviceId: 'with-custom-sdk-client', + }) + ); + }); + + test('when the user provides NOT an SDK client in the options, it throws an error', async () => { + // Prepare + const awsSdkV3Client = {}; + const options: SSMProviderOptions = { + awsSdkV3Client: awsSdkV3Client as SSMClient, + }; + + // Act & Assess + expect(() => { + new SSMProvider(options); + }).toThrow(); + }); + }); + describe('Method: getParametersByName', () => { class SSMProviderMock extends SSMProvider { diff --git a/packages/parameters/tests/unit/SecretsProvider.test.ts b/packages/parameters/tests/unit/SecretsProvider.test.ts index 8b4fc3067a..e35923cb32 100644 --- a/packages/parameters/tests/unit/SecretsProvider.test.ts +++ b/packages/parameters/tests/unit/SecretsProvider.test.ts @@ -6,6 +6,7 @@ import { SecretsProvider } from '../../src/secrets'; import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager'; import type { GetSecretValueCommandInput } from '@aws-sdk/client-secrets-manager'; +import type { SecretsProviderOptions } from '../../src/types/SecretsProvider'; import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; @@ -19,6 +20,79 @@ describe('Class: SecretsProvider', () => { jest.clearAllMocks(); }); + describe('Method: constructor', () => { + test('when the class instantiates without SDK client and client config it has default options', async () => { + + // Prepare + const options: SecretsProviderOptions = {}; + + // Act + const provider = new SecretsProvider(options); + + // Assess + expect(provider.client.config).toEqual( + expect.objectContaining({ + serviceId: 'Secrets Manager', + }) + ); + }); + + test('when the user provides a client config in the options, the class instantiates a new client with client config options', async () => { + + // Prepare + const options: SecretsProviderOptions = { + clientConfig: { + serviceId: 'with-client-config', + }, + }; + + // Act + const provider = new SecretsProvider(options); + + // Assess + expect(provider.client.config).toEqual( + expect.objectContaining({ + serviceId: 'with-client-config', + }) + ); + }); + + test('when the user provides an SDK client in the options, the class instantiates with it', async () => { + + // Prepare + const awsSdkV3Client = new SecretsManagerClient({ + serviceId: 'with-custom-sdk-client', + }); + + const options: SecretsProviderOptions = { + awsSdkV3Client: awsSdkV3Client, + }; + + // Act + const provider = new SecretsProvider(options); + + // Assess + expect(provider.client.config).toEqual( + expect.objectContaining({ + serviceId: 'with-custom-sdk-client', + }) + ); + }); + + test('when the user provides NOT an SDK client in the options, it throws an error', async () => { + // Prepare + const awsSdkV3Client = {}; + const options: SecretsProviderOptions = { + awsSdkV3Client: awsSdkV3Client as SecretsManagerClient, + }; + + // Act & Assess + expect(() => { + new SecretsProvider(options); + }).toThrow(); + }); + }); + describe('Method: _get', () => { test('when called with only a name, it gets the secret string', async () => { From eacb1d9f59a82ad34234f51198ed215c41a64b41 Mon Sep 17 00:00:00 2001 From: KevenFuentes9 <111793213+KevenFuentes9@users.noreply.github.com> Date: Mon, 6 Feb 2023 16:01:56 -0500 Subject: [PATCH 15/56] feat(idempotency): Add function wrapper and decorator (#1262) * feat: initial idempotency classes * feat: refactor persistence layer classes into their own folder * feat: rename idempotency config to differentiate from idempotency options * feat: added type for a generic function * feat: remove idempotency configuration for this FR * feat: refactored type of function to accept any combo of parameters * feat: adding PersistenceLayer * feat: PersistenceLayer unit tests for saveInProgress * feat: added saveSuccess * feat: added getRecord * feat: added delete record * feat: branch coverage and cleaning up imports * feat: added more tests * feat: deleted unused methods * feat: added comments * feat: implement get command for dynamo persistence layer * feat: implement get command for dynamo persistence layer * feat: allow for data attr to be passed and return in persistence layer get * feat: added implementation for delete, update, put * feat: create condition on put for not in progress status * feat: use inprogress enum for status * feat: added error when unable to get record for idempotency key * feat: added error for conditional write of an existing record * feat: tests added for put record on dynamo persistence layer * feat: implemented the idempotency record functions for status, expiry, and json response * test: check if the status is expired * test: idempotency record is not expired and status maintained * feat: added tests for get record * feat: add aws-sdk-client-mock jest assertion library * feat: add unit tests for update record and delete record * feat: remove optional chaining from item made unnecessary with error branch * feat: remove unused block * feat: refactored mock child class to be shared amongst dynamo persistence layer tests * test: add path to get the response data from the data record * feat: added branch to handle conditional check failure * feat: add configuration option to dynamo client creation to remove undefined values * feat: change how time is measured to seconds * feat: change type of the response/result to a record * feat:updated imports * feat: added save in progress to handle already existing records in dynamo * feat: add log message for the already in progress error * feat: change the anyfunction type definition to also include a sync function * refactor: create constructor object for dynamo persistence layer * fix: remove temp eslint disable * fix: adjust verbiage on test blocks Co-authored-by: ijemmy * style: put constructor parameters onto one line for readability * fix: update dynamo persistence layer tests to use new construtor options * fix: remove unneeded eslint ignore from persistence layer * style: put parameters for dynamo client command object onto one line for readability * fix: move lib-dynamo dep under the correct package * refactor: change idempotency record to use options object in contructor * feat: add consistent read to dynamo persistence layer * fix: revert changes to layer-publisher package-lock * feat added the call to the function in the idempotency handler and add overarching error scenario * feat: add logic to invoke function if it has not already been called; add logic to call the idempotency hander * chore: move and enhance comment on question * chore: update comments * feat: use new record formatting for idempotent function wrapper * test: add test case for issues even getting to save the record in persistence layer * chore: refactoring test suite for idempotent wrapper * chore: clean up comments * feat: added decorator for idempotency * chore: get rid of extra private member on the idempotency handler class * chore: refactor to use class for options * chore: bring in old version of package-locks * chore: update paths for interface * chore: remove env files * chore: rename file * chore: renaming test names and function names for idempotency decorator/wrapper --------- Co-authored-by: vgphoenixcampos <111440293+vgphoenixcampos@users.noreply.github.com> Co-authored-by: jeffrey-baker-vg Co-authored-by: Phoenix Campos Co-authored-by: ijemmy --- package-lock.json | 29 ++-- package.json | 2 +- packages/idempotency/src/Exceptions.ts | 17 +- .../idempotency/src/IdempotencyHandler.ts | 39 +++++ .../idempotency/src/idempotentDecorator.ts | 19 ++ .../idempotency/src/makeFunctionIdempotent.ts | 26 ++- packages/idempotency/src/types/AnyFunction.ts | 9 +- packages/idempotency/src/types/index.ts | 3 +- .../helpers/populateEnvironmentVariables.ts | 2 +- .../tests/unit/idempotentDecorator.test.ts | 163 ++++++++++++++++++ .../tests/unit/makeFunctionIdempotent.test.ts | 163 ++++++++++++++++++ .../DynamoDbPersistenceLayer.test.ts | 1 - 12 files changed, 444 insertions(+), 29 deletions(-) create mode 100644 packages/idempotency/src/IdempotencyHandler.ts create mode 100644 packages/idempotency/src/idempotentDecorator.ts create mode 100644 packages/idempotency/tests/unit/idempotentDecorator.test.ts create mode 100644 packages/idempotency/tests/unit/makeFunctionIdempotent.test.ts diff --git a/package-lock.json b/package-lock.json index 4eceee618a..1c7a720b79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16623,7 +16623,7 @@ }, "packages/commons": { "name": "@aws-lambda-powertools/commons", - "version": "1.5.1", + "version": "1.2.1", "license": "MIT-0" }, "packages/idempotency": { @@ -16641,11 +16641,13 @@ }, "packages/logger": { "name": "@aws-lambda-powertools/logger", - "version": "1.5.1", + "version": "1.2.1", "license": "MIT", "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.1", - "lodash.merge": "^4.6.2" + "@aws-lambda-powertools/commons": "^1.2.1", + "lodash.clonedeep": "^4.5.0", + "lodash.merge": "^4.6.2", + "lodash.pickby": "^4.6.0" }, "devDependencies": { "@types/lodash.merge": "^4.6.7" @@ -16653,10 +16655,10 @@ }, "packages/metrics": { "name": "@aws-lambda-powertools/metrics", - "version": "1.5.1", + "version": "1.2.1", "license": "MIT-0", "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.1" + "@aws-lambda-powertools/commons": "^1.2.1" }, "devDependencies": { "@types/promise-retry": "^1.1.3", @@ -16694,11 +16696,11 @@ }, "packages/tracer": { "name": "@aws-lambda-powertools/tracer", - "version": "1.5.1", + "version": "1.2.1", "license": "MIT-0", "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.1", - "aws-xray-sdk-core": "^3.4.0" + "@aws-lambda-powertools/commons": "^1.2.1", + "aws-xray-sdk-core": "^3.3.6" }, "devDependencies": { "@aws-sdk/client-dynamodb": "^3.231.0", @@ -16943,7 +16945,8 @@ "@aws-lambda-powertools/logger": { "version": "file:packages/logger", "requires": { - "@aws-lambda-powertools/commons": "^1.5.1", + "@aws-lambda-powertools/commons": "^1.2.1", + "@types/lodash.clonedeep": "^4.5.7", "@types/lodash.merge": "^4.6.7", "lodash.merge": "^4.6.2" } @@ -16951,7 +16954,7 @@ "@aws-lambda-powertools/metrics": { "version": "file:packages/metrics", "requires": { - "@aws-lambda-powertools/commons": "^1.5.1", + "@aws-lambda-powertools/commons": "^1.2.1", "@types/promise-retry": "^1.1.3", "promise-retry": "^2.0.1" } @@ -16983,8 +16986,8 @@ "@aws-lambda-powertools/tracer": { "version": "file:packages/tracer", "requires": { - "@aws-lambda-powertools/commons": "^1.5.1", - "@aws-sdk/client-dynamodb": "^3.231.0", + "@aws-lambda-powertools/commons": "^1.2.1", + "@aws-sdk/client-dynamodb": "^3.100.0", "@types/promise-retry": "^1.1.3", "aws-sdk": "^2.1276.0", "aws-xray-sdk-core": "^3.4.0", diff --git a/package.json b/package.json index c7ca00b1e1..b937ffeb0a 100644 --- a/package.json +++ b/package.json @@ -86,4 +86,4 @@ "dependencies": { "hosted-git-info": "^6.1.1" } -} \ No newline at end of file +} diff --git a/packages/idempotency/src/Exceptions.ts b/packages/idempotency/src/Exceptions.ts index 405a59aa66..d39a01b37e 100644 --- a/packages/idempotency/src/Exceptions.ts +++ b/packages/idempotency/src/Exceptions.ts @@ -10,8 +10,23 @@ class IdempotencyInvalidStatusError extends Error { } +class IdempotencyInconsistentStateError extends Error { + +} + +class IdempotencyAlreadyInProgressError extends Error { + +} + +class IdempotencyPersistenceLayerError extends Error { + +} + export { IdempotencyItemNotFoundError, IdempotencyItemAlreadyExistsError, - IdempotencyInvalidStatusError + IdempotencyInvalidStatusError, + IdempotencyInconsistentStateError, + IdempotencyAlreadyInProgressError, + IdempotencyPersistenceLayerError }; \ No newline at end of file diff --git a/packages/idempotency/src/IdempotencyHandler.ts b/packages/idempotency/src/IdempotencyHandler.ts new file mode 100644 index 0000000000..e629fa917b --- /dev/null +++ b/packages/idempotency/src/IdempotencyHandler.ts @@ -0,0 +1,39 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { AnyFunctionWithRecord, IdempotencyRecordStatus } from './types'; +import { IdempotencyOptions } from './types/IdempotencyOptions'; +import { IdempotencyRecord } from 'persistence'; +import { IdempotencyInconsistentStateError, IdempotencyItemAlreadyExistsError, IdempotencyAlreadyInProgressError, IdempotencyPersistenceLayerError } from './Exceptions'; + +export class IdempotencyHandler { + + public constructor(private functionToMakeIdempotent: AnyFunctionWithRecord, private functionPayloadToBeHashed: unknown, + private idempotencyOptions: IdempotencyOptions, private fullFunctionPayload: Record) {} + + public determineResultFromIdempotencyRecord(idempotencyRecord: IdempotencyRecord): Promise | U{ + if (idempotencyRecord.getStatus() === IdempotencyRecordStatus.EXPIRED) { + throw new IdempotencyInconsistentStateError('Item has expired during processing and may not longer be valid.'); + } else if (idempotencyRecord.getStatus() === IdempotencyRecordStatus.INPROGRESS){ + throw new IdempotencyAlreadyInProgressError(`There is already an execution in progress with idempotency key: ${idempotencyRecord.idempotencyKey}`); + } else { + // Currently recalling the method as this fulfills FR1. FR3 will address using the previously stored value https://github.com/awslabs/aws-lambda-powertools-typescript/issues/447 + return this.functionToMakeIdempotent(this.fullFunctionPayload); + } + } + + public async processIdempotency(): Promise { + try { + await this.idempotencyOptions.persistenceStore.saveInProgress(this.functionPayloadToBeHashed); + } catch (e) { + if (e instanceof IdempotencyItemAlreadyExistsError) { + const idempotencyRecord: IdempotencyRecord = await this.idempotencyOptions.persistenceStore.getRecord(this.functionPayloadToBeHashed); + + return this.determineResultFromIdempotencyRecord(idempotencyRecord); + } else { + throw new IdempotencyPersistenceLayerError(); + } + } + + return this.functionToMakeIdempotent(this.fullFunctionPayload); + } +} \ No newline at end of file diff --git a/packages/idempotency/src/idempotentDecorator.ts b/packages/idempotency/src/idempotentDecorator.ts new file mode 100644 index 0000000000..51e387b428 --- /dev/null +++ b/packages/idempotency/src/idempotentDecorator.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { IdempotencyOptions } from './types/IdempotencyOptions'; +import { IdempotencyHandler } from './IdempotencyHandler'; + +const idempotent = function (options: IdempotencyOptions) { + return function (_target: any, _propertyKey: string, descriptor: PropertyDescriptor) { + const childFunction = descriptor.value; + descriptor.value = function(record: Record){ + const idempotencyHandler: IdempotencyHandler = new IdempotencyHandler(childFunction, record[options.dataKeywordArgument], options, record); + + return idempotencyHandler.processIdempotency(); + }; + + return descriptor; + }; +}; + +export { idempotent }; + \ No newline at end of file diff --git a/packages/idempotency/src/makeFunctionIdempotent.ts b/packages/idempotency/src/makeFunctionIdempotent.ts index 40b6e52acf..fd416ccfc5 100644 --- a/packages/idempotency/src/makeFunctionIdempotent.ts +++ b/packages/idempotency/src/makeFunctionIdempotent.ts @@ -1,11 +1,19 @@ -import type { AnyFunction } from './types/AnyFunction'; -import type { IdempotencyOptions } from './types/IdempotencyOptions'; - -const makeFunctionIdempotent = ( - fn: AnyFunction, - _options: IdempotencyOptions - // TODO: revisit this with a more specific type if possible - /* eslint-disable @typescript-eslint/no-explicit-any */ -): (...args: Array) => Promise => (...args) => fn(...args); +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { AnyFunctionWithRecord, AnyIdempotentFunction } from './types/AnyFunction'; +import { IdempotencyOptions } from './types/IdempotencyOptions'; +import { IdempotencyHandler } from './IdempotencyHandler'; + +const makeFunctionIdempotent = function ( + fn: AnyFunctionWithRecord, + options: IdempotencyOptions +): AnyIdempotentFunction { + const wrappedFn: AnyIdempotentFunction = function (record: Record): Promise { + const idempotencyHandler: IdempotencyHandler = new IdempotencyHandler(fn, record[options.dataKeywordArgument], options, record); + + return idempotencyHandler.processIdempotency(); + }; + + return wrappedFn; +}; export { makeFunctionIdempotent }; diff --git a/packages/idempotency/src/types/AnyFunction.ts b/packages/idempotency/src/types/AnyFunction.ts index bddcd8fc15..4b2a894a13 100644 --- a/packages/idempotency/src/types/AnyFunction.ts +++ b/packages/idempotency/src/types/AnyFunction.ts @@ -1,6 +1,11 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any -type AnyFunction = (...args: Array) => Promise; +type AnyFunctionWithRecord = (record: Record) => Promise | U; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyIdempotentFunction = (record: Record) => Promise; export { - AnyFunction + // AnyFunction, + AnyFunctionWithRecord, + AnyIdempotentFunction }; \ No newline at end of file diff --git a/packages/idempotency/src/types/index.ts b/packages/idempotency/src/types/index.ts index 307c6bc093..c725c5a6ba 100644 --- a/packages/idempotency/src/types/index.ts +++ b/packages/idempotency/src/types/index.ts @@ -1,3 +1,4 @@ export * from './AnyFunction'; export * from './IdempotencyRecordStatus'; -export * from './PersistenceLayer'; \ No newline at end of file +export * from './IdempotencyRecordOptions'; +export * from './PersistenceLayer'; diff --git a/packages/idempotency/tests/helpers/populateEnvironmentVariables.ts b/packages/idempotency/tests/helpers/populateEnvironmentVariables.ts index 0511a61959..cac6f99929 100644 --- a/packages/idempotency/tests/helpers/populateEnvironmentVariables.ts +++ b/packages/idempotency/tests/helpers/populateEnvironmentVariables.ts @@ -6,4 +6,4 @@ process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '128'; if (process.env.AWS_REGION === undefined && process.env.CDK_DEFAULT_REGION === undefined) { process.env.AWS_REGION = 'eu-west-1'; } -process.env._HANDLER = 'index.handler'; \ No newline at end of file +process.env._HANDLER = 'index.handler'; diff --git a/packages/idempotency/tests/unit/idempotentDecorator.test.ts b/packages/idempotency/tests/unit/idempotentDecorator.test.ts new file mode 100644 index 0000000000..3df2689116 --- /dev/null +++ b/packages/idempotency/tests/unit/idempotentDecorator.test.ts @@ -0,0 +1,163 @@ +/** + * Test Function Wrapper + * + * @group unit/idempotency/all + */ + +import { IdempotencyOptions } from '../../src/types/IdempotencyOptions'; +import { PersistenceLayer, IdempotencyRecord } from '../../src/persistence'; +import { idempotent } from '../../src/idempotentDecorator'; +import { IdempotencyRecordStatus, IdempotencyRecordOptions } from '../../src/types'; +import { IdempotencyItemAlreadyExistsError, IdempotencyAlreadyInProgressError, IdempotencyInconsistentStateError, IdempotencyPersistenceLayerError } from '../../src/Exceptions'; + +const mockSaveInProgress = jest.spyOn(PersistenceLayer.prototype, 'saveInProgress').mockImplementation(); +const mockGetRecord = jest.spyOn(PersistenceLayer.prototype, 'getRecord').mockImplementation(); + +class PersistenceLayerTestClass extends PersistenceLayer { + protected _deleteRecord = jest.fn(); + protected _getRecord = jest.fn(); + protected _putRecord = jest.fn(); + protected _updateRecord = jest.fn(); +} + +const options: IdempotencyOptions = { persistenceStore: new PersistenceLayerTestClass(), dataKeywordArgument: 'testingKey' }; +const functionalityToDecorate = jest.fn(); + +class TestingClass { + @idempotent(options) + public testing(record: Record): string { + functionalityToDecorate(record); + + return 'Hi'; + } +} + +describe('Given a class with a function to decorate', (classWithFunction = new TestingClass()) => { + const keyValueToBeSaved = 'thisWillBeSaved'; + const inputRecord = { testingKey: keyValueToBeSaved, otherKey: 'thisWillNot' }; + beforeEach(()=> jest.clearAllMocks()); + describe('When wrapping a function with no previous executions', () => { + beforeEach(async () => { + classWithFunction.testing(inputRecord); + }); + + test('Then it will save the record to INPROGRESS', () => { + expect(mockSaveInProgress).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then it will call the function that was decorated', () => { + expect(functionalityToDecorate).toBeCalledWith(inputRecord); + }); + }); + + describe('When decorating a function with previous execution that is INPROGRESS', () => { + let resultingError: Error; + beforeEach(async () => { + mockSaveInProgress.mockRejectedValue(new IdempotencyItemAlreadyExistsError()); + const idempotencyOptions: IdempotencyRecordOptions = { + idempotencyKey: 'key', + status: IdempotencyRecordStatus.INPROGRESS + }; + mockGetRecord.mockResolvedValue(new IdempotencyRecord(idempotencyOptions)); + try { + await classWithFunction.testing(inputRecord); + } catch (e) { + resultingError = e as Error; + } + }); + + test('Then it will attempt to save the record to INPROGRESS', () => { + expect(mockSaveInProgress).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then it will get the previous execution record', () => { + expect(mockGetRecord).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then it will not call the function that was decorated', () => { + expect(functionalityToDecorate).not.toBeCalled(); + }); + + test('Then an IdempotencyAlreadyInProgressError is thrown', () => { + expect(resultingError).toBeInstanceOf(IdempotencyAlreadyInProgressError); + }); + }); + + describe('When decorating a function with previous execution that is EXPIRED', () => { + let resultingError: Error; + beforeEach(async () => { + mockSaveInProgress.mockRejectedValue(new IdempotencyItemAlreadyExistsError()); + const idempotencyOptions: IdempotencyRecordOptions = { + idempotencyKey: 'key', + status: IdempotencyRecordStatus.EXPIRED + }; + mockGetRecord.mockResolvedValue(new IdempotencyRecord(idempotencyOptions)); + try { + await classWithFunction.testing(inputRecord); + } catch (e) { + resultingError = e as Error; + } + }); + + test('Then it will attempt to save the record to INPROGRESS', () => { + expect(mockSaveInProgress).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then it will get the previous execution record', () => { + expect(mockGetRecord).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then it will not call the function that was decorated', () => { + expect(functionalityToDecorate).not.toBeCalled(); + }); + + test('Then an IdempotencyInconsistentStateError is thrown', () => { + expect(resultingError).toBeInstanceOf(IdempotencyInconsistentStateError); + }); + }); + + describe('When wrapping a function with previous execution that is COMPLETED', () => { + beforeEach(async () => { + mockSaveInProgress.mockRejectedValue(new IdempotencyItemAlreadyExistsError()); + const idempotencyOptions: IdempotencyRecordOptions = { + idempotencyKey: 'key', + status: IdempotencyRecordStatus.COMPLETED + }; + mockGetRecord.mockResolvedValue(new IdempotencyRecord(idempotencyOptions)); + await classWithFunction.testing(inputRecord); + }); + + test('Then it will attempt to save the record to INPROGRESS', () => { + expect(mockSaveInProgress).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then it will get the previous execution record', () => { + expect(mockGetRecord).toBeCalledWith(keyValueToBeSaved); + }); + + //This should be the saved record once FR3 is complete https://github.com/awslabs/aws-lambda-powertools-typescript/issues/447 + test('Then it will call the function that was decorated with the whole input record', () => { + expect(functionalityToDecorate).toBeCalledWith(inputRecord); + }); + }); + + describe('When wrapping a function with issues saving the record', () => { + let resultingError: Error; + beforeEach(async () => { + mockSaveInProgress.mockRejectedValue(new Error('RandomError')); + try { + await classWithFunction.testing(inputRecord); + } catch (e) { + resultingError = e as Error; + } + }); + + test('Then it will attempt to save the record to INPROGRESS', () => { + expect(mockSaveInProgress).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then an IdempotencyPersistenceLayerError is thrown', () => { + expect(resultingError).toBeInstanceOf(IdempotencyPersistenceLayerError); + }); + }); +}); \ No newline at end of file diff --git a/packages/idempotency/tests/unit/makeFunctionIdempotent.test.ts b/packages/idempotency/tests/unit/makeFunctionIdempotent.test.ts new file mode 100644 index 0000000000..95fc50b068 --- /dev/null +++ b/packages/idempotency/tests/unit/makeFunctionIdempotent.test.ts @@ -0,0 +1,163 @@ +/** + * Test Function Wrapper + * + * @group unit/idempotency/all + */ + +import { IdempotencyOptions } from '../../src/types/IdempotencyOptions'; +import { IdempotencyRecord, PersistenceLayer } from '../../src/persistence'; +import { makeFunctionIdempotent } from '../../src/makeFunctionIdempotent'; +import { AnyIdempotentFunction, IdempotencyRecordStatus, IdempotencyRecordOptions } from '../../src/types'; +import { IdempotencyItemAlreadyExistsError, IdempotencyAlreadyInProgressError, IdempotencyInconsistentStateError, IdempotencyPersistenceLayerError } from '../../src/Exceptions'; + +const mockSaveInProgress = jest.spyOn(PersistenceLayer.prototype, 'saveInProgress').mockImplementation(); +const mockGetRecord = jest.spyOn(PersistenceLayer.prototype, 'getRecord').mockImplementation(); + +class PersistenceLayerTestClass extends PersistenceLayer { + protected _deleteRecord = jest.fn(); + protected _getRecord = jest.fn(); + protected _putRecord = jest.fn(); + protected _updateRecord = jest.fn(); +} + +describe('Given a function to wrap', (functionToWrap = jest.fn()) => { + beforeEach(()=> jest.clearAllMocks()); + describe('Given options for idempotency', (options: IdempotencyOptions = { persistenceStore: new PersistenceLayerTestClass(), dataKeywordArgument: 'testingKey' }) => { + const keyValueToBeSaved = 'thisWillBeSaved'; + const inputRecord = { testingKey: keyValueToBeSaved, otherKey: 'thisWillNot' }; + describe('When wrapping a function with no previous executions', () => { + let resultingFunction: AnyIdempotentFunction; + beforeEach(async () => { + resultingFunction = makeFunctionIdempotent(functionToWrap, options); + await resultingFunction(inputRecord); + }); + + test('Then it will save the record to INPROGRESS', () => { + expect(mockSaveInProgress).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then it will call the function that was wrapped with the whole input record', () => { + expect(functionToWrap).toBeCalledWith(inputRecord); + }); + }); + + describe('When wrapping a function with previous execution that is INPROGRESS', () => { + let resultingFunction: AnyIdempotentFunction; + let resultingError: Error; + beforeEach(async () => { + mockSaveInProgress.mockRejectedValue(new IdempotencyItemAlreadyExistsError()); + const idempotencyOptions: IdempotencyRecordOptions = { + idempotencyKey: 'key', + status: IdempotencyRecordStatus.INPROGRESS + }; + mockGetRecord.mockResolvedValue(new IdempotencyRecord(idempotencyOptions)); + resultingFunction = makeFunctionIdempotent(functionToWrap, options); + try { + await resultingFunction(inputRecord); + } catch (e) { + resultingError = e as Error; + } + }); + + test('Then it will attempt to save the record to INPROGRESS', () => { + expect(mockSaveInProgress).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then it will get the previous execution record', () => { + expect(mockGetRecord).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then the function that was wrapped is not called again', () => { + expect(functionToWrap).not.toBeCalled(); + }); + + test('Then an IdempotencyAlreadyInProgressError is thrown', ()=> { + expect(resultingError).toBeInstanceOf(IdempotencyAlreadyInProgressError); + }); + }); + + describe('When wrapping a function with previous execution that is EXPIRED', () => { + let resultingFunction: AnyIdempotentFunction; + let resultingError: Error; + beforeEach(async () => { + mockSaveInProgress.mockRejectedValue(new IdempotencyItemAlreadyExistsError()); + const idempotencyOptions: IdempotencyRecordOptions = { + idempotencyKey: 'key', + status: IdempotencyRecordStatus.EXPIRED + }; + mockGetRecord.mockResolvedValue(new IdempotencyRecord(idempotencyOptions)); + resultingFunction = makeFunctionIdempotent(functionToWrap, options); + try { + await resultingFunction(inputRecord); + } catch (e) { + resultingError = e as Error; + } + }); + + test('Then it will attempt to save the record to INPROGRESS', () => { + expect(mockSaveInProgress).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then it will get the previous execution record', () => { + expect(mockGetRecord).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then the function that was wrapped is not called again', () => { + expect(functionToWrap).not.toBeCalled(); + }); + + test('Then an IdempotencyInconsistentStateError is thrown', ()=> { + expect(resultingError).toBeInstanceOf(IdempotencyInconsistentStateError); + }); + }); + + describe('When wrapping a function with previous execution that is COMPLETED', () => { + let resultingFunction: AnyIdempotentFunction; + beforeEach(async () => { + mockSaveInProgress.mockRejectedValue(new IdempotencyItemAlreadyExistsError()); + const idempotencyOptions: IdempotencyRecordOptions = { + idempotencyKey: 'key', + status: IdempotencyRecordStatus.COMPLETED + }; + mockGetRecord.mockResolvedValue(new IdempotencyRecord(idempotencyOptions)); + resultingFunction = makeFunctionIdempotent(functionToWrap, options); + await resultingFunction(inputRecord); + }); + + test('Then it will attempt to save the record to INPROGRESS', () => { + expect(mockSaveInProgress).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then it will get the previous execution record', () => { + expect(mockGetRecord).toBeCalledWith(keyValueToBeSaved); + }); + + //This should be the saved record once FR3 is complete https://github.com/awslabs/aws-lambda-powertools-typescript/issues/447 + test('Then it will call the function that was wrapped with the whole input record', () => { + expect(functionToWrap).toBeCalledWith(inputRecord); + }); + }); + + describe('When wrapping a function with issues saving the record', () => { + let resultingFunction: AnyIdempotentFunction; + let resultingError: Error; + beforeEach(async () => { + mockSaveInProgress.mockRejectedValue(new Error('RandomError')); + resultingFunction = makeFunctionIdempotent(functionToWrap, options); + try { + await resultingFunction(inputRecord); + } catch (e) { + resultingError = e as Error; + } + }); + + test('Then it will attempt to save the record to INPROGRESS', () => { + expect(mockSaveInProgress).toBeCalledWith(keyValueToBeSaved); + }); + + test('Then an IdempotencyPersistenceLayerError is thrown', ()=> { + expect(resultingError).toBeInstanceOf(IdempotencyPersistenceLayerError); + }); + }); + }); +}); \ No newline at end of file diff --git a/packages/idempotency/tests/unit/persistence/DynamoDbPersistenceLayer.test.ts b/packages/idempotency/tests/unit/persistence/DynamoDbPersistenceLayer.test.ts index 46d3b3e23d..8d95de55e2 100644 --- a/packages/idempotency/tests/unit/persistence/DynamoDbPersistenceLayer.test.ts +++ b/packages/idempotency/tests/unit/persistence/DynamoDbPersistenceLayer.test.ts @@ -34,7 +34,6 @@ describe('Class: DynamoDBPersistenceLayer', () => { public _updateRecord(record: IdempotencyRecord): Promise { return super._updateRecord(record); } - } beforeEach(() => { From 64771fc173fd4995cbc71c005f2737cb68b3904c Mon Sep 17 00:00:00 2001 From: Alexander Schueren Date: Tue, 7 Feb 2023 10:45:26 +0100 Subject: [PATCH 16/56] add parameter package to package size workflow (#1266) --- .github/workflows/measure-packages-size.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/measure-packages-size.yml b/.github/workflows/measure-packages-size.yml index 830cdd867e..58d6c2df3d 100644 --- a/.github/workflows/measure-packages-size.yml +++ b/.github/workflows/measure-packages-size.yml @@ -33,7 +33,7 @@ jobs: - name: Packages size report uses: flochaz/pkg-size-action@v2.0.0 with: - build-command: mkdir dist && npm run package -w packages/logger -w packages/tracer -w packages/metrics -w packages/commons && npm run package-bundle -w packages/logger -w packages/tracer -w packages/metrics -w packages/commons && bash -c "mv ./packages/*/dist/* dist/" && ls dist + build-command: mkdir dist && npm run package -w packages/logger -w packages/tracer -w packages/metrics -w packages/commons -w packages/parameters && npm run package-bundle -w packages/logger -w packages/tracer -w packages/metrics -w packages/commons -w packages/parameters && bash -c "mv ./packages/*/dist/* dist/" && ls dist dist-directory: /dist pr-number: ${{ inputs.prNumber }} env: From de02b3db0113c06473554bfce6acb81816c1ceef Mon Sep 17 00:00:00 2001 From: Alexander Schueren Date: Tue, 7 Feb 2023 17:55:20 +0100 Subject: [PATCH 17/56] tests(parameters): integration tests for `SecretsProvider` (#1263) * change to more generic name * fix conditional * add provider to signature * add github issue to todo * add tests for caching and forceFetch --- ...secretsProvider.class.test.functionCode.ts | 93 +++++++++ .../tests/e2e/secretsProvider.class.test.ts | 188 ++++++++++++++++++ .../tests/helpers/cdkAspectGrantAccess.ts | 10 +- 3 files changed, 286 insertions(+), 5 deletions(-) create mode 100644 packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts create mode 100644 packages/parameters/tests/e2e/secretsProvider.class.test.ts diff --git a/packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts b/packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts new file mode 100644 index 0000000000..655b9472a6 --- /dev/null +++ b/packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts @@ -0,0 +1,93 @@ +import { Context } from 'aws-lambda'; +import { SecretsProvider } from '../../lib/secrets'; +import { TinyLogger } from '../helpers/tinyLogger'; +import { SecretsGetOptionsInterface } from '../../lib/types'; +import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager'; +import { middleware } from '../helpers/sdkMiddlewareRequestCounter'; + +const logger = new TinyLogger(); +const defaultProvider = new SecretsProvider(); + +const secretNamePlain = process.env.SECRET_NAME_PLAIN || ''; +const secretNameObject = process.env.SECRET_NAME_OBJECT || ''; +const secretNameBinary = process.env.SECRET_NAME_BINARY || ''; +const secretNameObjectWithSuffix = process.env.SECRET_NAME_OBJECT_WITH_SUFFIX || ''; +const secretNameBinaryWithSuffix = process.env.SECRET_NAME_BINARY_WITH_SUFFIX || ''; +const secretNamePlainChached = process.env.SECRET_NAME_PLAIN_CACHED || ''; + +// Provider test 8, 9 +const customClient = new SecretsManagerClient({}); +customClient.middlewareStack.use(middleware); +const providerWithMiddleware = new SecretsProvider({ + awsSdkV3Client: customClient +}); + +const _call_get = async (paramName: string, testName: string, options?: SecretsGetOptionsInterface, provider?: SecretsProvider,): Promise => { + try { + // we might get a provider with specific sdk options, otherwise fallback to default + const currentProvider = provider ? provider : defaultProvider; + + const parameterValue = await currentProvider.get(paramName, options); + logger.log({ + test: testName, + value: parameterValue + }); + } catch (err) { + logger.log({ + test: testName, + error: err.message + }); + } +}; + +export const handler = async (_event: unknown, _context: Context): Promise => { + + // Test 1 get single param as plaintext + await _call_get(secretNamePlain, 'get-plain'); + + // Test 2 get single param with transform json + await _call_get(secretNameObject, 'get-transform-json', { transform: 'json' }); + + // Test 3 get single param with transform binary + await _call_get(secretNameBinary, 'get-transform-binary', { transform: 'binary' }); + + // Test 4 get single param with transform auto json + await _call_get(secretNameObjectWithSuffix, 'get-transform-auto-json', { transform: 'auto' }); + + // Test 5 get single param with transform auto binary + await _call_get(secretNameBinaryWithSuffix, 'get-transform-auto-binary', { transform: 'auto' }); + + // Test 6 + // get parameter twice with middleware, which counts number of SDK requests, we check later if we only called SecretManager API once + try { + middleware.counter = 0; + await providerWithMiddleware.get(secretNamePlainChached); + await providerWithMiddleware.get(secretNamePlainChached); + logger.log({ + test: 'get-plain-cached', + value: middleware.counter // should be 1 + }); + } catch (err) { + logger.log({ + test: secretNamePlainChached, + error: err.message + }); + } + // Test 7 + // get parameter twice, but force fetch 2nd time, we count number of SDK requests and check that we made two API calls + try { + middleware.counter = 0; + await providerWithMiddleware.get(secretNamePlainChached); + await providerWithMiddleware.get(secretNamePlainChached, { forceFetch: true }); + logger.log({ + test: 'get-plain-force', + value: middleware.counter // should be 2 + }); + } catch (err) { + logger.log({ + test: secretNamePlainChached, + error: err.message + }); + } + +}; diff --git a/packages/parameters/tests/e2e/secretsProvider.class.test.ts b/packages/parameters/tests/e2e/secretsProvider.class.test.ts new file mode 100644 index 0000000000..a4ea351151 --- /dev/null +++ b/packages/parameters/tests/e2e/secretsProvider.class.test.ts @@ -0,0 +1,188 @@ +/** + * Test SecretsPorovider class + * + * @group e2e/parameters/secrets/class + */ +import { + createStackWithLambdaFunction, + generateUniqueName, + invokeFunction, + isValidRuntimeKey +} from '../../../commons/tests/utils/e2eUtils'; +import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT } from './constants'; +import { v4 } from 'uuid'; +import { Tracing } from 'aws-cdk-lib/aws-lambda'; +import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; +import { App, Aspects, SecretValue, Stack } from 'aws-cdk-lib'; +import path from 'path'; +import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; +import { InvocationLogs } from '../../../commons/tests/utils/InvocationLogs'; +import { ResourceAccessGranter } from '../helpers/cdkAspectGrantAccess'; + +const runtime: string = process.env.RUNTIME || 'nodejs18x'; + +if (!isValidRuntimeKey(runtime)) { + throw new Error(`Invalid runtime key: ${runtime}`); +} + +const uuid = v4(); +const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'secretsProvider'); +const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'secretsProvider'); +const lambdaFunctionCodeFile = 'secretsProvider.class.test.functionCode.ts'; + +const secretNamePlain = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretPlain'); +const secretNamePlainCached = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretPlainCached'); +const secretNameObject = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject'); +const secretNameBinary = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretBinary'); +const secretNameObjectWithSuffix = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject.json'); +const secretNameBinaryWithSuffix = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject.binary'); + +const invocationCount = 1; + +const integTestApp = new App(); +let stack: Stack; + +describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () => { + + let invocationLogs: InvocationLogs[]; + + beforeAll(async () => { + stack = createStackWithLambdaFunction({ + app: integTestApp, + stackName: stackName, + functionName: functionName, + functionEntry: path.join(__dirname, lambdaFunctionCodeFile), + tracing: Tracing.ACTIVE, + environment: { + UUID: uuid, + SECRET_NAME_PLAIN: secretNamePlain, + SECRET_NAME_OBJECT: secretNameObject, + SECRET_NAME_BINARY: secretNameBinary, + SECRET_NAME_OBJECT_WITH_SUFFIX: secretNameObjectWithSuffix, + SECRET_NAME_BINARY_WITH_SUFFIX: secretNameBinaryWithSuffix, + SECRET_NAME_PLAIN_CACHED: secretNamePlainCached, + }, + runtime: runtime + }); + + const secretString = new Secret(stack, 'testSecretPlain', { + secretName: secretNamePlain, + secretStringValue: SecretValue.unsafePlainText('foo') + }); + + const secretObject = new Secret(stack, 'testSecretObject', { + secretName: secretNameObject, + secretObjectValue: { + foo: SecretValue.unsafePlainText('bar'), + } + }); + + const secretBinary = new Secret(stack, 'testSecretBinary', { + secretName: secretNameBinary, + secretStringValue: SecretValue.unsafePlainText('Zm9v') // 'foo' encoded in base64 + }); + + const secretObjectWithSuffix = new Secret(stack, 'testSecretObjectWithSuffix', { + secretName: secretNameObjectWithSuffix, + secretObjectValue: { + foo: SecretValue.unsafePlainText('bar') + } + }); + + const secretBinaryWithSuffix = new Secret(stack, 'testSecretBinaryWithSuffix', { + secretName: secretNameBinaryWithSuffix, + secretStringValue: SecretValue.unsafePlainText('Zm9v') // 'foo' encoded in base64 + }); + + const secretStringCached = new Secret(stack, 'testSecretStringCached', { + secretName: secretNamePlainCached, + secretStringValue: SecretValue.unsafePlainText('foo') + }); + + Aspects.of(stack).add(new ResourceAccessGranter([ secretString, secretObject, secretBinary, secretObjectWithSuffix, secretBinaryWithSuffix, secretStringCached ])); + + await deployStack(integTestApp, stack); + + invocationLogs = await invokeFunction(functionName, invocationCount, 'SEQUENTIAL'); + + }, SETUP_TIMEOUT); + + describe('SecretsProvider usage', () => { + it('should retrieve a single parameter', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[0]); + + expect(testLog).toStrictEqual({ + test: 'get-plain', + value: 'foo' + }); + }, TEST_CASE_TIMEOUT); + + it('should retrieve a single parameter with transform json', async () => { + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[1]); + + expect(testLog).toStrictEqual({ + test: 'get-transform-json', + value: { foo: 'bar' } + }); + }, TEST_CASE_TIMEOUT); + + it('should retrieve single param with transform binary', async () => { + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[2]); + + expect(testLog).toStrictEqual({ + test: 'get-transform-binary', + value: 'foo' + }); + }, TEST_CASE_TIMEOUT); + }); + + it('should retrieve single param with transform auto json', async () => { + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[3]); + + expect(testLog).toStrictEqual({ + test: 'get-transform-auto-json', + value: { foo: 'bar' } + }); + }, TEST_CASE_TIMEOUT); + + it('should retrieve single param wit transform auto binary', async () => { + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[4]); + + expect(testLog).toStrictEqual({ + test: 'get-transform-auto-binary', + value: 'foo' + }); + }); + + it('should retrieve single parameter cached', async () => { + const logs = invocationLogs[0].getFunctionLogs(); + const testLogFirst = InvocationLogs.parseFunctionLog(logs[5]); + + expect(testLogFirst).toStrictEqual({ + test: 'get-plain-cached', + value: 1 + }); + }); + + it('should retrieve single parameter twice without caching', async () => { + const logs = invocationLogs[0].getFunctionLogs(); + const testLogFirst = InvocationLogs.parseFunctionLog(logs[6]); + + expect(testLogFirst).toStrictEqual({ + test: 'get-plain-force', + value: 1 + }); + }); + + afterAll(async () => { + if (!process.env.DISABLE_TEARDOWN) { + await destroyStack(integTestApp, stack); + } + }, TEARDOWN_TIMEOUT); +}); \ No newline at end of file diff --git a/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts b/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts index 22f8c09e6f..57d99a393b 100644 --- a/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts +++ b/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts @@ -6,23 +6,23 @@ import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; /** * An aspect that grants access to resources to a Lambda function. - * + * * In our integration tests, we dynamically generate AWS CDK stacks that contain a Lambda function. * We want to grant access to resources to the Lambda function, but we don't know the name of the * Lambda function at the time we create the resources. Additionally, we want to keep the code * that creates the stacks and functions as generic as possible. - * + * * This aspect allows us to grant access to specific resources to all Lambda functions in a stack * after the stack tree has been generated and before the stack is deployed. This aspect is * used to grant access to different resource types (DynamoDB tables, SSM parameters, etc.). - * + * * @see {@link https://docs.aws.amazon.com/cdk/v2/guide/aspects.html|CDK Docs - Aspects} */ export class ResourceAccessGranter implements IAspect { private readonly resources: Table[] | Secret[]; - public constructor(tables: Table[] | Secret[]) { - this.resources = tables; + public constructor(resources: Table[] | Secret[]) { + this.resources = resources; } public visit(node: IConstruct): void { From aa144075f3476538932312bbc1e339d98cfb347f Mon Sep 17 00:00:00 2001 From: Niko Achilles Kokkinos Date: Tue, 7 Feb 2023 20:04:43 +0200 Subject: [PATCH 18/56] refactor(docs): apply eslint rules to code snippets (#1259) * fix(docs): apply eslint rules to code snippets * fix(docs): remove overrides, introduce changes to code-snippets * fix(docs): code-snippet captureAWSAll (#1252) --- .eslintrc.js | 3 +- ...sable-run-linting-check-and-unit-tests.yml | 4 +- docs/snippets/logger/appendKeys.ts | 38 +++++++------- docs/snippets/logger/basicUsage.ts | 2 +- .../logger/bringYourOwnFormatterClass.ts | 4 +- .../logger/bringYourOwnFormatterHandler.ts | 26 +++++----- docs/snippets/logger/clearStateDecorator.ts | 32 ++++++------ docs/snippets/logger/clearStateMiddy.ts | 30 ++++++------ docs/snippets/logger/createChild.ts | 14 +++--- docs/snippets/logger/decorator.ts | 10 ++-- docs/snippets/logger/eventDecorator.ts | 10 ++-- docs/snippets/logger/eventMiddy.ts | 6 +-- docs/snippets/logger/extraData.ts | 48 +++++++++--------- docs/snippets/logger/logError.ts | 26 +++++----- docs/snippets/logger/logSampling.ts | 26 +++++----- docs/snippets/logger/manual.ts | 4 +- docs/snippets/logger/middy.ts | 6 +-- docs/snippets/logger/sam.ts | 1 + docs/snippets/logger/unitTesting.ts | 23 ++------- docs/snippets/metrics/addMetadata.ts | 8 +-- docs/snippets/metrics/basicUsage.ts | 4 +- .../captureColdStartMetricDecorator.ts | 8 +-- .../metrics/captureColdStartMetricMiddy.ts | 6 +-- docs/snippets/metrics/createMetrics.ts | 6 +-- docs/snippets/metrics/customDimensions.ts | 8 +-- docs/snippets/metrics/decorator.ts | 8 +-- docs/snippets/metrics/defaultDimensions.ts | 10 ++-- .../metrics/defaultDimensionsDecorator.ts | 10 ++-- .../metrics/defaultDimensionsMiddy.ts | 8 +-- docs/snippets/metrics/manual.ts | 6 +-- docs/snippets/metrics/middy.ts | 6 +-- docs/snippets/metrics/multiValueMetrics.ts | 9 ++-- docs/snippets/metrics/sam.ts | 3 +- docs/snippets/metrics/setDefaultDimensions.ts | 4 +- .../singleMetricDifferentDimsDecorator.ts | 20 ++++---- .../metrics/singleMetricDifferentDimsMiddy.ts | 18 +++---- docs/snippets/metrics/throwOnEmptyMetrics.ts | 6 +-- docs/snippets/package.json | 9 ++-- docs/snippets/tracer/accessRootTraceId.ts | 22 ++++----- docs/snippets/tracer/basicUsage.ts | 2 +- docs/snippets/tracer/captureAWS.ts | 2 +- docs/snippets/tracer/captureAWSAll.ts | 3 +- docs/snippets/tracer/captureAWSv3.ts | 4 +- docs/snippets/tracer/captureHTTP.ts | 5 +- .../snippets/tracer/captureMethodDecorator.ts | 18 +++---- docs/snippets/tracer/captureMethodManual.ts | 45 +++++++++-------- docs/snippets/tracer/decorator.ts | 10 ++-- .../tracer/disableCaptureResponseHandler.ts | 8 +-- .../tracer/disableCaptureResponseMethod.ts | 17 ++++--- .../tracer/disableCaptureResponseMiddy.ts | 10 ++-- docs/snippets/tracer/manual.ts | 49 +++++++++---------- docs/snippets/tracer/middy.ts | 8 +-- docs/snippets/tracer/putAnnotation.ts | 4 +- docs/snippets/tracer/putMetadata.ts | 8 +-- docs/snippets/tracer/sam.ts | 1 + package-lock.json | 31 ++++++------ 56 files changed, 357 insertions(+), 360 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b83ce24ffc..ef25880334 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,4 +1,5 @@ module.exports = { + root: true, env: { browser: false, es2020: true, @@ -63,5 +64,5 @@ module.exports = { 'prefer-arrow-callback': 'error', quotes: [ 'error', 'single', { allowTemplateLiterals: true } ], semi: [ 'error', 'always' ] - }, + } }; diff --git a/.github/workflows/reusable-run-linting-check-and-unit-tests.yml b/.github/workflows/reusable-run-linting-check-and-unit-tests.yml index 1ce91c800c..e779fd0737 100644 --- a/.github/workflows/reusable-run-linting-check-and-unit-tests.yml +++ b/.github/workflows/reusable-run-linting-check-and-unit-tests.yml @@ -42,9 +42,9 @@ jobs: if: steps.cache-node-modules.outputs.cache-hit == 'true' run: | npm run build -w packages/commons - npm run build -w packages/logger & npm run build -w packages/tracer & npm run build -w packages/metrics & npm run build -w packages/parameters & npm run build -w packages/idempotency + npm run build -w packages/logger & npm run build -w packages/tracer & npm run build -w packages/metrics & npm run build -w packages/parameters & npm run build -w packages/idempotency & npm run build -w docs/snippets - name: Run linting - run: npm run lint -w packages/commons -w packages/logger -w packages/tracer -w packages/metrics -w packages/parameters -w packages/idempotency + run: npm run lint -w packages/commons -w packages/logger -w packages/tracer -w packages/metrics -w packages/parameters -w packages/idempotency -w docs/snippets - name: Run unit tests run: npm t -w packages/commons -w packages/logger -w packages/tracer -w packages/metrics -w packages/parameters -w packages/idempotency check-examples: diff --git a/docs/snippets/logger/appendKeys.ts b/docs/snippets/logger/appendKeys.ts index 3c0d92cb40..d8d94541b6 100644 --- a/docs/snippets/logger/appendKeys.ts +++ b/docs/snippets/logger/appendKeys.ts @@ -2,15 +2,15 @@ import { Logger } from '@aws-lambda-powertools/logger'; // Add persistent log keys via the constructor const logger = new Logger({ - persistentLogAttributes: { - aws_account_id: '123456789012', - aws_region: 'eu-west-1', - logger: { - name: '@aws-lambda-powertools/logger', - version: '0.0.1', - }, - extra_key: "some-value" - } + persistentLogAttributes: { + aws_account_id: '123456789012', + aws_region: 'eu-west-1', + logger: { + name: '@aws-lambda-powertools/logger', + version: '0.0.1', + }, + extra_key: 'some-value' + } }); // OR add persistent log keys to an existing Logger instance with the appendKeys method: @@ -24,18 +24,18 @@ const logger = new Logger({ // extra_key: "some-value" // }); -export const handler = async (_event: any, _context: any): Promise => { +export const handler = async (_event: unknown, _context: unknown): Promise => { - // If you don't want to log the "extra_key" attribute in your logs, you can remove it - logger.removeKeys(["extra_key"]) + // If you don't want to log the "extra_key" attribute in your logs, you can remove it + logger.removeKeys(['extra_key']); - // This info log will print all extra custom attributes added above - // Extra attributes: logger object with name and version of the logger library, awsAccountId, awsRegion - logger.info('This is an INFO log'); - logger.info('This is another INFO log'); + // This info log will print all extra custom attributes added above + // Extra attributes: logger object with name and version of the logger library, awsAccountId, awsRegion + logger.info('This is an INFO log'); + logger.info('This is another INFO log'); - return { - foo: 'bar' - }; + return { + foo: 'bar' + }; }; \ No newline at end of file diff --git a/docs/snippets/logger/basicUsage.ts b/docs/snippets/logger/basicUsage.ts index 07d02405ae..8fbda33daf 100644 --- a/docs/snippets/logger/basicUsage.ts +++ b/docs/snippets/logger/basicUsage.ts @@ -3,5 +3,5 @@ import { Logger } from '@aws-lambda-powertools/logger'; const logger = new Logger({ serviceName: 'serverlessAirline' }); export const handler = async (_event, _context): Promise => { - // ... + logger.info('Hello World'); }; \ No newline at end of file diff --git a/docs/snippets/logger/bringYourOwnFormatterClass.ts b/docs/snippets/logger/bringYourOwnFormatterClass.ts index b294ee0dc5..ab7e207cb9 100644 --- a/docs/snippets/logger/bringYourOwnFormatterClass.ts +++ b/docs/snippets/logger/bringYourOwnFormatterClass.ts @@ -1,8 +1,8 @@ -import { LogFormatter } from "@aws-lambda-powertools/logger"; +import { LogFormatter } from '@aws-lambda-powertools/logger'; import { LogAttributes, UnformattedAttributes, -} from "@aws-lambda-powertools/logger/lib/types"; +} from '@aws-lambda-powertools/logger/lib/types'; // Replace this line with your own type type MyCompanyLog = LogAttributes; diff --git a/docs/snippets/logger/bringYourOwnFormatterHandler.ts b/docs/snippets/logger/bringYourOwnFormatterHandler.ts index 0121e1bc64..b565708f9d 100644 --- a/docs/snippets/logger/bringYourOwnFormatterHandler.ts +++ b/docs/snippets/logger/bringYourOwnFormatterHandler.ts @@ -2,23 +2,23 @@ import { Logger } from '@aws-lambda-powertools/logger'; import { MyCompanyLogFormatter } from './utils/formatters/MyCompanyLogFormatter'; const logger = new Logger({ - logFormatter: new MyCompanyLogFormatter(), - logLevel: 'DEBUG', - serviceName: 'serverlessAirline', - sampleRateValue: 0.5, - persistentLogAttributes: { - awsAccountId: process.env.AWS_ACCOUNT_ID, - logger: { - name: '@aws-lambda-powertools/logger', - version: '0.0.1' - } - }, + logFormatter: new MyCompanyLogFormatter(), + logLevel: 'DEBUG', + serviceName: 'serverlessAirline', + sampleRateValue: 0.5, + persistentLogAttributes: { + awsAccountId: process.env.AWS_ACCOUNT_ID, + logger: { + name: '@aws-lambda-powertools/logger', + version: '0.0.1' + } + }, }); export const handler = async (event, context): Promise => { - logger.addContext(context); + logger.addContext(context); - logger.info('This is an INFO log', { correlationIds: { myCustomCorrelationId: 'foo-bar-baz' } }); + logger.info('This is an INFO log', { correlationIds: { myCustomCorrelationId: 'foo-bar-baz' } }); }; \ No newline at end of file diff --git a/docs/snippets/logger/clearStateDecorator.ts b/docs/snippets/logger/clearStateDecorator.ts index 8251b99fcf..359b63cc29 100644 --- a/docs/snippets/logger/clearStateDecorator.ts +++ b/docs/snippets/logger/clearStateDecorator.ts @@ -4,26 +4,26 @@ import { LambdaInterface } from '@aws-lambda-powertools/commons'; // Persistent attributes added outside the handler will be // cached across invocations const logger = new Logger({ - logLevel: 'DEBUG', - persistentLogAttributes: { - foo: "bar", - biz: "baz" - } + logLevel: 'DEBUG', + persistentLogAttributes: { + foo: 'bar', + biz: 'baz' + } }); class Lambda implements LambdaInterface { - // Enable the clear state flag - @logger.injectLambdaContext({ clearState: true }) - public async handler(_event: any, _context: any): Promise { - // Persistent attributes added inside the handler will NOT be cached - // across invocations - if (event['special_key'] === '123456'){ - logger.appendKeys({ - details: { special_key: '123456' } - }); - } - logger.debug('This is a DEBUG log'); + // Enable the clear state flag + @logger.injectLambdaContext({ clearState: true }) + public async handler(event: unknown, _context: unknown): Promise { + // Persistent attributes added inside the handler will NOT be cached + // across invocations + if (event['special_key'] === '123456'){ + logger.appendKeys({ + details: { special_key: '123456' } + }); } + logger.debug('This is a DEBUG log'); + } } diff --git a/docs/snippets/logger/clearStateMiddy.ts b/docs/snippets/logger/clearStateMiddy.ts index 2c68403d22..f9e1f55ff7 100644 --- a/docs/snippets/logger/clearStateMiddy.ts +++ b/docs/snippets/logger/clearStateMiddy.ts @@ -4,24 +4,24 @@ import middy from '@middy/core'; // Persistent attributes added outside the handler will be // cached across invocations const logger = new Logger({ - logLevel: 'DEBUG', - persistentLogAttributes: { - foo: "bar", - biz: "baz" - } + logLevel: 'DEBUG', + persistentLogAttributes: { + foo: 'bar', + biz: 'baz' + } }); -const lambdaHandler = async (event: { special_key: string }, _context: any): Promise => { - // Persistent attributes added inside the handler will NOT be cached - // across invocations - if (event['special_key'] === '123456') { - logger.appendKeys({ - details: { special_key: event['special_key'] } - }); - } - logger.debug('This is a DEBUG log'); +const lambdaHandler = async (event: { special_key: string }, _context: unknown): Promise => { + // Persistent attributes added inside the handler will NOT be cached + // across invocations + if (event['special_key'] === '123456') { + logger.appendKeys({ + details: { special_key: event['special_key'] } + }); + } + logger.debug('This is a DEBUG log'); }; // Enable the clear state flag export const handler = middy(lambdaHandler) - .use(injectLambdaContext(logger, { clearState: true })); \ No newline at end of file + .use(injectLambdaContext(logger, { clearState: true })); \ No newline at end of file diff --git a/docs/snippets/logger/createChild.ts b/docs/snippets/logger/createChild.ts index db18c5b9a3..7b9e7780e3 100644 --- a/docs/snippets/logger/createChild.ts +++ b/docs/snippets/logger/createChild.ts @@ -2,20 +2,20 @@ import { Logger } from '@aws-lambda-powertools/logger'; // With this logger, all the INFO logs will be printed const logger = new Logger({ - logLevel: 'INFO' + logLevel: 'INFO' }); // With this logger, only the ERROR logs will be printed const childLogger = logger.createChild({ - logLevel: 'ERROR' + logLevel: 'ERROR' }); -export const handler = async (_event: any, _context: any): Promise => { +export const handler = async (_event: unknown, _context: unknown): Promise => { - logger.info('This is an INFO log, from the parent logger'); - logger.error('This is an ERROR log, from the parent logger'); + logger.info('This is an INFO log, from the parent logger'); + logger.error('This is an ERROR log, from the parent logger'); - childLogger.info('This is an INFO log, from the child logger'); - childLogger.error('This is an ERROR log, from the child logger'); + childLogger.info('This is an INFO log, from the child logger'); + childLogger.error('This is an ERROR log, from the child logger'); }; \ No newline at end of file diff --git a/docs/snippets/logger/decorator.ts b/docs/snippets/logger/decorator.ts index 92122a691b..0e725bd517 100644 --- a/docs/snippets/logger/decorator.ts +++ b/docs/snippets/logger/decorator.ts @@ -4,11 +4,11 @@ import { LambdaInterface } from '@aws-lambda-powertools/commons'; const logger = new Logger(); class Lambda implements LambdaInterface { - // Decorate your handler class method - @logger.injectLambdaContext() - public async handler(_event: any, _context: any): Promise { - logger.info('This is an INFO log with some context'); - } + // Decorate your handler class method + @logger.injectLambdaContext() + public async handler(_event: unknown, _context: unknown): Promise { + logger.info('This is an INFO log with some context'); + } } diff --git a/docs/snippets/logger/eventDecorator.ts b/docs/snippets/logger/eventDecorator.ts index 57d8ce927c..d99668a04b 100644 --- a/docs/snippets/logger/eventDecorator.ts +++ b/docs/snippets/logger/eventDecorator.ts @@ -4,11 +4,11 @@ import { LambdaInterface } from '@aws-lambda-powertools/commons'; const logger = new Logger(); class Lambda implements LambdaInterface { - // Set the log event flag to true - @logger.injectLambdaContext({ logEvent: true }) - public async handler(_event: any, _context: any): Promise { - logger.info('This is an INFO log with some context'); - } + // Set the log event flag to true + @logger.injectLambdaContext({ logEvent: true }) + public async handler(_event: unknown, _context: unknown): Promise { + logger.info('This is an INFO log with some context'); + } } diff --git a/docs/snippets/logger/eventMiddy.ts b/docs/snippets/logger/eventMiddy.ts index 34c68207fb..4118903d33 100644 --- a/docs/snippets/logger/eventMiddy.ts +++ b/docs/snippets/logger/eventMiddy.ts @@ -3,9 +3,9 @@ import middy from '@middy/core'; const logger = new Logger(); -const lambdaHandler = async (_event: any, _context: any): Promise => { - logger.info('This is an INFO log with some context'); +const lambdaHandler = async (_event: unknown, _context: unknown): Promise => { + logger.info('This is an INFO log with some context'); }; export const handler = middy(lambdaHandler) - .use(injectLambdaContext(logger, { logEvent: true })); \ No newline at end of file + .use(injectLambdaContext(logger, { logEvent: true })); \ No newline at end of file diff --git a/docs/snippets/logger/extraData.ts b/docs/snippets/logger/extraData.ts index fd315e3f2e..5a536307d8 100644 --- a/docs/snippets/logger/extraData.ts +++ b/docs/snippets/logger/extraData.ts @@ -2,37 +2,37 @@ import { Logger } from '@aws-lambda-powertools/logger'; const logger = new Logger(); -export const handler = async (event: any, _context: any): Promise => { +export const handler = async (event: unknown, _context: unknown): Promise => { - const myImportantVariable = { - foo: 'bar' - }; + const myImportantVariable = { + foo: 'bar' + }; - // Log additional data in single log items + // Log additional data in single log items - // As second parameter - logger.info('This is a log with an extra variable', { data: myImportantVariable }); + // As second parameter + logger.info('This is a log with an extra variable', { data: myImportantVariable }); - // You can also pass multiple parameters containing arbitrary objects - logger.info('This is a log with 3 extra objects', - { data: myImportantVariable }, - { correlationIds: { myCustomCorrelationId: 'foo-bar-baz' } }, - { lambdaEvent: event } - ); + // You can also pass multiple parameters containing arbitrary objects + logger.info('This is a log with 3 extra objects', + { data: myImportantVariable }, + { correlationIds: { myCustomCorrelationId: 'foo-bar-baz' } }, + { lambdaEvent: event } + ); - // Simply pass a string for logging additional data - logger.info('This is a log with additional string value', 'string value'); + // Simply pass a string for logging additional data + logger.info('This is a log with additional string value', 'string value'); - // Directly passing an object containing both the message and the additional info - const logObject = { - message: 'This is a log message', - additionalValue: 42 - }; + // Directly passing an object containing both the message and the additional info + const logObject = { + message: 'This is a log message', + additionalValue: 42 + }; - logger.info(logObject); + logger.info(logObject); - return { - foo: 'bar' - }; + return { + foo: 'bar' + }; }; \ No newline at end of file diff --git a/docs/snippets/logger/logError.ts b/docs/snippets/logger/logError.ts index c41c4a58a5..230fb7cbe7 100644 --- a/docs/snippets/logger/logError.ts +++ b/docs/snippets/logger/logError.ts @@ -2,20 +2,20 @@ import { Logger } from '@aws-lambda-powertools/logger'; const logger = new Logger(); -export const handler = async (_event: any, _context: any): Promise => { +export const handler = async (_event: unknown, _context: unknown): Promise => { - try { - throw new Error('Unexpected error #1'); - } catch (error) { - // Log information about the error using the default "error" key - logger.error('This is the first error', error as Error); - } + try { + throw new Error('Unexpected error #1'); + } catch (error) { + // Log information about the error using the default "error" key + logger.error('This is the first error', error as Error); + } - try { - throw new Error('Unexpected error #2'); - } catch (error) { - // Log information about the error using a custom "myCustomErrorKey" key - logger.error('This is the second error', { myCustomErrorKey: error as Error } ); - } + try { + throw new Error('Unexpected error #2'); + } catch (error) { + // Log information about the error using a custom "myCustomErrorKey" key + logger.error('This is the second error', { myCustomErrorKey: error as Error } ); + } }; \ No newline at end of file diff --git a/docs/snippets/logger/logSampling.ts b/docs/snippets/logger/logSampling.ts index eea4af5f07..4b566f33ec 100644 --- a/docs/snippets/logger/logSampling.ts +++ b/docs/snippets/logger/logSampling.ts @@ -2,23 +2,23 @@ import { Logger } from '@aws-lambda-powertools/logger'; // Notice the log level set to 'ERROR' const logger = new Logger({ - logLevel: 'ERROR', - sampleRateValue: 0.5 + logLevel: 'ERROR', + sampleRateValue: 0.5 }); -export const handler = async (_event: any, _context: any): Promise => { +export const handler = async (_event: unknown, _context: unknown): Promise => { - // This log item (equal to log level 'ERROR') will be printed to standard output - // in all Lambda invocations - logger.error('This is an ERROR log'); + // This log item (equal to log level 'ERROR') will be printed to standard output + // in all Lambda invocations + logger.error('This is an ERROR log'); - // These log items (below the log level 'ERROR') have ~50% chance - // of being printed in a Lambda invocation - logger.debug('This is a DEBUG log that has 50% chance of being printed'); - logger.info('This is an INFO log that has 50% chance of being printed'); - logger.warn('This is a WARN log that has 50% chance of being printed'); + // These log items (below the log level 'ERROR') have ~50% chance + // of being printed in a Lambda invocation + logger.debug('This is a DEBUG log that has 50% chance of being printed'); + logger.info('This is an INFO log that has 50% chance of being printed'); + logger.warn('This is a WARN log that has 50% chance of being printed'); - // Optional: refresh sample rate calculation on runtime - // logger.refreshSampleRateCalculation(); + // Optional: refresh sample rate calculation on runtime + // logger.refreshSampleRateCalculation(); }; \ No newline at end of file diff --git a/docs/snippets/logger/manual.ts b/docs/snippets/logger/manual.ts index e8544919b9..b7c271be35 100644 --- a/docs/snippets/logger/manual.ts +++ b/docs/snippets/logger/manual.ts @@ -4,8 +4,8 @@ const logger = new Logger(); export const handler = async (_event, context): Promise => { - logger.addContext(context); + logger.addContext(context); - logger.info('This is an INFO log with some context'); + logger.info('This is an INFO log with some context'); }; \ No newline at end of file diff --git a/docs/snippets/logger/middy.ts b/docs/snippets/logger/middy.ts index 5d48839133..83b723bf7f 100644 --- a/docs/snippets/logger/middy.ts +++ b/docs/snippets/logger/middy.ts @@ -3,9 +3,9 @@ import middy from '@middy/core'; const logger = new Logger(); -const lambdaHandler = async (_event: any, _context: any): Promise => { - logger.info('This is an INFO log with some context'); +const lambdaHandler = async (_event: unknown, _context: unknown): Promise => { + logger.info('This is an INFO log with some context'); }; export const handler = middy(lambdaHandler) - .use(injectLambdaContext(logger)); \ No newline at end of file + .use(injectLambdaContext(logger)); \ No newline at end of file diff --git a/docs/snippets/logger/sam.ts b/docs/snippets/logger/sam.ts index 0970b4e125..12cc9cc7eb 100644 --- a/docs/snippets/logger/sam.ts +++ b/docs/snippets/logger/sam.ts @@ -2,6 +2,7 @@ import { Logger } from '@aws-lambda-powertools/logger'; // Logger parameters fetched from the environment variables (see template.yaml tab) const logger = new Logger(); +logger.info('Hello World'); // You can also pass the parameters in the constructor // const logger = new Logger({ diff --git a/docs/snippets/logger/unitTesting.ts b/docs/snippets/logger/unitTesting.ts index b47352ab20..4c09ab7285 100644 --- a/docs/snippets/logger/unitTesting.ts +++ b/docs/snippets/logger/unitTesting.ts @@ -1,25 +1,12 @@ -const dummyContext = { - callbackWaitsForEmptyEventLoop: true, - functionVersion: '$LATEST', - functionName: 'foo-bar-function', - memoryLimitInMB: '128', - logGroupName: '/aws/lambda/foo-bar-function', - logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', - getRemainingTimeInMillis: () => 1234, - done: () => console.log('Done!'), - fail: () => console.log('Failed!'), - succeed: () => console.log('Succeeded!'), -}; +import { ContextExamples as dummyContext } from '@aws-lambda-powertools/commons'; describe('MyUnitTest', () => { - test('Lambda invoked successfully', async () => { + test('Lambda invoked successfully', async () => { - const testEvent = { test: 'test' }; - await handler(testEvent, dummyContext); + const testEvent = { test: 'test' }; + await handler(testEvent, dummyContext); - }); + }); }); \ No newline at end of file diff --git a/docs/snippets/metrics/addMetadata.ts b/docs/snippets/metrics/addMetadata.ts index ce56cac80d..adbe592b90 100644 --- a/docs/snippets/metrics/addMetadata.ts +++ b/docs/snippets/metrics/addMetadata.ts @@ -3,10 +3,10 @@ import middy from '@middy/core'; const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); -const lambdaHandler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - metrics.addMetadata('bookingId', '7051cd10-6283-11ec-90d6-0242ac120003'); +const lambdaHandler = async (_event: unknown, _context: unknown): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetadata('bookingId', '7051cd10-6283-11ec-90d6-0242ac120003'); }; export const handler = middy(lambdaHandler) - .use(logMetrics(metrics)); \ No newline at end of file + .use(logMetrics(metrics)); \ No newline at end of file diff --git a/docs/snippets/metrics/basicUsage.ts b/docs/snippets/metrics/basicUsage.ts index ea4d5a53ec..15388d2c82 100644 --- a/docs/snippets/metrics/basicUsage.ts +++ b/docs/snippets/metrics/basicUsage.ts @@ -1,7 +1,7 @@ -import { Metrics } from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); export const handler = async (_event, _context): Promise => { - // ... + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); }; \ No newline at end of file diff --git a/docs/snippets/metrics/captureColdStartMetricDecorator.ts b/docs/snippets/metrics/captureColdStartMetricDecorator.ts index 3161b074cb..8817d6a799 100644 --- a/docs/snippets/metrics/captureColdStartMetricDecorator.ts +++ b/docs/snippets/metrics/captureColdStartMetricDecorator.ts @@ -5,8 +5,8 @@ const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orde export class MyFunction implements LambdaInterface { - @metrics.logMetrics({ captureColdStartMetric: true }) - public async handler(_event: any, _context: any): Promise { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - } + @metrics.logMetrics({ captureColdStartMetric: true }) + public async handler(_event: unknown, _context: unknown): Promise { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + } } \ No newline at end of file diff --git a/docs/snippets/metrics/captureColdStartMetricMiddy.ts b/docs/snippets/metrics/captureColdStartMetricMiddy.ts index 092e06b50b..3b84cb7194 100644 --- a/docs/snippets/metrics/captureColdStartMetricMiddy.ts +++ b/docs/snippets/metrics/captureColdStartMetricMiddy.ts @@ -3,9 +3,9 @@ import middy from '@middy/core'; const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); -const lambdaHandler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +const lambdaHandler = async (_event: unknown, _context: unknown): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); }; export const handler = middy(lambdaHandler) - .use(logMetrics(metrics, { captureColdStartMetric: true })); \ No newline at end of file + .use(logMetrics(metrics, { captureColdStartMetric: true })); \ No newline at end of file diff --git a/docs/snippets/metrics/createMetrics.ts b/docs/snippets/metrics/createMetrics.ts index 072966690a..640afe1ada 100644 --- a/docs/snippets/metrics/createMetrics.ts +++ b/docs/snippets/metrics/createMetrics.ts @@ -2,7 +2,7 @@ import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); -export const handler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - metrics.publishStoredMetrics(); +export const handler = async (_event: unknown, _context: unknown): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.publishStoredMetrics(); }; \ No newline at end of file diff --git a/docs/snippets/metrics/customDimensions.ts b/docs/snippets/metrics/customDimensions.ts index 8c4dd883d7..95d25f9cab 100644 --- a/docs/snippets/metrics/customDimensions.ts +++ b/docs/snippets/metrics/customDimensions.ts @@ -2,8 +2,8 @@ import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); -export const handler = async (_event: any, _context: any): Promise => { - metrics.addDimension('environment', 'prod'); - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - metrics.publishStoredMetrics(); +export const handler = async (_event: unknown, _context: unknown): Promise => { + metrics.addDimension('environment', 'prod'); + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.publishStoredMetrics(); }; \ No newline at end of file diff --git a/docs/snippets/metrics/decorator.ts b/docs/snippets/metrics/decorator.ts index a3430414bc..f64d842d5e 100644 --- a/docs/snippets/metrics/decorator.ts +++ b/docs/snippets/metrics/decorator.ts @@ -5,10 +5,10 @@ const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orde class Lambda implements LambdaInterface { - @metrics.logMetrics() - public async handler(_event: any, _context: any): Promise { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - } + @metrics.logMetrics() + public async handler(_event: unknown, _context: unknown): Promise { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + } } const handlerClass = new Lambda(); diff --git a/docs/snippets/metrics/defaultDimensions.ts b/docs/snippets/metrics/defaultDimensions.ts index 3d48bd6bb6..509bdda170 100644 --- a/docs/snippets/metrics/defaultDimensions.ts +++ b/docs/snippets/metrics/defaultDimensions.ts @@ -1,11 +1,11 @@ import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ - namespace: 'serverlessAirline', - serviceName: 'orders', - defaultDimensions: { 'environment': 'prod', 'foo': 'bar' } + namespace: 'serverlessAirline', + serviceName: 'orders', + defaultDimensions: { 'environment': 'prod', 'foo': 'bar' } }); -export const handler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +export const handler = async (_event: unknown, _context: unknown): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); }; \ No newline at end of file diff --git a/docs/snippets/metrics/defaultDimensionsDecorator.ts b/docs/snippets/metrics/defaultDimensionsDecorator.ts index ed2631e321..d31c8cde6b 100644 --- a/docs/snippets/metrics/defaultDimensionsDecorator.ts +++ b/docs/snippets/metrics/defaultDimensionsDecorator.ts @@ -5,11 +5,11 @@ const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orde const DEFAULT_DIMENSIONS = { 'environment': 'prod', 'foo': 'bar' }; export class Lambda implements LambdaInterface { - // Decorate your handler class method - @metrics.logMetrics({ defaultDimensions: DEFAULT_DIMENSIONS }) - public async handler(_event: any, _context: any): Promise { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - } + // Decorate your handler class method + @metrics.logMetrics({ defaultDimensions: DEFAULT_DIMENSIONS }) + public async handler(_event: unknown, _context: unknown): Promise { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + } } const handlerClass = new Lambda(); diff --git a/docs/snippets/metrics/defaultDimensionsMiddy.ts b/docs/snippets/metrics/defaultDimensionsMiddy.ts index aedfc554e3..dc9035e937 100644 --- a/docs/snippets/metrics/defaultDimensionsMiddy.ts +++ b/docs/snippets/metrics/defaultDimensionsMiddy.ts @@ -3,11 +3,11 @@ import middy from '@middy/core'; const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); -const lambdaHandler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +const lambdaHandler = async (_event: unknown, _context: unknown): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); }; // Wrap the handler with middy export const handler = middy(lambdaHandler) - // Use the middleware by passing the Metrics instance as a parameter - .use(logMetrics(metrics, { defaultDimensions:{ 'environment': 'prod', 'foo': 'bar' } })); \ No newline at end of file +// Use the middleware by passing the Metrics instance as a parameter + .use(logMetrics(metrics, { defaultDimensions:{ 'environment': 'prod', 'foo': 'bar' } })); \ No newline at end of file diff --git a/docs/snippets/metrics/manual.ts b/docs/snippets/metrics/manual.ts index 00e552a294..7df2301fdd 100644 --- a/docs/snippets/metrics/manual.ts +++ b/docs/snippets/metrics/manual.ts @@ -2,7 +2,7 @@ import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); -export const handler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 10); - metrics.publishStoredMetrics(); +export const handler = async (_event: unknown, _context: unknown): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 10); + metrics.publishStoredMetrics(); }; \ No newline at end of file diff --git a/docs/snippets/metrics/middy.ts b/docs/snippets/metrics/middy.ts index 6af8f3827b..088186002b 100644 --- a/docs/snippets/metrics/middy.ts +++ b/docs/snippets/metrics/middy.ts @@ -3,9 +3,9 @@ import middy from '@middy/core'; const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); -const lambdaHandler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +const lambdaHandler = async (_event: unknown, _context: unknown): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); }; export const handler = middy(lambdaHandler) - .use(logMetrics(metrics)); \ No newline at end of file + .use(logMetrics(metrics)); \ No newline at end of file diff --git a/docs/snippets/metrics/multiValueMetrics.ts b/docs/snippets/metrics/multiValueMetrics.ts index aea850b62e..6214940c39 100644 --- a/docs/snippets/metrics/multiValueMetrics.ts +++ b/docs/snippets/metrics/multiValueMetrics.ts @@ -1,10 +1,9 @@ import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; -import { Context } from 'aws-lambda'; const metrics = new Metrics({ namespace:'serverlessAirline', serviceName:'orders' }); -export const handler = async (event: any, context: Context): Promise => { - metrics.addMetric('performedActionA', MetricUnits.Count, 2); - // do something else... - metrics.addMetric('performedActionA', MetricUnits.Count, 1); +export const handler = async (_event: unknown, _context: unknown): Promise => { + metrics.addMetric('performedActionA', MetricUnits.Count, 2); + // do something else... + metrics.addMetric('performedActionA', MetricUnits.Count, 1); }; \ No newline at end of file diff --git a/docs/snippets/metrics/sam.ts b/docs/snippets/metrics/sam.ts index 5a2c910529..8acd0b6ba5 100644 --- a/docs/snippets/metrics/sam.ts +++ b/docs/snippets/metrics/sam.ts @@ -1,7 +1,8 @@ -import { Metrics } from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; // Metrics parameters fetched from the environment variables (see template.yaml tab) const metrics = new Metrics(); +metrics.addMetric('successfulBooking', MetricUnits.Count, 1); // You can also pass the parameters in the constructor // const metrics = new Metrics({ diff --git a/docs/snippets/metrics/setDefaultDimensions.ts b/docs/snippets/metrics/setDefaultDimensions.ts index c6a5eb7d97..1491d709ae 100644 --- a/docs/snippets/metrics/setDefaultDimensions.ts +++ b/docs/snippets/metrics/setDefaultDimensions.ts @@ -3,6 +3,6 @@ import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); metrics.setDefaultDimensions({ 'environment': 'prod', 'foo': 'bar' }); -export const handler = async (event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +export const handler = async (_event: unknown, _context: unknown): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); }; \ No newline at end of file diff --git a/docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts b/docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts index 84d1dde551..758a1aba2c 100644 --- a/docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts +++ b/docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts @@ -5,17 +5,17 @@ const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orde class Lambda implements LambdaInterface { - @metrics.logMetrics() - public async handler(_event: any, _context: any): Promise { - metrics.addDimension('metricUnit', 'milliseconds'); - // This metric will have the "metricUnit" dimension, and no "metricType" dimension: - metrics.addMetric('latency', MetricUnits.Milliseconds, 56); + @metrics.logMetrics() + public async handler(_event: unknown, _context: unknown): Promise { + metrics.addDimension('metricUnit', 'milliseconds'); + // This metric will have the "metricUnit" dimension, and no "metricType" dimension: + metrics.addMetric('latency', MetricUnits.Milliseconds, 56); - const singleMetric = metrics.singleMetric(); - // This metric will have the "metricType" dimension, and no "metricUnit" dimension: - singleMetric.addDimension('metricType', 'business'); - singleMetric.addMetric('orderSubmitted', MetricUnits.Count, 1); - } + const singleMetric = metrics.singleMetric(); + // This metric will have the "metricType" dimension, and no "metricUnit" dimension: + singleMetric.addDimension('metricType', 'business'); + singleMetric.addMetric('orderSubmitted', MetricUnits.Count, 1); + } } const handlerClass = new Lambda(); diff --git a/docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts b/docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts index c15f5f717f..62ed3863b7 100644 --- a/docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts +++ b/docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts @@ -3,16 +3,16 @@ import middy from '@middy/core'; const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); -const lambdaHandler = async (_event: any, _context: any): Promise => { - metrics.addDimension('metricUnit', 'milliseconds'); - // This metric will have the "metricUnit" dimension, and no "metricType" dimension: - metrics.addMetric('latency', MetricUnits.Milliseconds, 56); +const lambdaHandler = async (_event: unknown, _context: unknown): Promise => { + metrics.addDimension('metricUnit', 'milliseconds'); + // This metric will have the "metricUnit" dimension, and no "metricType" dimension: + metrics.addMetric('latency', MetricUnits.Milliseconds, 56); - const singleMetric = metrics.singleMetric(); - // This metric will have the "metricType" dimension, and no "metricUnit" dimension: - singleMetric.addDimension('metricType', 'business'); - singleMetric.addMetric('orderSubmitted', MetricUnits.Count, 1); + const singleMetric = metrics.singleMetric(); + // This metric will have the "metricType" dimension, and no "metricUnit" dimension: + singleMetric.addDimension('metricType', 'business'); + singleMetric.addMetric('orderSubmitted', MetricUnits.Count, 1); }; export const handler = middy(lambdaHandler) - .use(logMetrics(metrics)); \ No newline at end of file + .use(logMetrics(metrics)); \ No newline at end of file diff --git a/docs/snippets/metrics/throwOnEmptyMetrics.ts b/docs/snippets/metrics/throwOnEmptyMetrics.ts index 2b3f8bb4a4..3b9784f7b1 100644 --- a/docs/snippets/metrics/throwOnEmptyMetrics.ts +++ b/docs/snippets/metrics/throwOnEmptyMetrics.ts @@ -3,9 +3,9 @@ import middy from '@middy/core'; const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); -const lambdaHandler = async (_event: any, _context: any): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +const lambdaHandler = async (_event: unknown, _context: unknown): Promise => { + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); }; export const handler = middy(lambdaHandler) - .use(logMetrics(metrics, { throwOnEmptyMetrics: true })); \ No newline at end of file + .use(logMetrics(metrics, { throwOnEmptyMetrics: true })); \ No newline at end of file diff --git a/docs/snippets/package.json b/docs/snippets/package.json index 33aeb1247c..48cb3fa3da 100644 --- a/docs/snippets/package.json +++ b/docs/snippets/package.json @@ -10,8 +10,8 @@ "test": "echo 'Not Applicable'", "test:e2e": "echo 'Not Applicable'", "build": "echo 'Not Applicable'", - "lint": "echo 'Not Applicable'", - "lint-fix": "echo 'Not Applicable'" + "lint": "eslint --ext .ts --no-error-on-unmatched-pattern logger tracer metrics parameters", + "lint-fix": "eslint --fix --ext .ts --no-error-on-unmatched-pattern logger tracer metrics parameters" }, "license": "MIT-0", "repository": { @@ -27,6 +27,7 @@ "@aws-sdk/client-dynamodb": "^3.245.0", "@aws-sdk/client-secrets-manager": "^3.250.0", "@aws-sdk/client-ssm": "^3.245.0", - "@aws-sdk/util-dynamodb": "^3.245.0" + "@aws-sdk/util-dynamodb": "^3.245.0", + "axios": "^1.2.4" } -} \ No newline at end of file +} diff --git a/docs/snippets/tracer/accessRootTraceId.ts b/docs/snippets/tracer/accessRootTraceId.ts index c385e39b7c..f40b797346 100644 --- a/docs/snippets/tracer/accessRootTraceId.ts +++ b/docs/snippets/tracer/accessRootTraceId.ts @@ -2,17 +2,17 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); -export const handler = async (event: unknown, context: Context): Promise => { - try { +export const handler = async (_event: unknown, _context: unknown): Promise => { + try { - } catch (err) { - const rootTraceId = tracer.getRootXrayTraceId(); + } catch (err) { + const rootTraceId = tracer.getRootXrayTraceId(); - // Example of returning an error response - return { - statusCode: 500, - body: `Internal Error - Please contact support and quote the following id: ${rootTraceId}`, - headers: { '_X_AMZN_TRACE_ID': rootTraceId }, - }; - } + // Example of returning an error response + return { + statusCode: 500, + body: `Internal Error - Please contact support and quote the following id: ${rootTraceId}`, + headers: { '_X_AMZN_TRACE_ID': rootTraceId }, + }; + } }; \ No newline at end of file diff --git a/docs/snippets/tracer/basicUsage.ts b/docs/snippets/tracer/basicUsage.ts index a8c830e78b..b44814eb41 100644 --- a/docs/snippets/tracer/basicUsage.ts +++ b/docs/snippets/tracer/basicUsage.ts @@ -3,5 +3,5 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); export const handler = async (_event, _context): Promise => { - // ... + tracer.getSegment(); }; \ No newline at end of file diff --git a/docs/snippets/tracer/captureAWS.ts b/docs/snippets/tracer/captureAWS.ts index b94151c014..5155f569b9 100644 --- a/docs/snippets/tracer/captureAWS.ts +++ b/docs/snippets/tracer/captureAWS.ts @@ -2,4 +2,4 @@ import { S3 } from 'aws-sdk'; import { Tracer } from '@aws-lambda-powertools/tracer'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); -const s3 = tracer.captureAWSClient(new S3()); \ No newline at end of file +tracer.captureAWSClient(new S3()); \ No newline at end of file diff --git a/docs/snippets/tracer/captureAWSAll.ts b/docs/snippets/tracer/captureAWSAll.ts index d758e1dd7c..a5aca36989 100644 --- a/docs/snippets/tracer/captureAWSAll.ts +++ b/docs/snippets/tracer/captureAWSAll.ts @@ -1,4 +1,5 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; +import AWS from 'aws-sdk'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); -const AWS = tracer.captureAWS(require('aws-sdk')); \ No newline at end of file +tracer.captureAWS(AWS); \ No newline at end of file diff --git a/docs/snippets/tracer/captureAWSv3.ts b/docs/snippets/tracer/captureAWSv3.ts index 8270d10c0a..dd1ca6870a 100644 --- a/docs/snippets/tracer/captureAWSv3.ts +++ b/docs/snippets/tracer/captureAWSv3.ts @@ -1,5 +1,5 @@ -import { S3Client } from '@aws-sdk/client-s3'; +import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager'; import { Tracer } from '@aws-lambda-powertools/tracer'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); -const client = tracer.captureAWSv3Client(new S3Client({})); \ No newline at end of file +tracer.captureAWSv3Client(new SecretsManagerClient({})); \ No newline at end of file diff --git a/docs/snippets/tracer/captureHTTP.ts b/docs/snippets/tracer/captureHTTP.ts index cf4cae3cdb..15ca0adef4 100644 --- a/docs/snippets/tracer/captureHTTP.ts +++ b/docs/snippets/tracer/captureHTTP.ts @@ -3,6 +3,7 @@ import axios from 'axios'; // (1) const tracer = new Tracer({ serviceName: 'serverlessAirline' }); -export const handler = async (event: unknown, context: Context): Promise => { - await axios.get('https://httpbin.org/status/200'); +export const handler = async (_event: unknown, _context: unknown): Promise => { + const { data } = await axios.get('https://httpbin.org/status/200'); + tracer.addResponseAsMetadata(data); }; \ No newline at end of file diff --git a/docs/snippets/tracer/captureMethodDecorator.ts b/docs/snippets/tracer/captureMethodDecorator.ts index 6c580f3e5d..69b997b265 100644 --- a/docs/snippets/tracer/captureMethodDecorator.ts +++ b/docs/snippets/tracer/captureMethodDecorator.ts @@ -4,16 +4,16 @@ import { LambdaInterface } from '@aws-lambda-powertools/commons'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); class Lambda implements LambdaInterface { - // Decorate your class method - @tracer.captureMethod() // (1) - public async getChargeId(): Promise { - /* ... */ - return 'foo bar'; - } + // Decorate your class method + @tracer.captureMethod() // (1) + public async getChargeId(): Promise { + /* ... */ + return 'foo bar'; + } - public async handler(_event: any, _context: any): Promise { - /* ... */ - } + public async handler(_event: unknown, _context: unknown): Promise { + await this.getChargeId(); + } } const handlerClass = new Lambda(); diff --git a/docs/snippets/tracer/captureMethodManual.ts b/docs/snippets/tracer/captureMethodManual.ts index af8c0a1dc8..8e8a6e07ed 100644 --- a/docs/snippets/tracer/captureMethodManual.ts +++ b/docs/snippets/tracer/captureMethodManual.ts @@ -3,32 +3,31 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); const getChargeId = async (): Promise => { - const parentSubsegment = tracer.getSegment(); // This is the subsegment currently active - // Create subsegment for the function & set it as active - const subsegment = parentSubsegment.addNewSubsegment(`### chargeId`); - tracer.setSegment(subsegment); + const parentSubsegment = tracer.getSegment(); // This is the subsegment currently active + // Create subsegment for the function & set it as active + const subsegment = parentSubsegment.addNewSubsegment(`### chargeId`); + tracer.setSegment(subsegment); - let res; - try { - /* ... */ - // Add the response as metadata - tracer.addResponseAsMetadata(res, 'chargeId'); - } catch (err) { - // Add the error as metadata - tracer.addErrorAsMetadata(err as Error); - throw err; - } + let res; + try { + /* ... */ + // Add the response as metadata + tracer.addResponseAsMetadata(res, 'chargeId'); + } catch (err) { + // Add the error as metadata + tracer.addErrorAsMetadata(err as Error); + throw err; + } - // Close subsegment (the AWS Lambda one is closed automatically) - subsegment.close(); - // Set the facade segment as active again - tracer.setSegment(parentSubsegment); + // Close subsegment (the AWS Lambda one is closed automatically) + subsegment.close(); + // Set the facade segment as active again + tracer.setSegment(parentSubsegment); - return res; + return res; }; -export const handler = async (_event: any, _context: any): Promise => { - const chargeId = getChargeId(); - const payment = collectPayment(chargeId); - /* ... */ +export const handler = async (_event: unknown, _context: unknown): Promise => { + await getChargeId(); + }; \ No newline at end of file diff --git a/docs/snippets/tracer/decorator.ts b/docs/snippets/tracer/decorator.ts index 1e17ea6af0..5373433f6f 100644 --- a/docs/snippets/tracer/decorator.ts +++ b/docs/snippets/tracer/decorator.ts @@ -4,11 +4,11 @@ import { LambdaInterface } from '@aws-lambda-powertools/commons'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); class Lambda implements LambdaInterface { - // Decorate your handler class method - @tracer.captureLambdaHandler() - public async handler(_event: any, _context: any): Promise { - /* ... */ - } + // Decorate your handler class method + @tracer.captureLambdaHandler() + public async handler(_event: unknown, _context: unknown): Promise { + tracer.getSegment(); + } } const handlerClass = new Lambda(); diff --git a/docs/snippets/tracer/disableCaptureResponseHandler.ts b/docs/snippets/tracer/disableCaptureResponseHandler.ts index de9549c22c..0191f51b02 100644 --- a/docs/snippets/tracer/disableCaptureResponseHandler.ts +++ b/docs/snippets/tracer/disableCaptureResponseHandler.ts @@ -4,10 +4,10 @@ import { LambdaInterface } from '@aws-lambda-powertools/commons'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); class Lambda implements LambdaInterface { - @tracer.captureLambdaHandler({ captureResponse: false }) - async handler(_event: any, _context: any): Promise { - /* ... */ - } + @tracer.captureLambdaHandler({ captureResponse: false }) + public async handler(_event: unknown, _context: unknown): Promise { + tracer.getSegment(); + } } const handlerClass = new Lambda(); diff --git a/docs/snippets/tracer/disableCaptureResponseMethod.ts b/docs/snippets/tracer/disableCaptureResponseMethod.ts index 7386d75338..7828d3b264 100644 --- a/docs/snippets/tracer/disableCaptureResponseMethod.ts +++ b/docs/snippets/tracer/disableCaptureResponseMethod.ts @@ -1,17 +1,18 @@ +import { LambdaInterface } from '@aws-lambda-powertools/commons'; import { Tracer } from '@aws-lambda-powertools/tracer'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); class Lambda implements LambdaInterface { - @tracer.captureMethod({ captureResponse: false }) - public async getChargeId(): Promise { - /* ... */ - return 'foo bar'; - } + @tracer.captureMethod({ captureResponse: false }) + public async getChargeId(): Promise { + /* ... */ + return 'foo bar'; + } - public async handler(_event: any, _context: any): Promise { - /* ... */ - } + public async handler(_event: unknown, _context: unknown): Promise { + /* ... */ + } } const handlerClass = new Lambda(); diff --git a/docs/snippets/tracer/disableCaptureResponseMiddy.ts b/docs/snippets/tracer/disableCaptureResponseMiddy.ts index b0916d00fc..567b38036a 100644 --- a/docs/snippets/tracer/disableCaptureResponseMiddy.ts +++ b/docs/snippets/tracer/disableCaptureResponseMiddy.ts @@ -3,12 +3,12 @@ import middy from '@middy/core'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); -const lambdaHandler = async (_event: any, _context: any): Promise => { - /* ... */ +const lambdaHandler = async (_event: unknown, _context: unknown): Promise => { + /* ... */ }; // Wrap the handler with middy export const handler = middy(lambdaHandler) - // Use the middleware by passing the Tracer instance as a parameter, - // but specify the captureResponse option as false. - .use(captureLambdaHandler(tracer, { captureResponse: false })); \ No newline at end of file +// Use the middleware by passing the Tracer instance as a parameter, +// but specify the captureResponse option as false. + .use(captureLambdaHandler(tracer, { captureResponse: false })); \ No newline at end of file diff --git a/docs/snippets/tracer/manual.ts b/docs/snippets/tracer/manual.ts index de0fe064e9..a17be6a900 100644 --- a/docs/snippets/tracer/manual.ts +++ b/docs/snippets/tracer/manual.ts @@ -2,31 +2,30 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); -export const handler = async (_event: any, context: any): Promise => { - const segment = tracer.getSegment(); // This is the facade segment (the one that is created by AWS Lambda) - // Create subsegment for the function & set it as active - const subsegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`); - tracer.setSegment(subsegment); +export const handler = async (_event: unknown, _context: unknown): Promise => { + const segment = tracer.getSegment(); // This is the facade segment (the one that is created by AWS Lambda) + // Create subsegment for the function & set it as active + const subsegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`); + tracer.setSegment(subsegment); - // Annotate the subsegment with the cold start & serviceName - tracer.annotateColdStart(); - tracer.addServiceNameAnnotation(); + // Annotate the subsegment with the cold start & serviceName + tracer.annotateColdStart(); + tracer.addServiceNameAnnotation(); + + try { + + // Add the response as metadata + tracer.addResponseAsMetadata({}, process.env._HANDLER); + } catch (err) { + // Add the error as metadata + tracer.addErrorAsMetadata(err as Error); + throw err; + } finally { + // Close subsegment (the AWS Lambda one is closed automatically) + subsegment.close(); + // Set back the facade segment as active again + tracer.setSegment(segment); + } - let res; - try { - /* ... */ - // Add the response as metadata - tracer.addResponseAsMetadata(res, process.env._HANDLER); - } catch (err) { - // Add the error as metadata - tracer.addErrorAsMetadata(err as Error); - throw err; - } finally { - // Close subsegment (the AWS Lambda one is closed automatically) - subsegment.close(); - // Set back the facade segment as active again - tracer.setSegment(segment); - } - - return res; + return {}; }; \ No newline at end of file diff --git a/docs/snippets/tracer/middy.ts b/docs/snippets/tracer/middy.ts index 0318b099f6..0ca33cec20 100644 --- a/docs/snippets/tracer/middy.ts +++ b/docs/snippets/tracer/middy.ts @@ -3,11 +3,11 @@ import middy from '@middy/core'; // (1) const tracer = new Tracer({ serviceName: 'serverlessAirline' }); -const lambdaHandler = async (_event: any, _context: any): Promise => { - /* ... */ +const lambdaHandler = async (_event: unknown, _context: unknown): Promise => { + tracer.putAnnotation('successfulBooking', true); }; // Wrap the handler with middy export const handler = middy(lambdaHandler) - // Use the middleware by passing the Tracer instance as a parameter - .use(captureLambdaHandler(tracer)); \ No newline at end of file +// Use the middleware by passing the Tracer instance as a parameter + .use(captureLambdaHandler(tracer)); \ No newline at end of file diff --git a/docs/snippets/tracer/putAnnotation.ts b/docs/snippets/tracer/putAnnotation.ts index 5b48d831af..399049046b 100644 --- a/docs/snippets/tracer/putAnnotation.ts +++ b/docs/snippets/tracer/putAnnotation.ts @@ -2,6 +2,6 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); -export const handler = async (_event: any, _context: any): Promise => { - tracer.putAnnotation('successfulBooking', true); +export const handler = async (_event: unknown, _context: unknown): Promise => { + tracer.putAnnotation('successfulBooking', true); }; \ No newline at end of file diff --git a/docs/snippets/tracer/putMetadata.ts b/docs/snippets/tracer/putMetadata.ts index c69a307373..e9d873a632 100644 --- a/docs/snippets/tracer/putMetadata.ts +++ b/docs/snippets/tracer/putMetadata.ts @@ -2,7 +2,9 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); -export const handler = async (_event: any, _context: any): Promise => { - const res; /* ... */ - tracer.putMetadata('paymentResponse', res); +export const handler = async (_event: unknown, _context: unknown): Promise => { + + tracer.putMetadata('paymentResponse', { + 'foo': 'bar' + }); }; \ No newline at end of file diff --git a/docs/snippets/tracer/sam.ts b/docs/snippets/tracer/sam.ts index 881471c80b..407c1ac863 100644 --- a/docs/snippets/tracer/sam.ts +++ b/docs/snippets/tracer/sam.ts @@ -2,6 +2,7 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; // Tracer parameter fetched from the environment variables (see template.yaml tab) const tracer = new Tracer(); +tracer.getSegment(); // You can also pass the parameter in the constructor // const tracer = new Tracer({ diff --git a/package-lock.json b/package-lock.json index 1c7a720b79..17a8ec3afb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,7 +68,8 @@ "@aws-sdk/client-dynamodb": "^3.245.0", "@aws-sdk/client-secrets-manager": "^3.250.0", "@aws-sdk/client-ssm": "^3.245.0", - "@aws-sdk/util-dynamodb": "^3.245.0" + "@aws-sdk/util-dynamodb": "^3.245.0", + "axios": "^1.2.4" } }, "docs/snippets/node_modules/@aws-sdk/util-dynamodb": { @@ -5805,9 +5806,9 @@ } }, "node_modules/aws-sdk": { - "version": "2.1279.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1279.0.tgz", - "integrity": "sha512-52NbHEZTLlrld6XDLvVaOwEI0p4nYTYVuninX8s4kdkBXeTezaBahsufWT7LmeYh10gp70dnwaUxvja1TjmeRA==", + "version": "2.1302.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1302.0.tgz", + "integrity": "sha512-OeP31meLGCcBJel2Re1yRsrjqDT3FvLFMQwPVtKHkXnws6QpgVg1FPiEjz4emEREUi6NfbqGNVExOGLsKiz0YA==", "dev": true, "dependencies": { "buffer": "4.9.2", @@ -6077,9 +6078,9 @@ "dev": true }, "node_modules/axios": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", - "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.4.tgz", + "integrity": "sha512-lIQuCfBJvZB/Bv7+RWUqEJqNShGOVpk9v7P0ZWx5Ip0qY6u7JBAU6dzQPMLasU9vHL2uD8av/1FDJXj7n6c39w==", "dev": true, "dependencies": { "follow-redirects": "^1.15.0", @@ -21244,9 +21245,9 @@ } }, "aws-sdk": { - "version": "2.1279.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1279.0.tgz", - "integrity": "sha512-52NbHEZTLlrld6XDLvVaOwEI0p4nYTYVuninX8s4kdkBXeTezaBahsufWT7LmeYh10gp70dnwaUxvja1TjmeRA==", + "version": "2.1302.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1302.0.tgz", + "integrity": "sha512-OeP31meLGCcBJel2Re1yRsrjqDT3FvLFMQwPVtKHkXnws6QpgVg1FPiEjz4emEREUi6NfbqGNVExOGLsKiz0YA==", "dev": true, "requires": { "buffer": "4.9.2", @@ -21465,9 +21466,9 @@ "dev": true }, "axios": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", - "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.4.tgz", + "integrity": "sha512-lIQuCfBJvZB/Bv7+RWUqEJqNShGOVpk9v7P0ZWx5Ip0qY6u7JBAU6dzQPMLasU9vHL2uD8av/1FDJXj7n6c39w==", "dev": true, "requires": { "follow-redirects": "^1.15.0", @@ -23275,11 +23276,13 @@ "docs": { "version": "file:docs/snippets", "requires": { + "@aws-sdk/client-appconfigdata": "^3.245.0", "@aws-sdk/client-appconfigdata": "^3.245.0", "@aws-sdk/client-dynamodb": "^3.245.0", "@aws-sdk/client-secrets-manager": "^3.250.0", "@aws-sdk/client-ssm": "^3.245.0", - "@aws-sdk/util-dynamodb": "^3.245.0" + "@aws-sdk/util-dynamodb": "^3.245.0", + "axios": "^1.2.4" }, "dependencies": { "@aws-sdk/util-dynamodb": { From 61080da2b881353cb3cc84b075559dca07c67c87 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 7 Feb 2023 19:34:25 +0100 Subject: [PATCH 19/56] chore: moved docs/snippets checks to separate step (#1274) --- ...sable-run-linting-check-and-unit-tests.yml | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/.github/workflows/reusable-run-linting-check-and-unit-tests.yml b/.github/workflows/reusable-run-linting-check-and-unit-tests.yml index e779fd0737..3bac08a4f4 100644 --- a/.github/workflows/reusable-run-linting-check-and-unit-tests.yml +++ b/.github/workflows/reusable-run-linting-check-and-unit-tests.yml @@ -42,9 +42,9 @@ jobs: if: steps.cache-node-modules.outputs.cache-hit == 'true' run: | npm run build -w packages/commons - npm run build -w packages/logger & npm run build -w packages/tracer & npm run build -w packages/metrics & npm run build -w packages/parameters & npm run build -w packages/idempotency & npm run build -w docs/snippets + npm run build -w packages/logger & npm run build -w packages/tracer & npm run build -w packages/metrics & npm run build -w packages/parameters & npm run build -w packages/idempotency - name: Run linting - run: npm run lint -w packages/commons -w packages/logger -w packages/tracer -w packages/metrics -w packages/parameters -w packages/idempotency -w docs/snippets + run: npm run lint -w packages/commons -w packages/logger -w packages/tracer -w packages/metrics -w packages/parameters -w packages/idempotency - name: Run unit tests run: npm t -w packages/commons -w packages/logger -w packages/tracer -w packages/metrics -w packages/parameters -w packages/idempotency check-examples: @@ -64,7 +64,7 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 cache: "npm" - name: Cache node modules id: cache-node-modules @@ -97,7 +97,7 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 cache: "npm" - name: Cache node modules id: cache-node-modules @@ -114,3 +114,40 @@ jobs: run: npm run lint - name: Run tests run: npm t + check-docs-snippets: + runs-on: ubuntu-latest + env: + NODE_ENV: dev + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Setup NodeJS + uses: actions/setup-node@v3 + with: + node-version: 18 + cache: "npm" + - name: Setup npm + run: npm i -g npm@next-8 + - name: Cache node modules + id: cache-node-modules + uses: actions/cache@v3 + with: + path: "./node_modules" + # Use the combo between node version, name, and SHA-256 hash of the lock file as cache key so that + # if one of them changes the cache is invalidated/discarded + key: 18-cache-utilities-node-modules-${{ hashFiles('./package-lock.json') }} + - name: Install dependencies + # We can skip the installation if there was a cache hit + if: steps.cache-node-modules.outputs.cache-hit != 'true' + # See https://github.com/npm/cli/issues/4475 to see why --foreground-scripts + run: npm ci --foreground-scripts + - name: Build packages + # If there's a cache hit we still need to manually build the packages + # this would otherwise have been done automatically as a part of the + # post-install npm hook + if: steps.cache-node-modules.outputs.cache-hit == 'true' + run: | + npm run build -w packages/commons + npm run build -w packages/logger & npm run build -w packages/tracer & npm run build -w packages/metrics & npm run build -w packages/parameters & npm run build -w packages/idempotency & npm run build -w docs/snippets + - name: Run linting + run: npm run lint -w packages/commons -w packages/logger -w packages/tracer -w packages/metrics -w packages/parameters -w packages/idempotency -w docs/snippets From ba921c1d1c784ff1ff00b171df75f4bf87369c40 Mon Sep 17 00:00:00 2001 From: Alexander Schueren Date: Thu, 9 Feb 2023 12:03:54 +0100 Subject: [PATCH 20/56] tests(parameters): add comments to `SecretsProvider` e2e test (#1282) * fix import to src, instead of lib * clearing cache to fix forceFetch test case * add more comments, create dedicated resource for test 7 * Update packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts --------- Co-authored-by: Andrea Amorosi --- ...secretsProvider.class.test.functionCode.ts | 24 ++--- .../tests/e2e/secretsProvider.class.test.ts | 88 +++++++++++++------ 2 files changed, 74 insertions(+), 38 deletions(-) diff --git a/packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts b/packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts index 655b9472a6..a1bd955919 100644 --- a/packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts +++ b/packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts @@ -1,9 +1,9 @@ import { Context } from 'aws-lambda'; -import { SecretsProvider } from '../../lib/secrets'; import { TinyLogger } from '../helpers/tinyLogger'; -import { SecretsGetOptionsInterface } from '../../lib/types'; import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager'; import { middleware } from '../helpers/sdkMiddlewareRequestCounter'; +import { SecretsProvider } from '../../src/secrets'; +import { SecretsGetOptionsInterface } from '../../src/types'; const logger = new TinyLogger(); const defaultProvider = new SecretsProvider(); @@ -14,6 +14,7 @@ const secretNameBinary = process.env.SECRET_NAME_BINARY || ''; const secretNameObjectWithSuffix = process.env.SECRET_NAME_OBJECT_WITH_SUFFIX || ''; const secretNameBinaryWithSuffix = process.env.SECRET_NAME_BINARY_WITH_SUFFIX || ''; const secretNamePlainChached = process.env.SECRET_NAME_PLAIN_CACHED || ''; +const secretNamePlainForceFetch = process.env.SECRET_NAME_PLAIN_FORCE_FETCH || ''; // Provider test 8, 9 const customClient = new SecretsManagerClient({}); @@ -42,23 +43,23 @@ const _call_get = async (paramName: string, testName: string, options?: SecretsG export const handler = async (_event: unknown, _context: Context): Promise => { - // Test 1 get single param as plaintext + // Test 1 get single secret as plaintext await _call_get(secretNamePlain, 'get-plain'); - // Test 2 get single param with transform json + // Test 2 get single secret with transform json await _call_get(secretNameObject, 'get-transform-json', { transform: 'json' }); - // Test 3 get single param with transform binary + // Test 3 get single secret with transform binary await _call_get(secretNameBinary, 'get-transform-binary', { transform: 'binary' }); - // Test 4 get single param with transform auto json + // Test 4 get single secret with transform auto json await _call_get(secretNameObjectWithSuffix, 'get-transform-auto-json', { transform: 'auto' }); - // Test 5 get single param with transform auto binary + // Test 5 get single secret with transform auto binary await _call_get(secretNameBinaryWithSuffix, 'get-transform-auto-binary', { transform: 'auto' }); // Test 6 - // get parameter twice with middleware, which counts number of SDK requests, we check later if we only called SecretManager API once + // get secret twice with middleware, which counts number of SDK requests, we check later if we only called SecretManager API once try { middleware.counter = 0; await providerWithMiddleware.get(secretNamePlainChached); @@ -74,11 +75,12 @@ export const handler = async (_event: unknown, _context: Context): Promise }); } // Test 7 - // get parameter twice, but force fetch 2nd time, we count number of SDK requests and check that we made two API calls + // get secret twice, but force fetch 2nd time, we count number of SDK requests and check that we made two API calls try { middleware.counter = 0; - await providerWithMiddleware.get(secretNamePlainChached); - await providerWithMiddleware.get(secretNamePlainChached, { forceFetch: true }); + providerWithMiddleware.clearCache(); + await providerWithMiddleware.get(secretNamePlainForceFetch); + await providerWithMiddleware.get(secretNamePlainForceFetch, { forceFetch: true }); logger.log({ test: 'get-plain-force', value: middleware.counter // should be 2 diff --git a/packages/parameters/tests/e2e/secretsProvider.class.test.ts b/packages/parameters/tests/e2e/secretsProvider.class.test.ts index a4ea351151..48e2734974 100644 --- a/packages/parameters/tests/e2e/secretsProvider.class.test.ts +++ b/packages/parameters/tests/e2e/secretsProvider.class.test.ts @@ -24,29 +24,51 @@ const runtime: string = process.env.RUNTIME || 'nodejs18x'; if (!isValidRuntimeKey(runtime)) { throw new Error(`Invalid runtime key: ${runtime}`); } - -const uuid = v4(); -const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'secretsProvider'); -const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'secretsProvider'); -const lambdaFunctionCodeFile = 'secretsProvider.class.test.functionCode.ts'; - -const secretNamePlain = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretPlain'); -const secretNamePlainCached = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretPlainCached'); -const secretNameObject = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject'); -const secretNameBinary = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretBinary'); -const secretNameObjectWithSuffix = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject.json'); -const secretNameBinaryWithSuffix = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject.binary'); - -const invocationCount = 1; - -const integTestApp = new App(); -let stack: Stack; - +/** + * Collection of e2e tests for SecretsProvider utility. + * + * Test 1: create a secret with plain text value, fetch it with no additional options + * Test 2: create a secret with json value, fetch it using `transform: 'json'` option + * Test 3: create a secret with base64 encoded value (technicaly string), fetch it using `transform: 'binary'` option + * Test 4: create a secret with json value and secret name ends with .json, fetch it using `transform: 'auto'` option + * Test 5: create a secret with base64 encoded value (technicaly string) and secert name ends with .binary, fetch it using `transform: 'auto'` option + * Test 6: create a secret with plain text value, fetch it twice, check that value was cached, the number of SDK calls should be 1 + * Test 7: create a secret with plain text value, fetch it twice, second time with `forceFetch: true` option, check that value was not cached, the number of SDK calls should be 2 + * + * For tests 6 and 7 we use our own AWS SDK custom middleware plugin `sdkMiddlewareRequestCounter.ts` + * + * Adding new test: + * Please keep the state clean, and create dedicated resource for your test, don't reuse resources from other tests. + * Pass the necessary information to lambda function by using enviroment variables + * Make sure to add the right permissions to the lambda function to access the resources. We use our `ResourceAccessGranter` to add permissions. + * + */ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () => { + const uuid = v4(); let invocationLogs: InvocationLogs[]; + const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'secretsProvider'); + const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'secretsProvider'); + const lambdaFunctionCodeFile = 'secretsProvider.class.test.functionCode.ts'; + + const invocationCount = 1; + + const integTestApp = new App(); + let stack: Stack; beforeAll(async () => { + + // use unique names for each test to keep a clean state + const secretNamePlain = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretPlain'); + const secretNameObject = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject'); + const secretNameBinary = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretBinary'); + const secretNameObjectWithSuffix = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject.json'); + const secretNameBinaryWithSuffix = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject.binary'); + const secretNamePlainCached = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretPlainCached'); + const secretNamePlainForceFetch = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretPlainForceFetch'); + + // creates the test fuction that uses powertools secret provider we want to test + // pass env vars with secret names we want to fetch stack = createStackWithLambdaFunction({ app: integTestApp, stackName: stackName, @@ -61,6 +83,7 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () => SECRET_NAME_OBJECT_WITH_SUFFIX: secretNameObjectWithSuffix, SECRET_NAME_BINARY_WITH_SUFFIX: secretNameBinaryWithSuffix, SECRET_NAME_PLAIN_CACHED: secretNamePlainCached, + SECRET_NAME_PLAIN_FORCE_FETCH: secretNamePlainForceFetch, }, runtime: runtime }); @@ -99,7 +122,14 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () => secretStringValue: SecretValue.unsafePlainText('foo') }); - Aspects.of(stack).add(new ResourceAccessGranter([ secretString, secretObject, secretBinary, secretObjectWithSuffix, secretBinaryWithSuffix, secretStringCached ])); + const secretStringForceFetch = new Secret(stack, 'testSecretStringForceFetch', { + secretName: secretNamePlainForceFetch, + secretStringValue: SecretValue.unsafePlainText('foo') + }); + + // add secrets here to grant lambda permisisons to access secrets + Aspects.of(stack).add(new ResourceAccessGranter([ + secretString, secretObject, secretBinary, secretObjectWithSuffix, secretBinaryWithSuffix, secretStringCached, secretStringForceFetch ])); await deployStack(integTestApp, stack); @@ -108,7 +138,8 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () => }, SETUP_TIMEOUT); describe('SecretsProvider usage', () => { - it('should retrieve a single parameter', async () => { + + it('should retrieve a secret as plain string', async () => { const logs = invocationLogs[0].getFunctionLogs(); const testLog = InvocationLogs.parseFunctionLog(logs[0]); @@ -119,7 +150,7 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () => }); }, TEST_CASE_TIMEOUT); - it('should retrieve a single parameter with transform json', async () => { + it('should retrieve a secret using transform json option', async () => { const logs = invocationLogs[0].getFunctionLogs(); const testLog = InvocationLogs.parseFunctionLog(logs[1]); @@ -129,7 +160,7 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () => }); }, TEST_CASE_TIMEOUT); - it('should retrieve single param with transform binary', async () => { + it('should retrieve a secret using transform binary option', async () => { const logs = invocationLogs[0].getFunctionLogs(); const testLog = InvocationLogs.parseFunctionLog(logs[2]); @@ -140,17 +171,18 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () => }, TEST_CASE_TIMEOUT); }); - it('should retrieve single param with transform auto json', async () => { + it('should retrieve a secret using transform auto option with implicit json', async () => { const logs = invocationLogs[0].getFunctionLogs(); const testLog = InvocationLogs.parseFunctionLog(logs[3]); + // result should be a json object expect(testLog).toStrictEqual({ test: 'get-transform-auto-json', value: { foo: 'bar' } }); }, TEST_CASE_TIMEOUT); - it('should retrieve single param wit transform auto binary', async () => { + it('should retrieve a secret using transform auto option with implicit binary', async () => { const logs = invocationLogs[0].getFunctionLogs(); const testLog = InvocationLogs.parseFunctionLog(logs[4]); @@ -160,23 +192,25 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () => }); }); - it('should retrieve single parameter cached', async () => { + it('should retrieve a secret twice with cached value', async () => { const logs = invocationLogs[0].getFunctionLogs(); const testLogFirst = InvocationLogs.parseFunctionLog(logs[5]); + // we fetch twice, but we expect to make an API call only once expect(testLogFirst).toStrictEqual({ test: 'get-plain-cached', value: 1 }); }); - it('should retrieve single parameter twice without caching', async () => { + it('should retrieve a secret twice with forceFetch second time', async () => { const logs = invocationLogs[0].getFunctionLogs(); const testLogFirst = InvocationLogs.parseFunctionLog(logs[6]); + // we fetch twice, 2nd time with forceFetch: true flag, we expect two api calls expect(testLogFirst).toStrictEqual({ test: 'get-plain-force', - value: 1 + value: 2 }); }); From 84ab4b911d17d687bdbe60ded31f1e2b6860feb3 Mon Sep 17 00:00:00 2001 From: Sergei Cherniaev Date: Thu, 9 Feb 2023 16:18:40 +0300 Subject: [PATCH 21/56] fix(logger): createChild not passing all parent's attributes (#1267) * fix(logger): fix a bug when child logger did get all the parent's attributes * test(logger): update test, expect any boolean in logsSampled because it depends on random --- packages/logger/src/Logger.ts | 7 +- packages/logger/tests/unit/Logger.test.ts | 94 ++++++++++++++++++++++- 2 files changed, 97 insertions(+), 4 deletions(-) diff --git a/packages/logger/src/Logger.ts b/packages/logger/src/Logger.ts index a8ef03c8b5..2ce5df7059 100644 --- a/packages/logger/src/Logger.ts +++ b/packages/logger/src/Logger.ts @@ -203,8 +203,13 @@ class Logger extends Utility implements ClassThatLogs { * @returns {Logger} */ public createChild(options: ConstructorOptions = {}): Logger { + const parentsOptions = { + logLevel: this.getLogLevel(), + customConfigService: this.getCustomConfigService(), + logFormatter: this.getLogFormatter(), + }; const parentsPowertoolsLogData = this.getPowertoolLogData(); - const childLogger = new Logger(merge({}, parentsPowertoolsLogData, options)); + const childLogger = new Logger(merge(parentsOptions, parentsPowertoolsLogData, options)); const parentsPersistentLogAttributes = this.getPersistentLogAttributes(); childLogger.addPersistentLogAttributes(parentsPersistentLogAttributes); diff --git a/packages/logger/tests/unit/Logger.test.ts b/packages/logger/tests/unit/Logger.test.ts index 717c964ad1..4b0775421d 100644 --- a/packages/logger/tests/unit/Logger.test.ts +++ b/packages/logger/tests/unit/Logger.test.ts @@ -8,7 +8,7 @@ import { ContextExamples as dummyContext, Events as dummyEvent, LambdaInterface import { createLogger, Logger } from '../../src'; import { EnvironmentVariablesService } from '../../src/config'; import { PowertoolLogFormatter } from '../../src/formatter'; -import { ClassThatLogs, LogJsonIndent } from '../../src/types'; +import { ClassThatLogs, LogJsonIndent, ConstructorOptions } from '../../src/types'; import { Context } from 'aws-lambda'; import { Console } from 'console'; @@ -1297,7 +1297,7 @@ describe('Class: Logger', () => { describe('Method: createChild', () => { - test('Child and grandchild loggers should have all ancestor\'s options', () => { + test('child and grandchild loggers should have all ancestor\'s options', () => { // Prepare const INDENTATION = LogJsonIndent.COMPACT; const loggerOptions = { @@ -1398,7 +1398,7 @@ describe('Class: Logger', () => { }); - test('when called, it returns a DISTINCT clone of the logger instance', () => { + test('child logger should be a DISTINCT clone of the logger instance', () => { // Prepare const INDENTATION = LogJsonIndent.COMPACT; @@ -1716,6 +1716,94 @@ describe('Class: Logger', () => { }, }); }); + + test('child logger should have parent\'s logFormatter', () => { + + // Prepare + class MyCustomLogFormatter extends PowertoolLogFormatter {} + const parentLogger = new Logger({ + logFormatter: new MyCustomLogFormatter() + }); + + // Act + const childLoggerWithParentLogFormatter = parentLogger.createChild(); + + // Assess + expect(childLoggerWithParentLogFormatter).toEqual( + expect.objectContaining({ + logFormatter: expect.any(MyCustomLogFormatter), + }) + ); + }); + + test('child logger with custom logFormatter in options should have provided logFormatter', () => { + + // Prepare + class MyCustomLogFormatter extends PowertoolLogFormatter {} + const parentLogger = new Logger(); + + // Act + const childLoggerWithCustomLogFormatter = parentLogger.createChild({ + logFormatter: new MyCustomLogFormatter() + }); + + // Assess + expect(parentLogger).toEqual( + expect.objectContaining({ + logFormatter: expect.any(PowertoolLogFormatter), + }) + ); + + expect(childLoggerWithCustomLogFormatter).toEqual( + expect.objectContaining({ + logFormatter: expect.any(MyCustomLogFormatter), + }) + ); + }); + + test('child logger should have exact same attributes as the parent logger created with all non-default options', () => { + + // Prepare + class MyCustomLogFormatter extends PowertoolLogFormatter {} + class MyCustomEnvironmentVariablesService extends EnvironmentVariablesService {} + + const options: ConstructorOptions = { + logLevel: 'ERROR', + serviceName: 'test-service-name', + sampleRateValue: 0.77, + logFormatter: new MyCustomLogFormatter(), + customConfigService: new MyCustomEnvironmentVariablesService(), + persistentLogAttributes: { + aws_account_id: '1234567890', + aws_region: 'eu-west-1', + }, + environment: 'local' + }; + const parentLogger = new Logger(options); + + // Act + const childLogger = parentLogger.createChild(); + + // Assess + expect(childLogger).toEqual({ + ...parentLogger, + console: expect.any(Console), + logsSampled: expect.any(Boolean), + }); + + expect(childLogger).toEqual( + expect.objectContaining({ + logFormatter: expect.any(MyCustomLogFormatter), + }) + ); + + expect(childLogger).toEqual( + expect.objectContaining({ + customConfigService: expect.any(MyCustomEnvironmentVariablesService), + }) + ); + + }); }); From 6b08fb65dd5cdd93880b1c72ad8b1f3bb62937be Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Fri, 10 Feb 2023 19:27:25 +0100 Subject: [PATCH 22/56] docs(examples): made SAM CLI version requirement more explicit (#1284) --- examples/sam/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/sam/README.md b/examples/sam/README.md index 9c7f88077f..bf8c45af66 100644 --- a/examples/sam/README.md +++ b/examples/sam/README.md @@ -23,7 +23,8 @@ Before deploying this example install the npm dependencies: npm i ``` -In addition to the [recommended setup for this project](https://github.com/awslabs/aws-lambda-powertools-typescript/blob/main/CONTRIBUTING.md#setup), you'll need the [SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html). +> **Note** +> In order to run this example you'll need [AWS SAM CLI version 1.65 or later](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html). If you have an older version of the AWS SAM CLI, see [Upgrading the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/manage-sam-cli-versions.html#manage-sam-cli-versions-upgrade). ## Deploy the sample application From db215dd80c9a18a612c32c9fa5e373af1c110476 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Fri, 10 Feb 2023 20:10:12 +0100 Subject: [PATCH 23/56] tests(parameters): additional integration tests to `DynamoDBProvider` (#1285) --- ...ynamoDBProvider.class.test.functionCode.ts | 182 +++++++----------- .../tests/e2e/dynamoDBProvider.class.test.ts | 37 +++- 2 files changed, 102 insertions(+), 117 deletions(-) diff --git a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.functionCode.ts b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.functionCode.ts index f8d80314ae..30ac2bbbdc 100644 --- a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.functionCode.ts +++ b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.functionCode.ts @@ -1,11 +1,15 @@ import { Context } from 'aws-lambda'; import { DynamoDBProvider } from '../../src/dynamodb'; +import { + DynamoDBGetOptionsInterface, + DynamoDBGetMultipleOptionsInterface, +} from '../../src/types'; import { TinyLogger } from '../helpers/tinyLogger'; -// # TODO: Uncomment code below once #1222 is fixed -/* import { middleware } from '../helpers/sdkMiddlewareRequestCounter'; import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; -*/ + +// We use a custom logger to log pure JSON objects to stdout +const logger = new TinyLogger(); const tableGet = process.env.TABLE_GET ?? 'my-table'; const tableGetMultiple = process.env.TABLE_GET_MULTIPLE ?? 'my-table'; @@ -15,9 +19,6 @@ const keyAttr = process.env.KEY_ATTR ?? 'id'; const sortAttr = process.env.SORT_ATTR ?? 'sk'; const valueAttr = process.env.VALUE_ATTR ?? 'value'; -// We use a custom logger to log pure JSON objects to stdout -const logger = new TinyLogger(); - // Provider test 1, 5, 6 const providerGet = new DynamoDBProvider({ tableName: tableGet, @@ -39,163 +40,122 @@ const providerGetMultipleCustomKeys = new DynamoDBProvider({ sortAttr, valueAttr, }); -// # TODO: Uncomment code below once #1222 is fixed -/* + // Provider test 8, 9 const customClient = new DynamoDBClient({}); -providerWithMiddleware.middlewareStack.use(middleware); +customClient.middlewareStack.use(middleware); const providerWithMiddleware = new DynamoDBProvider({ - awsSdkV3Client: customClient + awsSdkV3Client: customClient, + tableName: tableGet, }); -*/ -export const handler = async (_event: unknown, _context: Context): Promise => { - // Test 1 - get a single parameter with default options (keyAttr: 'id', valueAttr: 'value') +// Helper function to call get() and log the result +const _call_get = async ( + paramName: string, + testName: string, + provider: DynamoDBProvider, + options?: DynamoDBGetOptionsInterface, +): Promise => { try { - const parameterValue = await providerGet.get('my-param'); + const parameterValue = await provider.get(paramName, options); logger.log({ - test: 'get', + test: testName, value: parameterValue }); } catch (err) { logger.log({ - test: 'get', + test: testName, error: err.message }); } +}; - // Test 2 - get multiple parameters with default options (keyAttr: 'id', sortAttr: 'sk', valueAttr: 'value') +// Helper function to call getMultiple() and log the result +const _call_get_multiple = async ( + paramPath: string, + testName: string, + provider: DynamoDBProvider, + options?: DynamoDBGetMultipleOptionsInterface, +): Promise => { try { - const parametersValues = await providerGetMultiple.getMultiple('my-params'); + const parameterValues = await provider.getMultiple( + paramPath, + options + ); logger.log({ - test: 'get-multiple', - value: parametersValues + test: testName, + value: parameterValues }); } catch (err) { logger.log({ - test: 'get-multiple', + test: testName, error: err.message }); } +}; + +export const handler = async (_event: unknown, _context: Context): Promise => { + // Test 1 - get a single parameter with default options (keyAttr: 'id', valueAttr: 'value') + await _call_get('my-param', 'get', providerGet); + + // Test 2 - get multiple parameters with default options (keyAttr: 'id', sortAttr: 'sk', valueAttr: 'value') + await _call_get_multiple('my-params', 'get-multiple', providerGetMultiple); // Test 3 - get a single parameter with custom options (keyAttr: 'key', valueAttr: 'val') - try { - const parameterValueCustom = await providerGetCustomKeys.get('my-param'); - logger.log({ - test: 'get-custom', - value: parameterValueCustom - }); - } catch (err) { - logger.log({ - test: 'get-custom', - error: err.message - }); - } + await _call_get('my-param', 'get-custom', providerGetCustomKeys); // Test 4 - get multiple parameters with custom options (keyAttr: 'key', sortAttr: 'sort', valueAttr: 'val') - try { - const parametersValuesCustom = await providerGetMultipleCustomKeys.getMultiple('my-params'); - logger.log({ - test: 'get-multiple-custom', - value: parametersValuesCustom - }); - } catch (err) { - logger.log({ - test: 'get-multiple-custom', - error: err.message - }); - } + await _call_get_multiple('my-params', 'get-multiple-custom', providerGetMultipleCustomKeys); // Test 5 - get a single parameter with json transform - try { - const parameterValueJson = await providerGet.get('my-param-json', { - transform: 'json' - }); - logger.log({ - test: 'get-json-transform', - value: typeof parameterValueJson // should be object - }); - } catch (err) { - logger.log({ - test: 'get-json-transform', - error: err.message - }); - } + await _call_get('my-param-json', 'get-json-transform', providerGet, { + transform: 'json' + }); // Test 6 - get a single parameter with binary transform - try { - const parameterValueBinary = await providerGet.get('my-param-binary', { - transform: 'binary' - }); - logger.log({ - test: 'get-binary-transform', - value: typeof parameterValueBinary // should be string - }); - } catch (err) { - logger.log({ - test: 'get-binary-transform', - error: err.message - }); - } + await _call_get('my-param-binary', 'get-binary-transform', providerGet, { + transform: 'binary' + }); // Test 7 - get multiple parameters with auto transform - try { - const parametersValuesAuto = await providerGetMultiple.getMultiple('my-encoded-params', { - transform: 'auto' - }); - if (!parametersValuesAuto) throw new Error('parametersValuesAuto is undefined'); - - logger.log({ - test: 'get-multiple-auto-transform', - value: - `${typeof parametersValuesAuto['config.json']},${typeof parametersValuesAuto['key.binary']}` // should be object,string - }); - } catch (err) { - logger.log({ - test: 'get-multiple-auto-transform', - error: err.message - }); - } + await _call_get_multiple('my-encoded-params', 'get-multiple-auto-transform', providerGetMultiple, { + transform: 'auto' + }); - // # TODO: Uncomment code below once #1222 is fixed - /** - * Test 8 - get a parameter twice, second time should be cached - * - * Should only make 1 request, we use middleware to count requests - */ - /* + // Test 8 + // get parameter twice with middleware, which counts the number of requests, we check later if we only called DynamoDB once try { + providerWithMiddleware.clearCache(); + middleware.counter = 0; await providerWithMiddleware.get('my-param'); await providerWithMiddleware.get('my-param'); logger.log({ - test: 'get-cache-request-count', - value: middleware.requestCount + test: 'get-cached', + value: middleware.counter // should be 1 }); } catch (err) { logger.log({ - test: 'get-cache-request-count', + test: 'get-cached', error: err.message }); } - */ - /** - * Test 9 - get a parameter once more but with forceFetch = true - * - * Request count should increase to 2, we use middleware to count requests - */ - /* + // Test 9 + // get parameter twice, but force fetch 2nd time, we count number of SDK requests and check that we made two API calls try { + providerWithMiddleware.clearCache(); + middleware.counter = 0; + await providerWithMiddleware.get('my-param'); await providerWithMiddleware.get('my-param', { forceFetch: true }); logger.log({ - test: 'get-force-fetch-request-count', - value: middleware.requestCount + test: 'get-forced', + value: middleware.counter // should be 2 }); } catch (err) { logger.log({ - test: 'get-force-fetch-request-count', + test: 'get-forced', error: err.message }); } - */ + }; \ No newline at end of file diff --git a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts index ce8f7f50d4..890d71cd8f 100644 --- a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts +++ b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts @@ -374,7 +374,7 @@ describe(`parameters E2E tests (dynamoDBProvider) for runtime: ${runtime}`, () = expect(testLog).toStrictEqual({ test: 'get-json-transform', - value: 'object', + value: { foo: 'bar' }, }); }); @@ -387,7 +387,7 @@ describe(`parameters E2E tests (dynamoDBProvider) for runtime: ${runtime}`, () = expect(testLog).toStrictEqual({ test: 'get-binary-transform', - value: 'string', // as opposed to Uint8Array + value: 'baz', }); }); @@ -400,15 +400,40 @@ describe(`parameters E2E tests (dynamoDBProvider) for runtime: ${runtime}`, () = expect(testLog).toStrictEqual({ test: 'get-multiple-auto-transform', - value: 'object,string', + value: { + 'config.json': { foo: 'bar' }, + 'key.binary': 'baz', + }, + }); + + }); + + // Test 8 - Get a parameter twice and check that the value is cached. + it('should retrieve multiple parameters with auto transforms', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[7]); + + expect(testLog).toStrictEqual({ + test: 'get-cached', + value: 1, }); }); - // TODO: implement tests for the following cases once #1222 is merged: - // Test 8 - get a parameter twice, second time should be cached - // Test 9 - get a parameter once more but with forceFetch = true + // Test 9 - Get a cached parameter and force retrieval. + it('should retrieve multiple parameters with auto transforms', async () => { + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[8]); + + expect(testLog).toStrictEqual({ + test: 'get-forced', + value: 2, + }); + + }); + }); afterAll(async () => { From 2fb3688edb87fc39ae1d681eb02c6458afc4abc7 Mon Sep 17 00:00:00 2001 From: Sergei Cherniaev Date: Mon, 13 Feb 2023 17:54:38 +0400 Subject: [PATCH 24/56] docs(logger): update child logger docs section and snippets (#1286) * docs(logger): add details about child logger usage * fix: fix formatting * Update docs/core/logger.md Co-authored-by: Andrea Amorosi * Update docs/core/logger.md Co-authored-by: Andrea Amorosi --------- Co-authored-by: Andrea Amorosi --- docs/core/logger.md | 14 ++++++++++++-- docs/snippets/logger/createChild.ts | 13 ++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/docs/core/logger.md b/docs/core/logger.md index c05fad22f0..fb25804135 100644 --- a/docs/core/logger.md +++ b/docs/core/logger.md @@ -390,8 +390,9 @@ The error will be logged with default key name `error`, but you can also pass yo ### Using multiple Logger instances across your code -Logger supports quick instance cloning via the `createChild` method. -This can be useful for example if you want to enable multiple Loggers with different logging levels in the same Lambda invocation. +The `createChild` method allows you to create a child instance of the Logger, which inherits all of the attributes from its parent. You have the option to override any of the settings and attributes from the parent logger, including [its settings](#utility-settings), any [persistent attributes](#appending-persistent-additional-log-keys-and-values), and [the log formatter](#custom-log-formatter-bring-your-own-formatter). Once a child logger is created, the logger and its parent will act as separate instances of the Logger class, and as such any change to one won't be applied to the other. + + The following example shows how to create multiple Loggers that share service name and persistent attributes while specifying different logging levels within a single Lambda invocation. As the result, only ERROR logs with all the inherited attributes will be displayed in CloudWatch Logs from the child logger, but all logs emitted will have the same service name and persistent attributes. === "handler.ts" @@ -407,6 +408,8 @@ This can be useful for example if you want to enable multiple Loggers with diffe "message": "This is an INFO log, from the parent logger", "service": "serverlessAirline", "timestamp": "2021-12-12T22:32:54.667Z", + "aws_account_id":"123456789012", + "aws_region":"eu-west-1", "xray_trace_id": "abcdef123456abcdef123456abcdef123456" } { @@ -414,6 +417,8 @@ This can be useful for example if you want to enable multiple Loggers with diffe "message": "This is an ERROR log, from the parent logger", "service": "serverlessAirline", "timestamp": "2021-12-12T22:32:54.670Z", + "aws_account_id":"123456789012", + "aws_region":"eu-west-1", "xray_trace_id": "abcdef123456abcdef123456abcdef123456" } { @@ -421,6 +426,8 @@ This can be useful for example if you want to enable multiple Loggers with diffe "message": "This is an ERROR log, from the child logger", "service": "serverlessAirline", "timestamp": "2021-12-12T22:32:54.670Z", + "aws_account_id":"123456789012", + "aws_region":"eu-west-1", "xray_trace_id": "abcdef123456abcdef123456abcdef123456" } ``` @@ -598,6 +605,9 @@ This is how the printed log would look: } ``` +!!! tip "Custom Log formatter and Child loggers" + It is not necessary to pass the `LogFormatter` each time a [child logger](#using-multiple-logger-instances-across-your-code) is created. The parent's LogFormatter will be inherited by the child logger. + ## Testing your code ### Inject Lambda Context diff --git a/docs/snippets/logger/createChild.ts b/docs/snippets/logger/createChild.ts index 7b9e7780e3..3535c03336 100644 --- a/docs/snippets/logger/createChild.ts +++ b/docs/snippets/logger/createChild.ts @@ -1,11 +1,18 @@ import { Logger } from '@aws-lambda-powertools/logger'; -// With this logger, all the INFO logs will be printed +// This logger has a service name, some persistent attributes +// and log level set to INFO const logger = new Logger({ - logLevel: 'INFO' + serviceName: 'serverlessAirline', + logLevel: 'INFO', + persistentLogAttributes: { + aws_account_id: '123456789012', + aws_region: 'eu-west-1', + }, }); -// With this logger, only the ERROR logs will be printed +// This other logger inherits all the parent's attributes +// but the log level, which is now set to ERROR const childLogger = logger.createChild({ logLevel: 'ERROR' }); From 140538e59d7e6cfc7f3addc604737fc71d333024 Mon Sep 17 00:00:00 2001 From: Heitor Lessa Date: Wed, 15 Feb 2023 11:04:49 +0000 Subject: [PATCH 25/56] docs(home): update powertools definition --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d97b378def..213d08a1b5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Join our Discord](https://dcbadge.vercel.app/api/server/B8zZKbbyET)](https://discord.gg/B8zZKbbyET) -A suite of utilities for AWS Lambda functions to ease the adoption of best practices such as tracing, structured logging, custom metrics, and more. +Powertools is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#features). You can use the library in both TypeScript and JavaScript code bases. From a9658c9396652420e3a36193fe5f13b698161676 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Wed, 15 Feb 2023 12:22:33 +0100 Subject: [PATCH 26/56] chore(docs): update project's description (#1290) * chore: update `commons` readme * chore: update `idempotency` readme * chore: update `logger` readme * chore: update `metrics` readme * chore: update `tracer` readme * chore: update main docs --- docs/index.md | 4 ++-- packages/commons/README.md | 4 ++-- packages/idempotency/README.md | 4 ++-- packages/logger/README.md | 4 ++-- packages/metrics/README.md | 2 +- packages/tracer/README.md | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/index.md b/docs/index.md index 54fbecb549..a6ea959862 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,7 +3,7 @@ title: Homepage description: AWS Lambda Powertools for TypeScript --- -A suite of utilities for AWS Lambda functions running on the Node.js runtime, to ease adopting best practices such as tracing, structured logging, custom metrics, [**and more**](#features). +Powertools is a developer toolkit to implement Serverless [best practices and increase developer velocity](#features). You can use Powertools in both TypeScript and JavaScript code bases. @@ -308,4 +308,4 @@ These are our core principles to guide our decision making. * **We strive for backwards compatibility**. New features and changes should keep backwards compatibility. If a breaking change cannot be avoided, the deprecation and migration process should be clearly defined. * **We work backwards from the community**. We aim to strike a balance of what would work best for 80% of customers. Emerging practices are considered and discussed via Requests for Comment (RFCs) * **Progressive**. Utilities are designed to be incrementally adoptable for customers at any stage of their Serverless journey. They follow language idioms and their community’s common practices. - \ No newline at end of file + diff --git a/packages/commons/README.md b/packages/commons/README.md index 184439883a..c319faf205 100644 --- a/packages/commons/README.md +++ b/packages/commons/README.md @@ -1,6 +1,6 @@ # AWS Lambda Powertools for TypeScript -A suite of utilities for AWS Lambda functions to ease the adoption of best practices such as tracing, structured logging, custom metrics, and more. +Powertools is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#features). You can use the library in both TypeScript and JavaScript code bases. @@ -82,4 +82,4 @@ Credits for the Lambda Powertools idea go to [DAZN](https://github.com/getndazn) ## License -This library is licensed under the MIT-0 License. See the LICENSE file. \ No newline at end of file +This library is licensed under the MIT-0 License. See the LICENSE file. diff --git a/packages/idempotency/README.md b/packages/idempotency/README.md index 848588fe31..98355a19e5 100644 --- a/packages/idempotency/README.md +++ b/packages/idempotency/README.md @@ -1,6 +1,6 @@ # AWS Lambda Powertools for TypeScript -A suite of utilities for AWS Lambda functions to ease the adoption of best practices such as tracing, structured logging, custom metrics, and more. +Powertools is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#features). You can use the library in both TypeScript and JavaScript code bases. @@ -80,4 +80,4 @@ Credits for the Lambda Powertools idea go to [DAZN](https://github.com/getndazn) ## License -This library is licensed under the MIT-0 License. See the LICENSE file. \ No newline at end of file +This library is licensed under the MIT-0 License. See the LICENSE file. diff --git a/packages/logger/README.md b/packages/logger/README.md index 80f5122041..972bd4af2f 100644 --- a/packages/logger/README.md +++ b/packages/logger/README.md @@ -1,6 +1,6 @@ # AWS Lambda Powertools for TypeScript -A suite of utilities for AWS Lambda functions to ease the adoption of best practices such as tracing, structured logging, custom metrics, and more. +Powertools is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#features). You can use the library in both TypeScript and JavaScript code bases. @@ -81,4 +81,4 @@ Credits for the Lambda Powertools idea go to [DAZN](https://github.com/getndazn) ## License -This library is licensed under the MIT-0 License. See the LICENSE file. \ No newline at end of file +This library is licensed under the MIT-0 License. See the LICENSE file. diff --git a/packages/metrics/README.md b/packages/metrics/README.md index a148d17c99..8344b0262c 100644 --- a/packages/metrics/README.md +++ b/packages/metrics/README.md @@ -1,6 +1,6 @@ # AWS Lambda Powertools for TypeScript -A suite of utilities for AWS Lambda functions to ease the adoption of best practices such as tracing, structured logging, custom metrics, and more. +Powertools is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#features). You can use the library in both TypeScript and JavaScript code bases. diff --git a/packages/tracer/README.md b/packages/tracer/README.md index a148d17c99..8344b0262c 100644 --- a/packages/tracer/README.md +++ b/packages/tracer/README.md @@ -1,6 +1,6 @@ # AWS Lambda Powertools for TypeScript -A suite of utilities for AWS Lambda functions to ease the adoption of best practices such as tracing, structured logging, custom metrics, and more. +Powertools is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#features). You can use the library in both TypeScript and JavaScript code bases. From fa236b94b6532685588edd2f440c911dc6de56ab Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Fri, 17 Feb 2023 17:06:29 +0100 Subject: [PATCH 27/56] tests(parameters): integration tests for `SSMProvider` (#1257) * chore: update cdkAspect to include SSM * tests: SSMProvider class usage * chore: removed unused import * tests: completed tests * tests: fixed typos * chore: added missing effect in cdkAspect * chore: reduced scope of ssmSecureString custom resource * refactor: options setting logic --- packages/parameters/src/ssm/SSMProvider.ts | 38 +- packages/parameters/src/ssm/index.ts | 3 +- .../tests/e2e/dynamoDBProvider.class.test.ts | 8 +- .../ssmProvider.class.test.functionCode.ts | 183 ++++++++++ .../tests/e2e/ssmProvider.class.test.ts | 335 ++++++++++++++++++ .../tests/helpers/cdkAspectGrantAccess.ts | 33 +- .../tests/helpers/parametersUtils.ts | 80 ++++- .../tests/helpers/ssmSecureStringCdk.ts | 54 +++ 8 files changed, 696 insertions(+), 38 deletions(-) create mode 100644 packages/parameters/tests/e2e/ssmProvider.class.test.functionCode.ts create mode 100644 packages/parameters/tests/e2e/ssmProvider.class.test.ts create mode 100644 packages/parameters/tests/helpers/ssmSecureStringCdk.ts diff --git a/packages/parameters/src/ssm/SSMProvider.ts b/packages/parameters/src/ssm/SSMProvider.ts index 39cc0ee569..dd45070162 100644 --- a/packages/parameters/src/ssm/SSMProvider.ts +++ b/packages/parameters/src/ssm/SSMProvider.ts @@ -144,14 +144,11 @@ class SSMProvider extends BaseProvider { options?: SSMGetOptionsInterface ): Promise { const sdkOptions: GetParameterCommandInput = { + ...(options?.sdkOptions || {}), Name: name, }; - if (options) { - if (options.hasOwnProperty('decrypt')) sdkOptions.WithDecryption = options.decrypt; - if (options.hasOwnProperty('sdkOptions')) { - Object.assign(sdkOptions, options.sdkOptions); - } - } + sdkOptions.WithDecryption = options?.decrypt !== undefined ? + options.decrypt : sdkOptions.WithDecryption; const result = await this.client.send(new GetParameterCommand(sdkOptions)); return result.Parameter?.Value; @@ -162,21 +159,18 @@ class SSMProvider extends BaseProvider { options?: SSMGetMultipleOptionsInterface ): Promise> { const sdkOptions: GetParametersByPathCommandInput = { + ...(options?.sdkOptions || {}), Path: path, }; const paginationOptions: PaginationConfiguration = { client: this.client }; - if (options) { - if (options.hasOwnProperty('decrypt')) sdkOptions.WithDecryption = options.decrypt; - if (options.hasOwnProperty('recursive')) sdkOptions.Recursive = options.recursive; - if (options.hasOwnProperty('sdkOptions')) { - Object.assign(sdkOptions, options.sdkOptions); - if (sdkOptions.MaxResults) { - paginationOptions.pageSize = sdkOptions.MaxResults; - } - } - } + sdkOptions.WithDecryption = options?.decrypt !== undefined ? + options.decrypt : sdkOptions.WithDecryption; + sdkOptions.Recursive = options?.recursive !== undefined ? + options.recursive : sdkOptions.Recursive; + paginationOptions.pageSize = sdkOptions.MaxResults !== undefined ? + sdkOptions.MaxResults : undefined; const parameters: Record = {}; for await (const page of paginateGetParametersByPath(paginationOptions, sdkOptions)) { @@ -389,13 +383,11 @@ class SSMProvider extends BaseProvider { const overrides = parameterOptions; overrides.transform = overrides.transform || configs.transform; - if (!overrides.hasOwnProperty('decrypt')) { - overrides.decrypt = configs.decrypt; - } - if (!overrides.hasOwnProperty('maxAge')) { - overrides.maxAge = configs.maxAge; - } - + overrides.decrypt = overrides.decrypt !== undefined ? + overrides.decrypt : configs.decrypt; + overrides.maxAge = overrides.maxAge !== undefined ? + overrides.maxAge : configs.maxAge; + if (overrides.decrypt) { parametersToDecrypt[parameterName] = overrides; } else { diff --git a/packages/parameters/src/ssm/index.ts b/packages/parameters/src/ssm/index.ts index 329d3d3f3b..e275be6d78 100644 --- a/packages/parameters/src/ssm/index.ts +++ b/packages/parameters/src/ssm/index.ts @@ -1,5 +1,4 @@ export * from './SSMProvider'; export * from './getParameter'; export * from './getParameters'; -export * from './getParametersByName'; -export * from '../types/SSMProvider'; \ No newline at end of file +export * from './getParametersByName'; \ No newline at end of file diff --git a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts index 890d71cd8f..ec468cd2b5 100644 --- a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts +++ b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts @@ -4,7 +4,6 @@ * @group e2e/parameters/dynamodb/class */ import path from 'path'; -import { Tracing } from 'aws-cdk-lib/aws-lambda'; import { AttributeType } from 'aws-cdk-lib/aws-dynamodb'; import { App, Stack, Aspects } from 'aws-cdk-lib'; import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb'; @@ -138,10 +137,9 @@ describe(`parameters E2E tests (dynamoDBProvider) for runtime: ${runtime}`, () = // Create a stack with a Lambda function stack = createStackWithLambdaFunction({ app: integTestApp, - stackName: stackName, - functionName: functionName, + stackName, + functionName, functionEntry: path.join(__dirname, lambdaFunctionCodeFile), - tracing: Tracing.ACTIVE, environment: { UUID: uuid, @@ -154,7 +152,7 @@ describe(`parameters E2E tests (dynamoDBProvider) for runtime: ${runtime}`, () = SORT_ATTR: sortAttr, VALUE_ATTR: valueAttr, }, - runtime: runtime, + runtime, }); // Create the DynamoDB tables diff --git a/packages/parameters/tests/e2e/ssmProvider.class.test.functionCode.ts b/packages/parameters/tests/e2e/ssmProvider.class.test.functionCode.ts new file mode 100644 index 0000000000..4f0280f0a8 --- /dev/null +++ b/packages/parameters/tests/e2e/ssmProvider.class.test.functionCode.ts @@ -0,0 +1,183 @@ +import { Context } from 'aws-lambda'; +import { + SSMProvider, +} from '../../src/ssm'; +import { + SSMGetOptionsInterface, + SSMGetMultipleOptionsInterface, + SSMGetParametersByNameOptionsInterface +} from '../../src/types'; +import { TinyLogger } from '../helpers/tinyLogger'; +import { middleware } from '../helpers/sdkMiddlewareRequestCounter'; +import { SSMClient } from '@aws-sdk/client-ssm'; + +// We use a custom logger to log pure JSON objects to stdout +const logger = new TinyLogger(); + +const defaultProvider = new SSMProvider(); +// Provider test 8, 9 +const customClient = new SSMClient({}); +customClient.middlewareStack.use(middleware); +const providerWithMiddleware = new SSMProvider({ + awsSdkV3Client: customClient +}); + +const paramA = process.env.PARAM_A ?? 'my-param'; +const paramB = process.env.PARAM_B ?? 'my-param'; +const paramEncryptedA = process.env.PARAM_ENCRYPTED_A ?? 'my-encrypted-param'; +const paramEncryptedB = process.env.PARAM_ENCRYPTED_B ?? 'my-encrypted-param'; + +// Use provider specified, or default to main one & return it with cache cleared +const resolveProvider = (provider?: SSMProvider): SSMProvider => { + const resolvedProvider = provider ? provider : defaultProvider; + resolvedProvider.clearCache(); + + return resolvedProvider; +}; + +// Helper function to call get() and log the result +const _call_get = async ( + paramName: string, + testName: string, + options?: SSMGetOptionsInterface, + provider?: SSMProvider +): Promise => { + try { + const currentProvider = resolveProvider(provider); + + const parameterValue = await currentProvider.get(paramName, options); + logger.log({ + test: testName, + value: parameterValue + }); + } catch (err) { + logger.log({ + test: testName, + error: err.message + }); + } +}; + +// Helper function to call getMultiple() and log the result +const _call_get_multiple = async ( + paramPath: string, + testName: string, + options?: SSMGetMultipleOptionsInterface, + provider?: SSMProvider +): Promise => { + try { + const currentProvider = resolveProvider(provider); + + const parameterValues = await currentProvider.getMultiple( + paramPath, + options + ); + logger.log({ + test: testName, + value: parameterValues + }); + } catch (err) { + logger.log({ + test: testName, + error: err.message + }); + } +}; + +// Helper function to call getParametersByName() and log the result +const _call_get_parameters_by_name = async ( + params: Record, + testName: string, + options?: SSMGetParametersByNameOptionsInterface, + provider?: SSMProvider +): Promise => { + try { + const currentProvider = resolveProvider(provider); + + const parameterValues = await currentProvider.getParametersByName(params, options); + logger.log({ + test: testName, + value: parameterValues + }); + } catch (err) { + logger.log({ + test: testName, + error: err.message + }); + } +}; + +export const handler = async (_event: unknown, _context: Context): Promise => { + // Test 1 - get a single parameter by name with default options + await _call_get(paramA, 'get'); + + // Test 2 - get a single parameter by name with decrypt + await _call_get(paramEncryptedA, 'get-decrypt', { decrypt: true }); + + // Test 3 - get multiple parameters by path with default options + // Get path (/param/get) + const parameterPath = paramA.substring(0, paramA.lastIndexOf('/')); + await _call_get_multiple(parameterPath, 'get-multiple'); + + // Test 4 - get multiple parameters by path recursively (aka. get all parameters under a path recursively) + // Get parameters root (i.e. from /param/get/a & /param/get/b to /param) + const parameterRoot = paramA.substring( + 0, + paramA.substring(1, paramA.length).indexOf('/') + 1 + ); + await _call_get_multiple(parameterRoot, 'get-multiple-recursive', { recursive: true }); + + // Test 5 - get multiple parameters by path with decrypt + // Get parameters path (i.e. from /param/get/a & /param/get/b to /param/get) + const parameterPathDecrypt = paramEncryptedA.substring(0, paramEncryptedA.lastIndexOf('/')); + await _call_get_multiple(parameterPathDecrypt, 'get-multiple-decrypt', { decrypt: true }); + + // Test 6 - get multiple parameters by name with default options + await _call_get_parameters_by_name({ + [paramA]: {}, + [paramB]: {}, + }, 'get-multiple-by-name'); + + // Test 7 - get multiple parameters by name, some of them encrypted and some not + await _call_get_parameters_by_name({ + [paramA]: {}, + [paramEncryptedA]: { decrypt: true }, + [paramEncryptedB]: { decrypt: true }, + }, 'get-multiple-by-name-mixed-decrypt'); + + // Test 8 + // get parameter twice with middleware, which counts the number of requests, we check later if we only called SSM API once + try { + providerWithMiddleware.clearCache(); + middleware.counter = 0; + await providerWithMiddleware.get(paramA); + await providerWithMiddleware.get(paramA); + logger.log({ + test: 'get-cached', + value: middleware.counter // should be 1 + }); + } catch (err) { + logger.log({ + test: 'get-cached', + error: err.message + }); + } + + // Test 9 + // get parameter twice, but force fetch 2nd time, we count number of SDK requests and check that we made two API calls + try { + providerWithMiddleware.clearCache(); + middleware.counter = 0; + await providerWithMiddleware.get(paramA); + await providerWithMiddleware.get(paramA, { forceFetch: true }); + logger.log({ + test: 'get-forced', + value: middleware.counter // should be 2 + }); + } catch (err) { + logger.log({ + test: 'get-forced', + error: err.message + }); + } +}; \ No newline at end of file diff --git a/packages/parameters/tests/e2e/ssmProvider.class.test.ts b/packages/parameters/tests/e2e/ssmProvider.class.test.ts new file mode 100644 index 0000000000..51701649e5 --- /dev/null +++ b/packages/parameters/tests/e2e/ssmProvider.class.test.ts @@ -0,0 +1,335 @@ +/** + * Test SSMProvider class + * + * @group e2e/parameters/ssm/class + */ +import path from 'path'; +import { App, Stack, Aspects } from 'aws-cdk-lib'; +import { StringParameter } from 'aws-cdk-lib/aws-ssm'; +import { v4 } from 'uuid'; +import { + generateUniqueName, + isValidRuntimeKey, + createStackWithLambdaFunction, + invokeFunction, +} from '../../../commons/tests/utils/e2eUtils'; +import { InvocationLogs } from '../../../commons/tests/utils/InvocationLogs'; +import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; +import { ResourceAccessGranter } from '../helpers/cdkAspectGrantAccess'; +import { + RESOURCE_NAME_PREFIX, + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT +} from './constants'; +import { + createSecureStringProvider, + createSSMSecureString +} from '../helpers/parametersUtils'; + +const runtime: string = process.env.RUNTIME || 'nodejs18x'; + +if (!isValidRuntimeKey(runtime)) { + throw new Error(`Invalid runtime key value: ${runtime}`); +} + +const uuid = v4(); +const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'ssmProvider'); +const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'ssmProvider'); +const lambdaFunctionCodeFile = 'ssmProvider.class.test.functionCode.ts'; + +const invocationCount = 1; + +// Parameter names to be used by Parameters in the Lambda function +const paramA = generateUniqueName(`/${RESOURCE_NAME_PREFIX}`, uuid, runtime, 'param/a'); +const paramB = generateUniqueName(`/${RESOURCE_NAME_PREFIX}`, uuid, runtime, 'param/b'); +const paramEncryptedA = generateUniqueName(`/${RESOURCE_NAME_PREFIX}`, uuid, runtime, 'param-encrypted/a'); +const paramEncryptedB = generateUniqueName(`/${RESOURCE_NAME_PREFIX}`, uuid, runtime, 'param-encrypted/b'); + +// Parameters values +const paramAValue = 'foo'; +const paramBValue = 'bar'; +const paramEncryptedAValue = 'foo-encrypted'; +const paramEncryptedBValue = 'bar-encrypted'; + +const integTestApp = new App(); +let stack: Stack; + +/** + * This test suite deploys a CDK stack with a Lambda function and a number of SSM parameters. + * The function code uses the Parameters utility to retrieve the SSM parameters. + * It then logs the values to CloudWatch Logs as JSON objects. + * + * Once the stack is deployed, the Lambda function is invoked and the CloudWatch Logs are retrieved. + * The logs are then parsed and the values are checked against the expected values for each test case. + * + * The parameters created are: + * - Name: param/a - Value: foo + * - Name: param/b - Value: bar + * - Name: param-encrypted/a - Value: foo-encrypted + * - Name: param-encrypted/b - Value: bar-encrypted + * + * These parameters allow to retrieve one or more parameters both by name and by path, as well as + * mixing encrypted and unencrypted parameters. + * + * The tests are: + * + * Test 1 + * get a single parameter by name with default options + * + * Test 2 + * get a single parameter by name with decrypt + * + * Test 3 + * get multiple parameters by path with default options + * + * Test 4 + * get multiple parameters by path recursively (aka. get all parameters under a path recursively) + * i.e. given /param, retrieve /param/get/a and /param/get/b (note path depth) + * + * Test 5 + * get multiple parameters by path with decrypt + * + * Test 6 + * get multiple parameters by name with default options + * + * Test 7 + * get multiple parameters by name, some of them encrypted and some not + * + * Test 8 + * get parameter twice with middleware, which counts the number of requests, + * we check later if we only called SSM API once + * + * Test 9 + * get parameter twice, but force fetch 2nd time, we count number of SDK requests and + * check that we made two API calls + */ +describe(`parameters E2E tests (ssmProvider) for runtime: ${runtime}`, () => { + + let invocationLogs: InvocationLogs[]; + + beforeAll(async () => { + // Create a stack with a Lambda function + stack = createStackWithLambdaFunction({ + app: integTestApp, + stackName, + functionName, + functionEntry: path.join(__dirname, lambdaFunctionCodeFile), + environment: { + UUID: uuid, + + // Values(s) to be used by Parameters in the Lambda function + PARAM_A: paramA, + PARAM_B: paramB, + PARAM_ENCRYPTED_A: paramEncryptedA, + PARAM_ENCRYPTED_B: paramEncryptedB, + }, + runtime, + }); + + // Create Custom Resource provider: + // will be used to create some SSM parameters not supported by CDK + const provider = createSecureStringProvider({ + stack, + parametersPrefix: `${RESOURCE_NAME_PREFIX}-${runtime}-${uuid.substring(0,5)}` + }); + + // Create SSM parameters + const parameterGetA = new StringParameter(stack, 'Param-a', { + parameterName: paramA, + stringValue: paramAValue, + }); + const parameterGetB = new StringParameter(stack, 'Param-b', { + parameterName: paramB, + stringValue: paramBValue, + }); + + const parameterEncryptedA = createSSMSecureString({ + stack, + provider, + id: 'Param-encrypted-a', + name: paramEncryptedA, + value: paramEncryptedAValue, + }); + + const parameterEncryptedB = createSSMSecureString({ + stack, + provider, + id: 'Param-encrypted-b', + name: paramEncryptedB, + value: paramEncryptedBValue, + }); + + // Give the Lambda function access to the SSM parameters + Aspects.of(stack).add(new ResourceAccessGranter([ + parameterGetA, + parameterGetB, + parameterEncryptedA, + parameterEncryptedB, + ])); + + // Deploy the stack + await deployStack(integTestApp, stack); + + // and invoke the Lambda function + invocationLogs = await invokeFunction(functionName, invocationCount, 'SEQUENTIAL'); + + }, SETUP_TIMEOUT); + + describe('SSMProvider usage', () => { + + // Test 1 - get a single parameter by name with default options + it('should retrieve a single parameter', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[0]); + + expect(testLog).toStrictEqual({ + test: 'get', + value: paramAValue + }); + + }, TEST_CASE_TIMEOUT); + + // Test 2 - get a single parameter by name with decrypt + it('should retrieve a single parameter with decryption', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[1]); + + expect(testLog).toStrictEqual({ + test: 'get-decrypt', + value: paramEncryptedAValue + }); + + }, TEST_CASE_TIMEOUT); + + // Test 3 - get multiple parameters by path with default options + it('should retrieve multiple parameters', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[2]); + const expectedParameterNameA = paramA.substring(paramA.lastIndexOf('/') + 1); + const expectedParameterNameB = paramB.substring(paramB.lastIndexOf('/') + 1); + + expect(testLog).toStrictEqual({ + test: 'get-multiple', + value: { + [expectedParameterNameA]: paramAValue, + [expectedParameterNameB]: paramBValue, + } + }); + + }, TEST_CASE_TIMEOUT); + + // Test 4 - get multiple parameters by path recursively + // (aka. get all parameters under a path recursively) i.e. + // given /param, retrieve /param/get/a and /param/get/b (note path depth) + it('should retrieve multiple parameters recursively', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[3]); + const expectedParameterNameA = paramA.substring(paramA.lastIndexOf('/') + 1); + const expectedParameterNameB = paramB.substring(paramB.lastIndexOf('/') + 1); + + expect(testLog).toStrictEqual({ + test: 'get-multiple-recursive', + value: { + [expectedParameterNameA]: paramAValue, + [expectedParameterNameB]: paramBValue, + } + }); + + }, TEST_CASE_TIMEOUT); + + it('should retrieve multiple parameters with decryption', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[4]); + const expectedParameterNameA = paramEncryptedA.substring( + paramEncryptedA.lastIndexOf('/') + 1 + ); + const expectedParameterNameB = paramEncryptedB.substring( + paramEncryptedB.lastIndexOf('/') + 1 + ); + + expect(testLog).toStrictEqual({ + test: 'get-multiple-decrypt', + value: { + [expectedParameterNameA]: paramEncryptedAValue, + [expectedParameterNameB]: paramEncryptedBValue, + } + }); + + }, TEST_CASE_TIMEOUT); + + // Test 6 - get multiple parameters by name with default options + it('should retrieve multiple parameters by name', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[5]); + + expect(testLog).toStrictEqual({ + test: 'get-multiple-by-name', + value: { + [paramA]: paramAValue, + [paramB]: paramBValue, + } + }); + + }, TEST_CASE_TIMEOUT); + + // Test 7 - get multiple parameters by name, some of them encrypted and some not + it('should retrieve multiple parameters by name with mixed decryption', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[6]); + + expect(testLog).toStrictEqual({ + test: 'get-multiple-by-name-mixed-decrypt', + value: { + [paramEncryptedA]: paramEncryptedAValue, + [paramEncryptedB]: paramEncryptedBValue, + [paramA]: paramAValue, + } + }); + + }, TEST_CASE_TIMEOUT); + + // Test 8 - get parameter twice with middleware, which counts the number + // of requests, we check later if we only called SSM API once + it('should retrieve single parameter cached', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[7]); + + expect(testLog).toStrictEqual({ + test: 'get-cached', + value: 1 + }); + + }, TEST_CASE_TIMEOUT); + + // Test 9 - get parameter twice, but force fetch 2nd time, + // we count number of SDK requests and check that we made two API calls + it('should retrieve single parameter twice without caching', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[8]); + + expect(testLog).toStrictEqual({ + test: 'get-forced', + value: 2 + }); + + }, TEST_CASE_TIMEOUT); + + }); + + afterAll(async () => { + if (!process.env.DISABLE_TEARDOWN) { + await destroyStack(integTestApp, stack); + } + }, TEARDOWN_TIMEOUT); + +}); \ No newline at end of file diff --git a/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts b/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts index 57d99a393b..f75848acd5 100644 --- a/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts +++ b/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts @@ -2,7 +2,12 @@ import { IAspect } from 'aws-cdk-lib'; import { IConstruct } from 'constructs'; import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; import { Table } from 'aws-cdk-lib/aws-dynamodb'; +import { PolicyStatement, Effect } from 'aws-cdk-lib/aws-iam'; import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; +import { StringParameter, IStringParameter } from 'aws-cdk-lib/aws-ssm'; + +const isStringParameterGeneric = (parameter: IConstruct): parameter is StringParameter | IStringParameter => + parameter.hasOwnProperty('parameterArn'); /** * An aspect that grants access to resources to a Lambda function. @@ -15,13 +20,13 @@ import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; * This aspect allows us to grant access to specific resources to all Lambda functions in a stack * after the stack tree has been generated and before the stack is deployed. This aspect is * used to grant access to different resource types (DynamoDB tables, SSM parameters, etc.). - * - * @see {@link https://docs.aws.amazon.com/cdk/v2/guide/aspects.html|CDK Docs - Aspects} + * + * @see {@link https://docs.aws.amazon.com/cdk/v2/guide/aspects.html CDK Docs - Aspects} */ export class ResourceAccessGranter implements IAspect { - private readonly resources: Table[] | Secret[]; + private readonly resources: Table[] | Secret[] | StringParameter[] | IStringParameter[]; - public constructor(resources: Table[] | Secret[]) { + public constructor(resources: Table[] | Secret[] | StringParameter[] | IStringParameter[]) { this.resources = resources; } @@ -30,12 +35,28 @@ export class ResourceAccessGranter implements IAspect { if (node instanceof NodejsFunction) { // Grant access to the resources - this.resources.forEach((resource: Table | Secret) => { + this.resources.forEach((resource: Table | Secret | StringParameter | IStringParameter) => { if (resource instanceof Table) { resource.grantReadData(node); - } else if (resource instanceof Secret) { + } else if ( + resource instanceof Secret + ) { + resource.grantRead(node); + } else if (isStringParameterGeneric(resource)) { resource.grantRead(node); + // Grant access also to the path of the parameter + node.addToRolePolicy( + new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'ssm:GetParametersByPath', + ], + resources: [ + resource.parameterArn.split(':').slice(0, -1).join(':'), + ], + }), + ); } }); diff --git a/packages/parameters/tests/helpers/parametersUtils.ts b/packages/parameters/tests/helpers/parametersUtils.ts index 3768a1fe53..e503fddd2b 100644 --- a/packages/parameters/tests/helpers/parametersUtils.ts +++ b/packages/parameters/tests/helpers/parametersUtils.ts @@ -1,4 +1,10 @@ -import { Stack, RemovalPolicy } from 'aws-cdk-lib'; +import { Stack, RemovalPolicy, CustomResource, Duration } from 'aws-cdk-lib'; +import { Provider } from 'aws-cdk-lib/custom-resources'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { Runtime } from 'aws-cdk-lib/aws-lambda'; +import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; +import { StringParameter, IStringParameter } from 'aws-cdk-lib/aws-ssm'; import { Table, TableProps, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; export type CreateDynamoDBTableOptions = { @@ -17,6 +23,76 @@ const createDynamoDBTable = (options: CreateDynamoDBTableOptions): Table => { return new Table(stack, id, props); }; +export type CreateSecureStringProviderOptions = { + stack: Stack + parametersPrefix: string +}; + +const createSecureStringProvider = (options: CreateSecureStringProviderOptions): Provider => { + const { stack, parametersPrefix } = options; + + const ssmSecureStringHandlerFn = new NodejsFunction( + stack, + 'ssm-securestring-handler', + { + entry: 'tests/helpers/ssmSecureStringCdk.ts', + handler: 'handler', + bundling: { + minify: true, + sourceMap: true, + target: 'es2020', + externalModules: [], + }, + runtime: Runtime.NODEJS_18_X, + timeout: Duration.seconds(15), + }); + ssmSecureStringHandlerFn.addToRolePolicy( + new PolicyStatement({ + actions: [ + 'ssm:PutParameter', + 'ssm:DeleteParameter', + ], + resources: [ + `arn:aws:ssm:${stack.region}:${stack.account}:parameter/${parametersPrefix}*`, + ], + }), + ); + + return new Provider(stack, 'ssm-secure-string-provider', { + onEventHandler: ssmSecureStringHandlerFn, + logRetention: RetentionDays.ONE_DAY, + }); +}; + +export type CreateSSMSecureStringOptions = { + stack: Stack + provider: Provider + id: string + name: string + value: string +}; + +const createSSMSecureString = (options: CreateSSMSecureStringOptions): IStringParameter => { + const { stack, provider, id, name, value } = options; + + new CustomResource(stack, `custom-${id}`, { + serviceToken: provider.serviceToken, + properties: { + Name: name, + Value: value, + }, + }); + + const param = StringParameter.fromSecureStringParameterAttributes(stack, id, { + parameterName: name, + }); + param.node.addDependency(provider); + + return param; +}; + export { - createDynamoDBTable + createDynamoDBTable, + createSSMSecureString, + createSecureStringProvider, }; \ No newline at end of file diff --git a/packages/parameters/tests/helpers/ssmSecureStringCdk.ts b/packages/parameters/tests/helpers/ssmSecureStringCdk.ts new file mode 100644 index 0000000000..2076c25c47 --- /dev/null +++ b/packages/parameters/tests/helpers/ssmSecureStringCdk.ts @@ -0,0 +1,54 @@ +import { + Context, + CloudFormationCustomResourceEvent +} from 'aws-lambda'; +import { + SSMClient, + PutParameterCommand, + DeleteParameterCommand +} from '@aws-sdk/client-ssm'; + +const client = new SSMClient({}); + +/** + * Create a new SSM SecureString parameter. + */ +const createResource = async (event: CloudFormationCustomResourceEvent): Promise => { + const { ResourceProperties } = event; + const { Name, Value } = ResourceProperties; + + await client.send(new PutParameterCommand({ + Name, + Value, + Type: 'SecureString', + })); +}; + +/** + * Delete an existing SSM parameter. + */ +const deleteResource = async (event: CloudFormationCustomResourceEvent): Promise => { + const { ResourceProperties } = event; + const { Name } = ResourceProperties; + + await client.send(new DeleteParameterCommand({ + Name, + })); +}; + +/** + * Custom resource handler for creating and deleting SSM SecureString parameters. This is used by + * CDK to create and delete the SSM SecureString parameters that are used to test the SSMProvider. + * + * We need a custom resource because CDK does not support creating SSM SecureString parameters. + */ +export const handler = async (event: CloudFormationCustomResourceEvent, _context: Context): Promise => { + if (event.RequestType === 'Create') { + await createResource(event); + } else if (event.RequestType === 'Delete') { + await deleteResource(event); + } else { + console.error('Unknown or unsupported request type', event); + throw new Error('Unknown or unsupported request type'); + } +}; \ No newline at end of file From 9942f07f2b81261875172ef2e891539cffc78d8b Mon Sep 17 00:00:00 2001 From: Niko Achilles Kokkinos Date: Mon, 20 Feb 2023 10:59:27 +0200 Subject: [PATCH 28/56] chore: remove unused extensions from Gitpod/Codespaces (#1312) --- .devcontainer/devcontainer.json | 1 - .gitpod.yml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bd207514cb..b49ea2d8a5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -22,7 +22,6 @@ "firsttris.vscode-jest-runner", "visualstudioexptteam.vscodeintellicode", "amazonwebservices.aws-toolkit-vscode", - "ms-vscode.vscode-typescript-tslint-plugin", "ms-azuretools.vscode-docker" ], // Use 'postCreateCommand' to run commands after the container is created. diff --git a/.gitpod.yml b/.gitpod.yml index 06cac6a0bb..33253c2032 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -17,5 +17,4 @@ vscode: - dbaeumer.vscode-eslint - esbenp.prettier-vscode - firsttris.vscode-jest-runner - - ms-azuretools.vscode-docker - - ms-vscode.vscode-typescript-tslint-plugin + - ms-azuretools.vscode-docker \ No newline at end of file From c672c40bc340be94941212a2e3037fd71ad50294 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 20 Feb 2023 16:11:53 +0100 Subject: [PATCH 29/56] chore(layers): moved `layer-publisher` into npm workspace (#1292) * chore: moved layer-publisher into npm workspace * chore: fixed leftover command * refactor: layer publisher * refactor: layer publisher * refactor: layer publisher * refactor: layer publisher * refactor: layer publisher * refactor: layer publisher * refactor: layer publisher * tests: remove snapshots & added resource testing * chore: extracted script * chore: update path in cdk.json --- .github/scripts/setup_tmp_layer_files.sh | 13 + .github/workflows/publish_layer.yaml | 35 +- ...sable-run-linting-check-and-unit-tests.yml | 25 +- .../workflows/reusable_deploy_layer_stack.yml | 35 +- .github/workflows/run-e2e-tests.yml | 9 +- .gitignore | 5 +- .husky/pre-commit | 4 +- .husky/pre-push | 8 +- layer-publisher/jest.config.js | 8 - layer-publisher/package-lock.json | 11347 ---------------- layer-publisher/package.json | 42 - .../src/powertools-typescript-layer.ts | 63 - .../tests/e2e/happy-case.test.lambda.ts | 40 - layer-publisher/tests/e2e/happy-case.test.ts | 88 - .../layer-publisher.test.ts.snap | 94 - .../tests/unit/layer-publisher.test.ts | 24 - {layer-publisher => layers}/.eslintrc.js | 0 {layer-publisher => layers}/.gitignore | 0 {layer-publisher => layers}/.npmignore | 0 {layer-publisher => layers}/CHANGELOG.md | 0 {layer-publisher => layers}/README.md | 0 .../bin/layers.ts | 7 +- {layer-publisher => layers}/cdk.json | 2 +- layers/jest.config.js | 45 + layers/package.json | 35 + .../src/layer-publisher-stack.ts | 34 +- layers/tests/e2e/constants.ts | 5 + .../layerPublisher.class.test.functionCode.ts | 46 + layers/tests/e2e/layerPublisher.test.ts | 147 + .../helpers/populateEnvironmentVariables.ts | 7 + layers/tests/unit/layer-publisher.test.ts | 53 + {layer-publisher => layers}/tsconfig.es.json | 0 {layer-publisher => layers}/tsconfig.json | 0 package-lock.json | 75 +- package.json | 7 +- packages/commons/tests/utils/e2eUtils.ts | 13 +- 36 files changed, 525 insertions(+), 11791 deletions(-) create mode 100644 .github/scripts/setup_tmp_layer_files.sh delete mode 100644 layer-publisher/jest.config.js delete mode 100644 layer-publisher/package-lock.json delete mode 100644 layer-publisher/package.json delete mode 100644 layer-publisher/src/powertools-typescript-layer.ts delete mode 100644 layer-publisher/tests/e2e/happy-case.test.lambda.ts delete mode 100644 layer-publisher/tests/e2e/happy-case.test.ts delete mode 100644 layer-publisher/tests/unit/__snapshots__/layer-publisher.test.ts.snap delete mode 100644 layer-publisher/tests/unit/layer-publisher.test.ts rename {layer-publisher => layers}/.eslintrc.js (100%) rename {layer-publisher => layers}/.gitignore (100%) rename {layer-publisher => layers}/.npmignore (100%) rename {layer-publisher => layers}/CHANGELOG.md (100%) rename {layer-publisher => layers}/README.md (100%) rename layer-publisher/bin/layer-publisher.ts => layers/bin/layers.ts (73%) rename {layer-publisher => layers}/cdk.json (92%) create mode 100644 layers/jest.config.js create mode 100644 layers/package.json rename {layer-publisher => layers}/src/layer-publisher-stack.ts (60%) create mode 100644 layers/tests/e2e/constants.ts create mode 100644 layers/tests/e2e/layerPublisher.class.test.functionCode.ts create mode 100644 layers/tests/e2e/layerPublisher.test.ts create mode 100644 layers/tests/helpers/populateEnvironmentVariables.ts create mode 100644 layers/tests/unit/layer-publisher.test.ts rename {layer-publisher => layers}/tsconfig.es.json (100%) rename {layer-publisher => layers}/tsconfig.json (100%) diff --git a/.github/scripts/setup_tmp_layer_files.sh b/.github/scripts/setup_tmp_layer_files.sh new file mode 100644 index 0000000000..77c5875734 --- /dev/null +++ b/.github/scripts/setup_tmp_layer_files.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +rm -rf tmp/nodejs +mkdir -p tmp/nodejs +cd tmp/nodejs +npm init -y +npm i \ + @aws-lambda-powertools/logger@$VERSION \ + @aws-lambda-powertools/metrics@$VERSION \ + @aws-lambda-powertools/tracer@$VERSION +rm -rf node_modules/@types \ + package.json \ + package-lock.json +cd ../.. \ No newline at end of file diff --git a/.github/workflows/publish_layer.yaml b/.github/workflows/publish_layer.yaml index 25142f11b8..0131612aa6 100644 --- a/.github/workflows/publish_layer.yaml +++ b/.github/workflows/publish_layer.yaml @@ -23,9 +23,6 @@ jobs: build-layer: runs-on: ubuntu-latest if: ${{ (github.event.workflow_run.conclusion == 'success') || (github.event_name == 'workflow_dispatch') }} - defaults: - run: - working-directory: ./layer-publisher steps: - name: checkout uses: actions/checkout@v3 @@ -41,22 +38,32 @@ jobs: LATEST_TAG=$(git describe --tag --abbrev=0) RELEASE_TAG_VERSION=${RELEASE_INPUT:-$LATEST_TAG} echo "RELEASE_TAG_VERSION=${RELEASE_TAG_VERSION:1}" >> $GITHUB_ENV - - name: install cdk and deps - run: | - npm install -g aws-cdk@2.55.0 - cdk --version - - name: install deps + - name: Cache node modules + id: cache-node-modules + uses: actions/cache@v3 + with: + path: "./node_modules" + # Use the combo between node version, name, and SHA-256 hash of the lock file as cache key so that + # if one of them changes the cache is invalidated/discarded + key: 18-cache-utilities-node-modules-${{ hashFiles('./package-lock.json') }} + - name: Install dependencies + # We can skip the installation if there was a cache hit + if: steps.cache-node-modules.outputs.cache-hit != 'true' + # See https://github.com/npm/cli/issues/4475 to see why --foreground-scripts + run: npm ci --foreground-scripts + - name: Create layer files run: | - npm ci + export VERSION=$RELEASE_TAG_VERSION + bash .github/scripts/setup_tmp_layer_files.sh - name: CDK build - run: cdk synth --context PowerToolsPackageVersion=$RELEASE_TAG_VERSION -o cdk.out + run: npm run cdk -w layers -- synth --context PowertoolsPackageVersion=$RELEASE_TAG_VERSION -o cdk.out - name: zip output run: zip -r cdk.out.zip cdk.out - name: Archive CDK artifacts uses: actions/upload-artifact@v3 with: - name: cdk-layer-artefact - path: layer-publisher/cdk.out.zip + name: cdk-layer-artifact + path: layers/cdk.out.zip # Deploy layer to all regions in beta account deploy-beta: @@ -65,7 +72,7 @@ jobs: uses: ./.github/workflows/reusable_deploy_layer_stack.yml with: stage: "BETA" - artefact-name: "cdk-layer-artefact" + artifact-name: "cdk-layer-artifact" secrets: target-account-role: ${{ secrets.AWS_LAYERS_BETA_ROLE_ARN }} @@ -76,6 +83,6 @@ jobs: uses: ./.github/workflows/reusable_deploy_layer_stack.yml with: stage: "PROD" - artefact-name: "cdk-layer-artefact" + artifact-name: "cdk-layer-artifact" secrets: target-account-role: ${{ secrets.AWS_LAYERS_PROD_ROLE_ARN }} \ No newline at end of file diff --git a/.github/workflows/reusable-run-linting-check-and-unit-tests.yml b/.github/workflows/reusable-run-linting-check-and-unit-tests.yml index 3bac08a4f4..c0280cd34e 100644 --- a/.github/workflows/reusable-run-linting-check-and-unit-tests.yml +++ b/.github/workflows/reusable-run-linting-check-and-unit-tests.yml @@ -88,9 +88,6 @@ jobs: runs-on: ubuntu-latest env: NODE_ENV: dev - defaults: - run: - working-directory: layer-publisher steps: - name: Checkout code uses: actions/checkout@v3 @@ -103,17 +100,23 @@ jobs: id: cache-node-modules uses: actions/cache@v3 with: - path: "./layer-publisher/node_modules" - # Use the combo between example, name, and SHA-256 hash of the layer-publisher lock files as cache key. - key: cache-layer-publisher-node-modules-${{ hashFiles('./layer-publisher/package-lock.json') }} - - name: Install Layer publisher + path: "./node_modules" + # Use the combo between node version, name, and SHA-256 hash of the lock file as cache key so that + # if one of them changes the cache is invalidated/discarded + key: 18-cache-utilities-node-modules-${{ hashFiles('./package-lock.json') }} + - name: Install dependencies # We can skip the installation if there was a cache hit if: steps.cache-node-modules.outputs.cache-hit != 'true' - run: npm ci + # See https://github.com/npm/cli/issues/4475 to see why --foreground-scripts + run: npm ci --foreground-scripts - name: Run linting - run: npm run lint + run: npm run lint -w layers + - name: Create layer files + run: | + export VERSION=latest + bash .github/scripts/setup_tmp_layer_files.sh - name: Run tests - run: npm t + run: npm run test:unit -w layers check-docs-snippets: runs-on: ubuntu-latest env: @@ -150,4 +153,4 @@ jobs: npm run build -w packages/commons npm run build -w packages/logger & npm run build -w packages/tracer & npm run build -w packages/metrics & npm run build -w packages/parameters & npm run build -w packages/idempotency & npm run build -w docs/snippets - name: Run linting - run: npm run lint -w packages/commons -w packages/logger -w packages/tracer -w packages/metrics -w packages/parameters -w packages/idempotency -w docs/snippets + run: npm run lint -w docs/snippets diff --git a/.github/workflows/reusable_deploy_layer_stack.yml b/.github/workflows/reusable_deploy_layer_stack.yml index ef75a81c8d..0fc9a542fc 100644 --- a/.github/workflows/reusable_deploy_layer_stack.yml +++ b/.github/workflows/reusable_deploy_layer_stack.yml @@ -10,7 +10,7 @@ on: stage: required: true type: string - artefact-name: + artifact-name: required: true type: string secrets: @@ -20,9 +20,6 @@ on: jobs: deploy-cdk-stack: runs-on: ubuntu-latest - defaults: - run: - working-directory: ./layer-publisher strategy: fail-fast: false matrix: @@ -63,19 +60,25 @@ jobs: uses: actions/setup-node@v3 with: node-version: "18" - - name: install cdk and deps - run: | - npm install -g aws-cdk@2.55.0 - cdk --version - - name: install deps - run: | - npm ci + - name: Cache node modules + id: cache-node-modules + uses: actions/cache@v3 + with: + path: "./node_modules" + # Use the combo between node version, name, and SHA-256 hash of the lock file as cache key so that + # if one of them changes the cache is invalidated/discarded + key: 18-cache-utilities-node-modules-${{ hashFiles('./package-lock.json') }} + - name: Install dependencies + # We can skip the installation if there was a cache hit + if: steps.cache-node-modules.outputs.cache-hit != 'true' + # See https://github.com/npm/cli/issues/4475 to see why --foreground-scripts + run: npm ci --foreground-scripts - name: Download artifact uses: actions/download-artifact@v3 with: - name: ${{ inputs.artefact-name }} - path: layer-publisher - - name: unzip artefact + name: ${{ inputs.artifact-name }} + path: layers + - name: Unzip artifact run: unzip cdk.out.zip - - name: CDK Deploy Layer - run: cdk deploy --app cdk.out --context region=${{ matrix.region }} 'LayerPublisherStack' --require-approval never --verbose + - name: Deploy Layer + run: npm run cdk -w layers -- deploy --app cdk.out --context region=${{ matrix.region }} 'LayerPublisherStack' --require-approval never --verbose diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 617ccc4a06..55775c82e1 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -122,8 +122,9 @@ jobs: if: steps.cache-node-modules.outputs.cache-hit == 'true' run: | npm run build -w packages/commons - - name: Run integration test on layers + - name: Create layer files run: | - npm ci --foreground-scripts - RUNTIME=nodejs${{ matrix.version }}.x npm run test:e2e - working-directory: layer-publisher + export VERSION=latest + bash .github/scripts/setup_tmp_layer_files.sh + - name: Run integration test on layers + run: RUNTIME=nodejs${{ matrix.version }}.x npm run test:e2e -w layers diff --git a/.gitignore b/.gitignore index ce7e2e2eb5..489c489c75 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,7 @@ site # SAM Example copies files /examples/sam/src/handlers/* -!/examples/sam/src/handlers/COPY_LAMBDA_FUNCTIONS_HERE \ No newline at end of file +!/examples/sam/src/handlers/COPY_LAMBDA_FUNCTIONS_HERE + +# Layer temp files +tmp \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index 19d7e73c29..7f18baea4f 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -5,6 +5,4 @@ npm run lint-fix -ws cd examples/sam && npm run lint-fix cd ../.. cd examples/cdk && npm run lint-fix -cd ../.. -cd layer-publisher && npm run lint-fix -cd .. +cd ../.. \ No newline at end of file diff --git a/.husky/pre-push b/.husky/pre-push index e9b6f2754c..d975532720 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,4 +1,10 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -npm t -ws \ No newline at end of file +npm t \ + -w packages/commons && \ + -w packages/logger && \ + -w packages/metrics && \ + -w packages/tracer && \ + -w packages/idempotency && \ + -w packages/parameters \ No newline at end of file diff --git a/layer-publisher/jest.config.js b/layer-publisher/jest.config.js deleted file mode 100644 index e3795fc8b4..0000000000 --- a/layer-publisher/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - testEnvironment: 'node', - roots: ['/tests'], - testMatch: ['**/*.test.ts'], - transform: { - '^.+\\.tsx?$': 'ts-jest' - } -}; diff --git a/layer-publisher/package-lock.json b/layer-publisher/package-lock.json deleted file mode 100644 index 8d8402857d..0000000000 --- a/layer-publisher/package-lock.json +++ /dev/null @@ -1,11347 +0,0 @@ -{ - "name": "layer-publisher", - "version": "1.5.1", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "layer-publisher", - "version": "1.5.0", - "dependencies": { - "aws-cdk-lib": "^2.55.0", - "constructs": "^10.1.190", - "if-node-version": "^1.1.1", - "source-map-support": "^0.5.21", - "ts-md5": "^1.3.1" - }, - "bin": { - "layer-publisher": "bin/layer-publisher.js" - }, - "devDependencies": { - "@aws-cdk/cloudformation-diff": "^2.55.0", - "@aws-cdk/cx-api": "^2.55.0", - "@types/jest": "^29.2.4", - "@types/node": "18.11.15", - "@typescript-eslint/eslint-plugin": "^5.46.1", - "@typescript-eslint/parser": "^5.46.1", - "aws-cdk": "^2.55.0", - "eslint": "^8.29.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.26.0", - "jest": "^29.3.1", - "ts-jest": "^29.0.3", - "ts-node": "10.9.1", - "typescript": "4.9.4" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.49", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.49.tgz", - "integrity": "sha512-Qd5bdLlC/sphWQQPNn7etKXWCh+fij7DWxtkIwvhhZ+LM6UEApGLS8sBLBQcRO2ZQdEuNb+zeClUy+3DNojfeg==" - }, - "node_modules/@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==" - }, - "node_modules/@aws-cdk/asset-node-proxy-agent-v5": { - "version": "2.0.38", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.38.tgz", - "integrity": "sha512-BBwAjORhuUkTGO3CxGS5Evcp5n20h9v06Sftn2R1DuSm8zIoUlPsNlI1HUk8XqYuoEI4aD7IKRQBLglv09ciJQ==" - }, - "node_modules/@aws-cdk/cfnspec": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/cfnspec/-/cfnspec-2.55.1.tgz", - "integrity": "sha512-jJCaEV9pTLFft2eYkwmblfxUBQeJSH4KbMteiVQdp6wkYbzoj/sjyFInfKHgtfIOeqjNtPZ74Dhl5fRJsOpBDw==", - "dev": true, - "dependencies": { - "fs-extra": "^9.1.0", - "md5": "^2.3.0" - } - }, - "node_modules/@aws-cdk/cloud-assembly-schema": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-2.55.1.tgz", - "integrity": "sha512-VVVQaqHP2PsNGRwfPsq1RxoOfyC54DO8RH+2nHqCyPIfSmrIH6Ux5Uk7KAURyXKdb3SjKra66bxPOzvXHOaBXg==", - "bundleDependencies": [ - "jsonschema", - "semver" - ], - "dev": true, - "dependencies": { - "jsonschema": "^1.4.1", - "semver": "^7.3.8" - }, - "engines": { - "node": ">= 14.15.0" - } - }, - "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": { - "version": "1.4.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": { - "version": "7.3.8", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/@aws-cdk/cloudformation-diff": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloudformation-diff/-/cloudformation-diff-2.55.1.tgz", - "integrity": "sha512-WgLubFXoQFGZuOkm7YtJx+kS8EN6rX9K6s257qFR7yNGpKBANSPVvWmlWX2qSvg++AkNSxKsuaZi+4V50TNbdA==", - "dev": true, - "dependencies": { - "@aws-cdk/cfnspec": "2.55.1", - "@types/node": "^14.18.34", - "chalk": "^4", - "diff": "^5.1.0", - "fast-deep-equal": "^3.1.3", - "string-width": "^4.2.3", - "table": "^6.8.1" - }, - "engines": { - "node": ">= 14.15.0" - } - }, - "node_modules/@aws-cdk/cloudformation-diff/node_modules/@types/node": { - "version": "14.18.36", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz", - "integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==", - "dev": true - }, - "node_modules/@aws-cdk/cx-api": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-2.55.1.tgz", - "integrity": "sha512-gWQCn/9gaayf2ev0HjCMQleY1gWJZGImLhsUF8kneYnOPCm/r0qpIzy3BxiWiDFLPh9gykDvHQhpH6EG0aXnGA==", - "bundleDependencies": [ - "semver" - ], - "dev": true, - "dependencies": { - "@aws-cdk/cloud-assembly-schema": "2.55.1", - "semver": "^7.3.8" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "@aws-cdk/cloud-assembly-schema": "2.55.1" - } - }, - "node_modules/@aws-cdk/cx-api/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@aws-cdk/cx-api/node_modules/semver": { - "version": "7.3.8", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@aws-cdk/cx-api/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/@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, - "dependencies": { - "@babel/highlight": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.20.10", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz", - "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", - "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator/node_modules/@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, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@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, - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@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, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "dependencies": { - "@babel/types": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", - "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.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, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.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, - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", - "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", - "dev": true, - "dependencies": { - "@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.7", - "@babel/types": "^7.20.7", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.3.1.tgz", - "integrity": "sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.3.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.3.1", - "jest-util": "^29.3.1", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.3.1.tgz", - "integrity": "sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw==", - "dev": true, - "dependencies": { - "@jest/console": "^29.3.1", - "@jest/reporters": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.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.2.0", - "jest-config": "^29.3.1", - "jest-haste-map": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.3.1", - "jest-resolve-dependencies": "^29.3.1", - "jest-runner": "^29.3.1", - "jest-runtime": "^29.3.1", - "jest-snapshot": "^29.3.1", - "jest-util": "^29.3.1", - "jest-validate": "^29.3.1", - "jest-watcher": "^29.3.1", - "micromatch": "^4.0.4", - "pretty-format": "^29.3.1", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.3.1.tgz", - "integrity": "sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "jest-mock": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.3.1.tgz", - "integrity": "sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==", - "dev": true, - "dependencies": { - "expect": "^29.3.1", - "jest-snapshot": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz", - "integrity": "sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.2.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.3.1.tgz", - "integrity": "sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==", - "dev": true, - "dependencies": { - "@jest/types": "^29.3.1", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^29.3.1", - "jest-mock": "^29.3.1", - "jest-util": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.3.1.tgz", - "integrity": "sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.3.1", - "@jest/expect": "^29.3.1", - "@jest/types": "^29.3.1", - "jest-mock": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.3.1.tgz", - "integrity": "sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.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.3.1", - "jest-util": "^29.3.1", - "jest-worker": "^29.3.1", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@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, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.15", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.3.1.tgz", - "integrity": "sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw==", - "dev": true, - "dependencies": { - "@jest/console": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz", - "integrity": "sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.3.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.3.1", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.3.1.tgz", - "integrity": "sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.3.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.3.1", - "jest-regex-util": "^29.2.0", - "jest-util": "^29.3.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz", - "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@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, - "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@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, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@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, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgr/utils": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz", - "integrity": "sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "is-glob": "^4.0.3", - "open": "^8.4.0", - "picocolors": "^1.0.0", - "tiny-glob": "^0.2.9", - "tslib": "^2.4.0" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "node_modules/@types/babel__core": { - "version": "7.1.20", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", - "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.3.0" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.5.tgz", - "integrity": "sha512-H2cSxkKgVmqNHXP7TC2L/WUorrZu8ZigyRywfVzv6EyBlxj39n4C00hjXYQWsbwqgElaj/CiAeSRmk5GoaKTgw==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.11.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.15.tgz", - "integrity": "sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw==", - "dev": true - }, - "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", - "dev": true - }, - "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "dev": true - }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.19", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.19.tgz", - "integrity": "sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz", - "integrity": "sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.48.1", - "@typescript-eslint/type-utils": "5.48.1", - "@typescript-eslint/utils": "5.48.1", - "debug": "^4.3.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.1.tgz", - "integrity": "sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.48.1", - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/typescript-estree": "5.48.1", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz", - "integrity": "sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/visitor-keys": "5.48.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz", - "integrity": "sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "5.48.1", - "@typescript-eslint/utils": "5.48.1", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.1.tgz", - "integrity": "sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz", - "integrity": "sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/visitor-keys": "5.48.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.1.tgz", - "integrity": "sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.48.1", - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/typescript-estree": "5.48.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz", - "integrity": "sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.48.1", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-cdk": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.55.1.tgz", - "integrity": "sha512-M/d4lO7rLMvPedNj7pEQtwyxn7nVLluiHggftXlYHOIeaoMG1QCbH1zTuZtMZZxdRRD4ZiOEHvvPYFU/HMcrPQ==", - "dev": true, - "bin": { - "cdk": "bin/cdk" - }, - "engines": { - "node": ">= 14.15.0" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/aws-cdk-lib": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.55.1.tgz", - "integrity": "sha512-v0MhL6RqazQ2HKj9TXJvXKQcQNIDYeRzcOJ2xB2Usz56xodArc2goLu2P51X5J84xOO4w2AgNaWMCBzd7MbyOQ==", - "bundleDependencies": [ - "@balena/dockerignore", - "case", - "fs-extra", - "ignore", - "jsonschema", - "minimatch", - "punycode", - "semver", - "yaml" - ], - "dependencies": { - "@aws-cdk/asset-awscli-v1": "^2.2.30", - "@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.1", - "jsonschema": "^1.4.1", - "minimatch": "^3.1.2", - "punycode": "^2.1.1", - "semver": "^7.3.8", - "yaml": "1.10.2" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "constructs": "^10.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { - "version": "1.0.2", - "inBundle": true, - "license": "Apache-2.0" - }, - "node_modules/aws-cdk-lib/node_modules/at-least-node": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/balanced-match": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/aws-cdk-lib/node_modules/case": { - "version": "1.6.3", - "inBundle": true, - "license": "(MIT OR GPL-3.0-or-later)", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/concat-map": { - "version": "0.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/fs-extra": { - "version": "9.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/aws-cdk-lib/node_modules/graceful-fs": { - "version": "4.2.10", - "inBundle": true, - "license": "ISC" - }, - "node_modules/aws-cdk-lib/node_modules/ignore": { - "version": "5.2.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/aws-cdk-lib/node_modules/jsonfile": { - "version": "6.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/aws-cdk-lib/node_modules/jsonschema": { - "version": "1.4.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/aws-cdk-lib/node_modules/lru-cache": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/aws-cdk-lib/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/aws-cdk-lib/node_modules/punycode": { - "version": "2.1.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/aws-cdk-lib/node_modules/semver": { - "version": "7.3.8", - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/aws-cdk-lib/node_modules/universalify": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/yallist": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/aws-cdk-lib/node_modules/yaml": { - "version": "1.10.2", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/babel-jest": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", - "integrity": "sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.3.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.2.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz", - "integrity": "sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz", - "integrity": "sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.2.0", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001442", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz", - "integrity": "sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/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, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/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 - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/constructs": { - "version": "10.1.216", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.1.216.tgz", - "integrity": "sha512-Nx/Pzuyy46Bc5LFu3XPSmTKNcjN4ChQ/R3syOb7bJLfQ5VhJn+o0aCKBVwnbuF1av0YCG6IaDz8fdtWhjefJjA==", - "engines": { - "node": ">= 14.17.0" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/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, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/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 - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", - "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.4", - "is-array-buffer": "^3.0.1", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz", - "integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==", - "dev": true, - "dependencies": { - "@eslint/eslintrc": "^1.4.1", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz", - "integrity": "sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "enhanced-resolve": "^5.10.0", - "get-tsconfig": "^4.2.0", - "globby": "^13.1.2", - "is-core-module": "^2.10.0", - "is-glob": "^4.0.3", - "synckit": "^0.8.4" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*" - } - }, - "node_modules/eslint-import-resolver-typescript/node_modules/globby": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", - "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", - "dev": true, - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-import-resolver-typescript/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", - "has": "^1.0.3", - "is-core-module": "^2.8.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", - "dev": true, - "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz", - "integrity": "sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.3.1", - "jest-get-type": "^29.2.0", - "jest-matcher-utils": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-util": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-tsconfig": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.3.0.tgz", - "integrity": "sha512-YCcF28IqSay3fqpIu5y3Krg/utCBHBeoflkZyHj/QcqI2nrLPC3ZegS9CmIo+hJb8K7aiGsuUl7PwWVjNG2HQQ==", - "dev": true, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globalyzer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", - "dev": true - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/if-node-version": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/if-node-version/-/if-node-version-1.1.1.tgz", - "integrity": "sha512-qMcJD7ftbSWlf+6w8nzZAWKOELmG3xH5Gu5XYW2+f9uaYj1t9JX5KNWxnvNMGhu8f+uIUgnqFHLlwyKTHaLWWA==", - "dependencies": { - "cross-spawn": "^5.0.1", - "semver": "^5.2.0" - }, - "bin": { - "if-node-version": "bin/index.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/if-node-version/node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", - "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "node_modules/if-node-version/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/if-node-version/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/if-node-version/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/if-node-version/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/if-node-version/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/if-node-version/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", - "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", - "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/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, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.3.1.tgz", - "integrity": "sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==", - "dev": true, - "dependencies": { - "@jest/core": "^29.3.1", - "@jest/types": "^29.3.1", - "import-local": "^3.0.2", - "jest-cli": "^29.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.2.0.tgz", - "integrity": "sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.3.1.tgz", - "integrity": "sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.3.1", - "@jest/expect": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.3.1", - "jest-matcher-utils": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-runtime": "^29.3.1", - "jest-snapshot": "^29.3.1", - "jest-util": "^29.3.1", - "p-limit": "^3.1.0", - "pretty-format": "^29.3.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.3.1.tgz", - "integrity": "sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==", - "dev": true, - "dependencies": { - "@jest/core": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/types": "^29.3.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.3.1", - "jest-util": "^29.3.1", - "jest-validate": "^29.3.1", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.3.1.tgz", - "integrity": "sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.3.1", - "@jest/types": "^29.3.1", - "babel-jest": "^29.3.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.3.1", - "jest-environment-node": "^29.3.1", - "jest-get-type": "^29.2.0", - "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.3.1", - "jest-runner": "^29.3.1", - "jest-util": "^29.3.1", - "jest-validate": "^29.3.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.3.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz", - "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.3.1", - "jest-get-type": "^29.2.0", - "pretty-format": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.3.1.tgz", - "integrity": "sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.3.1", - "chalk": "^4.0.0", - "jest-get-type": "^29.2.0", - "jest-util": "^29.3.1", - "pretty-format": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.3.1.tgz", - "integrity": "sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.3.1", - "@jest/fake-timers": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "jest-mock": "^29.3.1", - "jest-util": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.3.1.tgz", - "integrity": "sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==", - "dev": true, - "dependencies": { - "@jest/types": "^29.3.1", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.2.0", - "jest-util": "^29.3.1", - "jest-worker": "^29.3.1", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz", - "integrity": "sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.2.0", - "pretty-format": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz", - "integrity": "sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.3.1", - "jest-get-type": "^29.2.0", - "pretty-format": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz", - "integrity": "sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.3.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.3.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.3.1.tgz", - "integrity": "sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.3.1", - "@types/node": "*", - "jest-util": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.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, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.3.1.tgz", - "integrity": "sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.3.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.3.1", - "jest-validate": "^29.3.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz", - "integrity": "sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.2.0", - "jest-snapshot": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.3.1.tgz", - "integrity": "sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.3.1", - "@jest/environment": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.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.3.1", - "jest-haste-map": "^29.3.1", - "jest-leak-detector": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-resolve": "^29.3.1", - "jest-runtime": "^29.3.1", - "jest-util": "^29.3.1", - "jest-watcher": "^29.3.1", - "jest-worker": "^29.3.1", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.3.1.tgz", - "integrity": "sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.3.1", - "@jest/fake-timers": "^29.3.1", - "@jest/globals": "^29.3.1", - "@jest/source-map": "^29.2.0", - "@jest/test-result": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.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.3.1", - "jest-message-util": "^29.3.1", - "jest-mock": "^29.3.1", - "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.3.1", - "jest-snapshot": "^29.3.1", - "jest-util": "^29.3.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.3.1.tgz", - "integrity": "sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.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.3.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.3.1", - "jest-get-type": "^29.2.0", - "jest-haste-map": "^29.3.1", - "jest-matcher-utils": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-util": "^29.3.1", - "natural-compare": "^1.4.0", - "pretty-format": "^29.3.1", - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", - "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.3.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.3.1.tgz", - "integrity": "sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==", - "dev": true, - "dependencies": { - "@jest/types": "^29.3.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.2.0", - "leven": "^3.1.0", - "pretty-format": "^29.3.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.3.1.tgz", - "integrity": "sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.3.1", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", - "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.3.1", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "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, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "dev": true, - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", - "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dev": true, - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-format": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz", - "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" - }, - "node_modules/punycode": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz", - "integrity": "sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/synckit": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.4.tgz", - "integrity": "sha512-Dn2ZkzMdSX827QbowGbU/4yjWuvNaCoScLLoMo/yKbu+P4GBR6cRGKZH27k6a9bRzdqcyd1DE96pQtQ6uNkmyw==", - "dev": true, - "dependencies": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.4.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/tiny-glob": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", - "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", - "dev": true, - "dependencies": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-jest": { - "version": "29.0.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.3.tgz", - "integrity": "sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.1", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-md5": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-1.3.1.tgz", - "integrity": "sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/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, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist-lint": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/v8-to-istanbul": { - "version": "9.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, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "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.49", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.49.tgz", - "integrity": "sha512-Qd5bdLlC/sphWQQPNn7etKXWCh+fij7DWxtkIwvhhZ+LM6UEApGLS8sBLBQcRO2ZQdEuNb+zeClUy+3DNojfeg==" - }, - "@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.38", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.38.tgz", - "integrity": "sha512-BBwAjORhuUkTGO3CxGS5Evcp5n20h9v06Sftn2R1DuSm8zIoUlPsNlI1HUk8XqYuoEI4aD7IKRQBLglv09ciJQ==" - }, - "@aws-cdk/cfnspec": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/cfnspec/-/cfnspec-2.55.1.tgz", - "integrity": "sha512-jJCaEV9pTLFft2eYkwmblfxUBQeJSH4KbMteiVQdp6wkYbzoj/sjyFInfKHgtfIOeqjNtPZ74Dhl5fRJsOpBDw==", - "dev": true, - "requires": { - "fs-extra": "^9.1.0", - "md5": "^2.3.0" - } - }, - "@aws-cdk/cloud-assembly-schema": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-2.55.1.tgz", - "integrity": "sha512-VVVQaqHP2PsNGRwfPsq1RxoOfyC54DO8RH+2nHqCyPIfSmrIH6Ux5Uk7KAURyXKdb3SjKra66bxPOzvXHOaBXg==", - "dev": true, - "requires": { - "jsonschema": "^1.4.1", - "semver": "^7.3.8" - }, - "dependencies": { - "jsonschema": { - "version": "1.4.1", - "bundled": true, - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "bundled": true, - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.8", - "bundled": true, - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "bundled": true, - "dev": true - } - } - }, - "@aws-cdk/cloudformation-diff": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloudformation-diff/-/cloudformation-diff-2.55.1.tgz", - "integrity": "sha512-WgLubFXoQFGZuOkm7YtJx+kS8EN6rX9K6s257qFR7yNGpKBANSPVvWmlWX2qSvg++AkNSxKsuaZi+4V50TNbdA==", - "dev": true, - "requires": { - "@aws-cdk/cfnspec": "2.55.1", - "@types/node": "^14.18.34", - "chalk": "^4", - "diff": "^5.1.0", - "fast-deep-equal": "^3.1.3", - "string-width": "^4.2.3", - "table": "^6.8.1" - }, - "dependencies": { - "@types/node": { - "version": "14.18.36", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz", - "integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==", - "dev": true - } - } - }, - "@aws-cdk/cx-api": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-2.55.1.tgz", - "integrity": "sha512-gWQCn/9gaayf2ev0HjCMQleY1gWJZGImLhsUF8kneYnOPCm/r0qpIzy3BxiWiDFLPh9gykDvHQhpH6EG0aXnGA==", - "dev": true, - "requires": { - "@aws-cdk/cloud-assembly-schema": "2.55.1", - "semver": "^7.3.8" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "bundled": true, - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.8", - "bundled": true, - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "bundled": true, - "dev": true - } - } - }, - "@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.10", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz", - "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==", - "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 - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", - "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", - "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" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@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.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", - "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@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 - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", - "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", - "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.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", - "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", - "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.7", - "@babel/types": "^7.20.7", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "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 - } - } - }, - "@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" - } - } - } - }, - "@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - } - }, - "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@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" - }, - "dependencies": { - "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" - } - }, - "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" - } - }, - "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" - } - }, - "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" - } - }, - "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" - } - }, - "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 - } - } - }, - "@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.3.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.3.1.tgz", - "integrity": "sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==", - "dev": true, - "requires": { - "@jest/types": "^29.3.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.3.1", - "jest-util": "^29.3.1", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.3.1.tgz", - "integrity": "sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw==", - "dev": true, - "requires": { - "@jest/console": "^29.3.1", - "@jest/reporters": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.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.2.0", - "jest-config": "^29.3.1", - "jest-haste-map": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.3.1", - "jest-resolve-dependencies": "^29.3.1", - "jest-runner": "^29.3.1", - "jest-runtime": "^29.3.1", - "jest-snapshot": "^29.3.1", - "jest-util": "^29.3.1", - "jest-validate": "^29.3.1", - "jest-watcher": "^29.3.1", - "micromatch": "^4.0.4", - "pretty-format": "^29.3.1", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.3.1.tgz", - "integrity": "sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "jest-mock": "^29.3.1" - } - }, - "@jest/expect": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.3.1.tgz", - "integrity": "sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==", - "dev": true, - "requires": { - "expect": "^29.3.1", - "jest-snapshot": "^29.3.1" - } - }, - "@jest/expect-utils": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz", - "integrity": "sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==", - "dev": true, - "requires": { - "jest-get-type": "^29.2.0" - } - }, - "@jest/fake-timers": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.3.1.tgz", - "integrity": "sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==", - "dev": true, - "requires": { - "@jest/types": "^29.3.1", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^29.3.1", - "jest-mock": "^29.3.1", - "jest-util": "^29.3.1" - } - }, - "@jest/globals": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.3.1.tgz", - "integrity": "sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==", - "dev": true, - "requires": { - "@jest/environment": "^29.3.1", - "@jest/expect": "^29.3.1", - "@jest/types": "^29.3.1", - "jest-mock": "^29.3.1" - } - }, - "@jest/reporters": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.3.1.tgz", - "integrity": "sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.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.3.1", - "jest-util": "^29.3.1", - "jest-worker": "^29.3.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.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@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.3.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.3.1.tgz", - "integrity": "sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw==", - "dev": true, - "requires": { - "@jest/console": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz", - "integrity": "sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==", - "dev": true, - "requires": { - "@jest/test-result": "^29.3.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.3.1", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.3.1.tgz", - "integrity": "sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.3.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.3.1", - "jest-regex-util": "^29.2.0", - "jest-util": "^29.3.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.1" - } - }, - "@jest/types": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz", - "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.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" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@pkgr/utils": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz", - "integrity": "sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "is-glob": "^4.0.3", - "open": "^8.4.0", - "picocolors": "^1.0.0", - "tiny-glob": "^0.2.9", - "tslib": "^2.4.0" - } - }, - "@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.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.1.20", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", - "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@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.2.5", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.5.tgz", - "integrity": "sha512-H2cSxkKgVmqNHXP7TC2L/WUorrZu8ZigyRywfVzv6EyBlxj39n4C00hjXYQWsbwqgElaj/CiAeSRmk5GoaKTgw==", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "@types/node": { - "version": "18.11.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.15.tgz", - "integrity": "sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw==", - "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/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "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.19", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.19.tgz", - "integrity": "sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==", - "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 - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz", - "integrity": "sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.48.1", - "@typescript-eslint/type-utils": "5.48.1", - "@typescript-eslint/utils": "5.48.1", - "debug": "^4.3.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.1.tgz", - "integrity": "sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.48.1", - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/typescript-estree": "5.48.1", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz", - "integrity": "sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/visitor-keys": "5.48.1" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz", - "integrity": "sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.48.1", - "@typescript-eslint/utils": "5.48.1", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.1.tgz", - "integrity": "sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz", - "integrity": "sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/visitor-keys": "5.48.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.1.tgz", - "integrity": "sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.48.1", - "@typescript-eslint/types": "5.48.1", - "@typescript-eslint/typescript-estree": "5.48.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz", - "integrity": "sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.48.1", - "eslint-visitor-keys": "^3.3.0" - } - }, - "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "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 - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "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" - }, - "dependencies": { - "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 - } - } - }, - "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": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "aws-cdk": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.55.1.tgz", - "integrity": "sha512-M/d4lO7rLMvPedNj7pEQtwyxn7nVLluiHggftXlYHOIeaoMG1QCbH1zTuZtMZZxdRRD4ZiOEHvvPYFU/HMcrPQ==", - "dev": true, - "requires": { - "fsevents": "2.3.2" - } - }, - "aws-cdk-lib": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.55.1.tgz", - "integrity": "sha512-v0MhL6RqazQ2HKj9TXJvXKQcQNIDYeRzcOJ2xB2Usz56xodArc2goLu2P51X5J84xOO4w2AgNaWMCBzd7MbyOQ==", - "requires": { - "@aws-cdk/asset-awscli-v1": "^2.2.30", - "@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.1", - "jsonschema": "^1.4.1", - "minimatch": "^3.1.2", - "punycode": "^2.1.1", - "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.1", - "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.1.1", - "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.3.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", - "integrity": "sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==", - "dev": true, - "requires": { - "@jest/transform": "^29.3.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.2.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.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz", - "integrity": "sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==", - "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.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz", - "integrity": "sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.2.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==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "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.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" - } - }, - "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==" - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "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.30001442", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz", - "integrity": "sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==", - "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 - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "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==" - }, - "constructs": { - "version": "10.1.216", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.1.216.tgz", - "integrity": "sha512-Nx/Pzuyy46Bc5LFu3XPSmTKNcjN4ChQ/R3syOb7bJLfQ5VhJn+o0aCKBVwnbuF1av0YCG6IaDz8fdtWhjefJjA==" - }, - "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" - } - }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "dev": true - }, - "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 - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true - }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "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": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "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 - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "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 - }, - "enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "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" - } - }, - "es-abstract": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", - "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.4", - "is-array-buffer": "^3.0.1", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - } - }, - "es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - } - }, - "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "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": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz", - "integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.4.1", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-import-resolver-typescript": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz", - "integrity": "sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ==", - "dev": true, - "requires": { - "debug": "^4.3.4", - "enhanced-resolve": "^5.10.0", - "get-tsconfig": "^4.2.0", - "globby": "^13.1.2", - "is-core-module": "^2.10.0", - "is-glob": "^4.0.3", - "synckit": "^0.8.4" - }, - "dependencies": { - "globby": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", - "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", - "dev": true, - "requires": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - } - }, - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", - "dev": true, - "requires": { - "debug": "^3.2.7" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", - "dev": true, - "requires": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", - "has": "^1.0.3", - "is-core-module": "^2.8.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, - "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 - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "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.3.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz", - "integrity": "sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.3.1", - "jest-get-type": "^29.2.0", - "jest-matcher-utils": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-util": "^29.3.1" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.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 - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "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" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "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": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.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 - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "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-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "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 - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "get-tsconfig": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.3.0.tgz", - "integrity": "sha512-YCcF28IqSay3fqpIu5y3Krg/utCBHBeoflkZyHj/QcqI2nrLPC3ZegS9CmIo+hJb8K7aiGsuUl7PwWVjNG2HQQ==", - "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" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3" - } - }, - "globalyzer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", - "dev": true - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "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-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "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 - }, - "if-node-version": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/if-node-version/-/if-node-version-1.1.1.tgz", - "integrity": "sha512-qMcJD7ftbSWlf+6w8nzZAWKOELmG3xH5Gu5XYW2+f9uaYj1t9JX5KNWxnvNMGhu8f+uIUgnqFHLlwyKTHaLWWA==", - "requires": { - "cross-spawn": "^5.0.1", - "semver": "^5.2.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - } - } - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "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 - }, - "internal-slot": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", - "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-array-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", - "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-typed-array": "^1.1.10" - } - }, - "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-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "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-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "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-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "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-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "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 - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "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" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "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.3.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.3.1.tgz", - "integrity": "sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==", - "dev": true, - "requires": { - "@jest/core": "^29.3.1", - "@jest/types": "^29.3.1", - "import-local": "^3.0.2", - "jest-cli": "^29.3.1" - } - }, - "jest-changed-files": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.2.0.tgz", - "integrity": "sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.3.1.tgz", - "integrity": "sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==", - "dev": true, - "requires": { - "@jest/environment": "^29.3.1", - "@jest/expect": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.3.1", - "jest-matcher-utils": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-runtime": "^29.3.1", - "jest-snapshot": "^29.3.1", - "jest-util": "^29.3.1", - "p-limit": "^3.1.0", - "pretty-format": "^29.3.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-cli": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.3.1.tgz", - "integrity": "sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==", - "dev": true, - "requires": { - "@jest/core": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/types": "^29.3.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.3.1", - "jest-util": "^29.3.1", - "jest-validate": "^29.3.1", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - } - }, - "jest-config": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.3.1.tgz", - "integrity": "sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.3.1", - "@jest/types": "^29.3.1", - "babel-jest": "^29.3.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.3.1", - "jest-environment-node": "^29.3.1", - "jest-get-type": "^29.2.0", - "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.3.1", - "jest-runner": "^29.3.1", - "jest-util": "^29.3.1", - "jest-validate": "^29.3.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.3.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - }, - "jest-diff": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz", - "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.3.1", - "jest-get-type": "^29.2.0", - "pretty-format": "^29.3.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.3.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.3.1.tgz", - "integrity": "sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==", - "dev": true, - "requires": { - "@jest/types": "^29.3.1", - "chalk": "^4.0.0", - "jest-get-type": "^29.2.0", - "jest-util": "^29.3.1", - "pretty-format": "^29.3.1" - } - }, - "jest-environment-node": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.3.1.tgz", - "integrity": "sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==", - "dev": true, - "requires": { - "@jest/environment": "^29.3.1", - "@jest/fake-timers": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "jest-mock": "^29.3.1", - "jest-util": "^29.3.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.3.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.3.1.tgz", - "integrity": "sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==", - "dev": true, - "requires": { - "@jest/types": "^29.3.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.3.1", - "jest-worker": "^29.3.1", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz", - "integrity": "sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==", - "dev": true, - "requires": { - "jest-get-type": "^29.2.0", - "pretty-format": "^29.3.1" - } - }, - "jest-matcher-utils": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz", - "integrity": "sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.3.1", - "jest-get-type": "^29.2.0", - "pretty-format": "^29.3.1" - } - }, - "jest-message-util": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz", - "integrity": "sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.3.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.3.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.3.1.tgz", - "integrity": "sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==", - "dev": true, - "requires": { - "@jest/types": "^29.3.1", - "@types/node": "*", - "jest-util": "^29.3.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, - "requires": {} - }, - "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.3.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.3.1.tgz", - "integrity": "sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.3.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.3.1", - "jest-validate": "^29.3.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz", - "integrity": "sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA==", - "dev": true, - "requires": { - "jest-regex-util": "^29.2.0", - "jest-snapshot": "^29.3.1" - } - }, - "jest-runner": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.3.1.tgz", - "integrity": "sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA==", - "dev": true, - "requires": { - "@jest/console": "^29.3.1", - "@jest/environment": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.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.3.1", - "jest-haste-map": "^29.3.1", - "jest-leak-detector": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-resolve": "^29.3.1", - "jest-runtime": "^29.3.1", - "jest-util": "^29.3.1", - "jest-watcher": "^29.3.1", - "jest-worker": "^29.3.1", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "dependencies": { - "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.3.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.3.1.tgz", - "integrity": "sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==", - "dev": true, - "requires": { - "@jest/environment": "^29.3.1", - "@jest/fake-timers": "^29.3.1", - "@jest/globals": "^29.3.1", - "@jest/source-map": "^29.2.0", - "@jest/test-result": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.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.3.1", - "jest-message-util": "^29.3.1", - "jest-mock": "^29.3.1", - "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.3.1", - "jest-snapshot": "^29.3.1", - "jest-util": "^29.3.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - } - }, - "jest-snapshot": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.3.1.tgz", - "integrity": "sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==", - "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.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.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.3.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.3.1", - "jest-get-type": "^29.2.0", - "jest-haste-map": "^29.3.1", - "jest-matcher-utils": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-util": "^29.3.1", - "natural-compare": "^1.4.0", - "pretty-format": "^29.3.1", - "semver": "^7.3.5" - } - }, - "jest-util": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", - "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==", - "dev": true, - "requires": { - "@jest/types": "^29.3.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.3.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.3.1.tgz", - "integrity": "sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==", - "dev": true, - "requires": { - "@jest/types": "^29.3.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.2.0", - "leven": "^3.1.0", - "pretty-format": "^29.3.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.3.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.3.1.tgz", - "integrity": "sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==", - "dev": true, - "requires": { - "@jest/test-result": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.3.1", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", - "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.3.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-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true - }, - "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": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "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 - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "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 - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "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 - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "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": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.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 - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "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" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "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" - } - }, - "md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "dev": true, - "requires": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, - "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 - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "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==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true - }, - "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 - }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "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.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", - "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", - "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" - } - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "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" - } - }, - "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dev": true, - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "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" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "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 - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "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 - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "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" - }, - "dependencies": { - "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" - } - }, - "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" - } - }, - "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" - } - } - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "pretty-format": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz", - "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.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" - } - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" - }, - "punycode": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz", - "integrity": "sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==" - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "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 - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "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" - }, - "dependencies": { - "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-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "requires": { - "lru-cache": "^6.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==", - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "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 - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "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 - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "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" - } - }, - "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "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 - }, - "synckit": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.4.tgz", - "integrity": "sha512-Dn2ZkzMdSX827QbowGbU/4yjWuvNaCoScLLoMo/yKbu+P4GBR6cRGKZH27k6a9bRzdqcyd1DE96pQtQ6uNkmyw==", - "dev": true, - "requires": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.4.0" - } - }, - "table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "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" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "tiny-glob": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", - "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", - "dev": true, - "requires": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" - } - }, - "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.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.3.tgz", - "integrity": "sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.1", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" - } - }, - "ts-md5": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-1.3.1.tgz", - "integrity": "sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==" - }, - "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" - }, - "dependencies": { - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - } - } - }, - "tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - } - } - }, - "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.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.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - }, - "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" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.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" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "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": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "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/layer-publisher/package.json b/layer-publisher/package.json deleted file mode 100644 index 4acf379284..0000000000 --- a/layer-publisher/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "layer-publisher", - "version": "1.5.1", - "bin": { - "layer-publisher": "bin/layer-publisher.js" - }, - "scripts": { - "build": "tsc", - "watch": "tsc -w", - "test": "npm run test:unit", - "cdk": "cdk", - "package": "echo 'Not applicable'", - "lint": "eslint --ext .ts --no-error-on-unmatched-pattern src tests --resolve-plugins-relative-to .", - "lint-fix": "eslint --fix --ext .ts --fix --no-error-on-unmatched-pattern src tests --resolve-plugins-relative-to .", - "test:unit": "if-node-version '>12' jest --testPathPattern=unit -u", - "test:e2e": "if-node-version '>12' jest --testPathPattern=e2e" - }, - "devDependencies": { - "@aws-cdk/cloudformation-diff": "^2.55.0", - "@aws-cdk/cx-api": "^2.55.0", - "@types/jest": "^29.2.4", - "@types/node": "18.11.15", - "@typescript-eslint/eslint-plugin": "^5.46.1", - "@typescript-eslint/parser": "^5.46.1", - "aws-cdk": "^2.55.0", - "eslint": "^8.29.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.26.0", - "jest": "^29.3.1", - "ts-jest": "^29.0.3", - "ts-node": "10.9.1", - "typescript": "4.9.4" - }, - "dependencies": { - "aws-cdk-lib": "^2.55.0", - "constructs": "^10.1.190", - "if-node-version": "^1.1.1", - "source-map-support": "^0.5.21", - "ts-md5": "^1.3.1" - } -} diff --git a/layer-publisher/src/powertools-typescript-layer.ts b/layer-publisher/src/powertools-typescript-layer.ts deleted file mode 100644 index bbdd602ac6..0000000000 --- a/layer-publisher/src/powertools-typescript-layer.ts +++ /dev/null @@ -1,63 +0,0 @@ -import * as path from 'path'; -import { aws_lambda as lambda } from 'aws-cdk-lib'; -import { Construct } from 'constructs'; -import { execSync } from 'child_process'; -import { Md5 } from 'ts-md5'; - -export interface PowerToolsTypeScriptLayerProps { - /** - * The powertools package version from npm repository. - */ - readonly version?: string - - /** - * the name of the layer, will be randomised by cdk if empty - */ - readonly layerVersionName?: string -} - -export class PowerToolsTypeScriptLayer extends lambda.LayerVersion { - public constructor(scope: Construct, id: string, props?: PowerToolsTypeScriptLayerProps) { - const version = props?.version ?? 'latest'; - console.log(`publishing layer ${props?.layerVersionName} version : ${version}`); - - const commands = [ - 'mkdir nodejs', - 'cd nodejs', - 'npm init -y', - `npm install --save \ - @aws-lambda-powertools/commons@${version} \ - @aws-lambda-powertools/logger@${version} \ - @aws-lambda-powertools/metrics@${version} \ - @aws-lambda-powertools/tracer@${version}`, - 'rm -rf node_modules/@types', - 'rm package.json package-lock.json', - ]; - const commandJoined = commands.join(' && '); - - super(scope, id, { - layerVersionName: props?.layerVersionName, - description: `Lambda Powertools for TypeScript version ${props?.version}`, - compatibleRuntimes: [ lambda.Runtime.NODEJS_14_X, lambda.Runtime.NODEJS_16_X, lambda.Runtime.NODEJS_18_X ], - code: lambda.Code.fromAsset(path.join(__dirname, '.'), { - assetHash: Md5.hashStr(commandJoined), - bundling: { - image: lambda.Runtime.NODEJS_14_X.bundlingImage, - local: { - tryBundle(outputDir: string) { - try { - execSync('npm --version && zip --version'); - } catch { - return false; - } - - execSync(commandJoined, { cwd: outputDir }); - - return true; - }, - }, - }, - }), - }); - } -} diff --git a/layer-publisher/tests/e2e/happy-case.test.lambda.ts b/layer-publisher/tests/e2e/happy-case.test.lambda.ts deleted file mode 100644 index f454713f79..0000000000 --- a/layer-publisher/tests/e2e/happy-case.test.lambda.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Logger } from '@aws-lambda-powertools/logger'; -import { Metrics } from '@aws-lambda-powertools/metrics'; -import { Tracer } from '@aws-lambda-powertools/tracer'; -import fs from 'fs'; - -const SERVICE_NAME = 'e2e-tests-layer-consumer'; -const logger = new Logger({ serviceName: SERVICE_NAME, logLevel: 'DEBUG' }); -const metrics = new Metrics({ serviceName: SERVICE_NAME }); -const tracer = new Tracer({ serviceName: SERVICE_NAME }); - -exports.handler = function (_event: never, _ctx: unknown): void { - // check logger lib access - logger.injectLambdaContext(); - logger.debug('Hello World!'); - // Check version - try { - const packageJSON = JSON.parse( - fs.readFileSync('/opt/nodejs/node_modules/@aws-lambda-powertools/logger/package.json', { - encoding: 'utf8', - flag: 'r', - }) - ); - metrics.captureColdStartMetric(); - const segment = tracer.getSegment(); - - const handlerSegment = segment.addNewSubsegment('Handler'); - - tracer.setSegment(handlerSegment); - - tracer.annotateColdStart(); - - if (packageJSON.version != process.env.POWERTOOLS_PACKAGE_VERSION) { - throw new Error( - `Package version mismatch: ${packageJSON.version} != ${process.env.POWERTOOLS_PACKAGE_VERSION}` - ); - } - } catch (error) { - logger.error(error); - } -}; diff --git a/layer-publisher/tests/e2e/happy-case.test.ts b/layer-publisher/tests/e2e/happy-case.test.ts deleted file mode 100644 index 7bdc2c6234..0000000000 --- a/layer-publisher/tests/e2e/happy-case.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Test layer - * - * @group e2e - * - */ - -import * as cdk from 'aws-cdk-lib'; -import { Stack } from 'aws-cdk-lib'; -import * as lambda from 'aws-cdk-lib/aws-lambda'; -import * as nodejs from 'aws-cdk-lib/aws-lambda-nodejs'; -import * as LayerPublisher from '../../src/layer-publisher-stack'; -import { deployStack, destroyStack } from '../../../packages/commons/tests/utils/cdk-cli'; -import { generateUniqueName, invokeFunction } from '../../../packages/commons/tests/utils/e2eUtils'; -import { LEVEL } from '../../../packages/commons/tests/utils/InvocationLogs'; - -const runtime = lambda.Runtime.ALL.find((r) => r.name === process.env.RUNTIME) ?? lambda.Runtime.NODEJS_14_X; - -const powerToolsPackageVersion = '1.0.1'; - -const e2eTestLayerPublicationApp = new cdk.App(); - -const layerStack = new LayerPublisher.LayerPublisherStack( - e2eTestLayerPublicationApp, - `E2ELayerPublisherStack-${runtime.name.split('.')[0]}`, - { - layerName: `e2e-tests-layer-${runtime.name.split('.')[0]}`, - powerToolsPackageVersion: powerToolsPackageVersion, - ssmParameterLayerArn: `/e2e-tests-layertools-layer-arn-${runtime.name.split('.')[0]}`, - } -); - -test(`The layer Created is usable with ${runtime} runtime lambda`, async () => { - // GIVEN - const { consumerStack, functionName } = createSampleLambda(runtime); - - await deployStack(e2eTestLayerPublicationApp, layerStack); - await deployStack(e2eTestLayerPublicationApp, consumerStack); - - // WHEN - const invocationLogs = await invokeFunction(functionName); - - // THEN - try { - const errorLogs = invocationLogs[0].getFunctionLogs(LEVEL.ERROR); - expect(errorLogs.length).toBe(0); - - const warningLogs = invocationLogs[0].getFunctionLogs(LEVEL.WARN); - expect(warningLogs.length).toBe(1); // the missing namespace warning - - const infoLogs = invocationLogs[0].getFunctionLogs(LEVEL.INFO); - expect(infoLogs.length).toBe(1); // the coldstart metric one - - const debugLogs = invocationLogs[0].getFunctionLogs(LEVEL.DEBUG); - expect(debugLogs.length).toBe(1); // the Hello World! message - } catch (error) { - console.log(JSON.stringify(invocationLogs[0].getFunctionLogs())); - throw error; - } - finally { - await destroyStack(e2eTestLayerPublicationApp, consumerStack); - } -}, 900000); - -const createSampleLambda = (runtime: cdk.aws_lambda.Runtime): { consumerStack: cdk.Stack; functionName: string } => { - const functionName = generateUniqueName('E2ETest', 'Layer', runtime.name.split('.')[0], 'Consumer'); - - const consumerStack = new Stack(e2eTestLayerPublicationApp, `${runtime.name.split('.')[0]}ConsumerStack`); - new nodejs.NodejsFunction(consumerStack, 'lambda', { - handler: 'handler', - functionName, - runtime: runtime, - bundling: { - externalModules: [ - '@aws-lambda-powertools/commons', - '@aws-lambda-powertools/logger', - '@aws-lambda-powertools/metrics', - '@aws-lambda-powertools/tracer' - ] - }, - environment: { - POWERTOOLS_PACKAGE_VERSION: powerToolsPackageVersion, - }, - layers: [layerStack.lambdaLayerVersion], - }); - - return { consumerStack, functionName }; -}; diff --git a/layer-publisher/tests/unit/__snapshots__/layer-publisher.test.ts.snap b/layer-publisher/tests/unit/__snapshots__/layer-publisher.test.ts.snap deleted file mode 100644 index 9e6b8f7ad7..0000000000 --- a/layer-publisher/tests/unit/__snapshots__/layer-publisher.test.ts.snap +++ /dev/null @@ -1,94 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Layer Created 1`] = ` -Object { - "Outputs": Object { - "LatestLayerArn": Object { - "Export": Object { - "Name": "AWSLambdaPowertoolsTypeScript", - }, - "Value": Object { - "Ref": "LambdaPowertoolsLayer733361E9", - }, - }, - }, - "Parameters": Object { - "BootstrapVersion": Object { - "Default": "/cdk-bootstrap/hnb659fds/version", - "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]", - "Type": "AWS::SSM::Parameter::Value", - }, - }, - "Resources": Object { - "LambdaPowertoolsLayer733361E9": Object { - "DeletionPolicy": "Retain", - "Properties": Object { - "CompatibleRuntimes": Array [ - "nodejs12.x", - "nodejs14.x", - "nodejs16.x", - ], - "Content": Object { - "S3Bucket": Object { - "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", - }, - "S3Key": "dbdb3f66eaeed03649521bf73dbcdd95a713086afccbac3f57fa407ffb76bdaa.zip", - }, - "Description": "Lambda Powertools for TypeScript version 1.0.1", - "LayerName": "AWSLambdaPowertoolsTypeScript", - }, - "Type": "AWS::Lambda::LayerVersion", - "UpdateReplacePolicy": "Retain", - }, - "PublicLayerAccess": Object { - "DeletionPolicy": "Retain", - "Properties": Object { - "Action": "lambda:GetLayerVersion", - "LayerVersionArn": Object { - "Ref": "LambdaPowertoolsLayer733361E9", - }, - "Principal": "*", - }, - "Type": "AWS::Lambda::LayerVersionPermission", - "UpdateReplacePolicy": "Retain", - }, - "VersionArn99E177D3": Object { - "Properties": Object { - "Name": "/layers/powertools-layer-arn", - "Type": "String", - "Value": Object { - "Ref": "LambdaPowertoolsLayer733361E9", - }, - }, - "Type": "AWS::SSM::Parameter", - }, - }, - "Rules": Object { - "CheckBootstrapVersion": Object { - "Assertions": Array [ - Object { - "Assert": Object { - "Fn::Not": Array [ - Object { - "Fn::Contains": Array [ - Array [ - "1", - "2", - "3", - "4", - "5", - ], - Object { - "Ref": "BootstrapVersion", - }, - ], - }, - ], - }, - "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.", - }, - ], - }, - }, -} -`; diff --git a/layer-publisher/tests/unit/layer-publisher.test.ts b/layer-publisher/tests/unit/layer-publisher.test.ts deleted file mode 100644 index 876325f255..0000000000 --- a/layer-publisher/tests/unit/layer-publisher.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Test layer publisher - * - * @group unit/layer-publisher - */ - -import * as cdk from 'aws-cdk-lib'; -import { Template } from 'aws-cdk-lib/assertions'; -import * as LayerPublisher from '../../src/layer-publisher-stack'; - -test('Layer Created', () => { - const app = new cdk.App(); - // WHEN - const stack = new LayerPublisher.LayerPublisherStack(app, 'MyTestStack', { - layerName: 'AWSLambdaPowertoolsTypeScript', - powerToolsPackageVersion: '1.0.1', - ssmParameterLayerArn: '/layers/powertools-layer-arn', - }); - - // THEN - const template = Template.fromStack(stack); - - expect(template).toMatchSnapshot(); -}); diff --git a/layer-publisher/.eslintrc.js b/layers/.eslintrc.js similarity index 100% rename from layer-publisher/.eslintrc.js rename to layers/.eslintrc.js diff --git a/layer-publisher/.gitignore b/layers/.gitignore similarity index 100% rename from layer-publisher/.gitignore rename to layers/.gitignore diff --git a/layer-publisher/.npmignore b/layers/.npmignore similarity index 100% rename from layer-publisher/.npmignore rename to layers/.npmignore diff --git a/layer-publisher/CHANGELOG.md b/layers/CHANGELOG.md similarity index 100% rename from layer-publisher/CHANGELOG.md rename to layers/CHANGELOG.md diff --git a/layer-publisher/README.md b/layers/README.md similarity index 100% rename from layer-publisher/README.md rename to layers/README.md diff --git a/layer-publisher/bin/layer-publisher.ts b/layers/bin/layers.ts similarity index 73% rename from layer-publisher/bin/layer-publisher.ts rename to layers/bin/layers.ts index 4c9c9bf2af..e5eea801e4 100644 --- a/layer-publisher/bin/layer-publisher.ts +++ b/layers/bin/layers.ts @@ -1,13 +1,14 @@ #!/usr/bin/env node import 'source-map-support/register'; -import * as cdk from 'aws-cdk-lib'; +import { App } from 'aws-cdk-lib'; import { LayerPublisherStack } from '../src/layer-publisher-stack'; const SSM_PARAM_LAYER_ARN = '/layers/powertools-layer-arn'; -const app = new cdk.App(); +const app = new App(); + new LayerPublisherStack(app, 'LayerPublisherStack', { - powerToolsPackageVersion: app.node.tryGetContext('PowerToolsPackageVersion'), + powertoolsPackageVersion: app.node.tryGetContext('PowertoolsPackageVersion'), layerName: 'AWSLambdaPowertoolsTypeScript', ssmParameterLayerArn: SSM_PARAM_LAYER_ARN, }); \ No newline at end of file diff --git a/layer-publisher/cdk.json b/layers/cdk.json similarity index 92% rename from layer-publisher/cdk.json rename to layers/cdk.json index 4d1a4ba742..4ca6aac30e 100644 --- a/layer-publisher/cdk.json +++ b/layers/cdk.json @@ -1,5 +1,5 @@ { - "app": "npx ts-node --prefer-ts-exts bin/layer-publisher.ts", + "app": "npx ts-node --prefer-ts-exts bin/layers.ts", "watch": { "include": [ "**" diff --git a/layers/jest.config.js b/layers/jest.config.js new file mode 100644 index 0000000000..4c5e481d90 --- /dev/null +++ b/layers/jest.config.js @@ -0,0 +1,45 @@ +module.exports = { + displayName: { + name: 'AWS Lambda Powertools utility: LAYERS', + color: 'black', + }, + runner: 'groups', + preset: 'ts-jest', + transform: { + '^.+\\.ts?$': 'ts-jest', + }, + moduleFileExtensions: [ 'js', 'ts' ], + 'collectCoverageFrom': [ + '**/src/**/*.ts', + '!**/node_modules/**', + ], + 'testMatch': ['**/?(*.)+(spec|test).ts'], + 'roots': [ + '/src', + '/tests', + ], + 'testPathIgnorePatterns': [ + '/node_modules/', + ], + 'testEnvironment': 'node', + 'coveragePathIgnorePatterns': [ + '/node_modules/', + '/types/', + ], + 'coverageThreshold': { + 'global': { + 'statements': 100, + 'branches': 100, + 'functions': 100, + 'lines': 100, + }, + }, + 'coverageReporters': [ + 'json-summary', + 'text', + 'lcov' + ], + 'setupFiles': [ + '/tests/helpers/populateEnvironmentVariables.ts' + ] +}; \ No newline at end of file diff --git a/layers/package.json b/layers/package.json new file mode 100644 index 0000000000..b8803973df --- /dev/null +++ b/layers/package.json @@ -0,0 +1,35 @@ +{ + "name": "layers", + "version": "1.5.1", + "bin": { + "layer": "bin/layers.js" + }, + "description": "This CDK app is meant to be used to publish Powertools for TypeScript Lambda Layer. It is composed of a single stack deploying the Layer into the target account.", + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "echo 'Not applicable'", + "cdk": "cdk", + "package": "echo 'Not applicable'", + "lint": "eslint --ext .ts --no-error-on-unmatched-pattern src tests", + "lint-fix": "eslint --fix --ext .ts --fix --no-error-on-unmatched-pattern src tests", + "test:unit": "jest --group=unit", + "test:e2e": "RUNTIME=nodejs14x jest --group=e2e" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/awslabs/aws-lambda-powertools-typescript.git" + }, + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com" + }, + "license": "MIT-0", + "bugs": { + "url": "https://github.com/awslabs/aws-lambda-powertools-typescript/issues" + }, + "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript#readme", + "devDependencies": { + "source-map-support": "^0.5.21" + } +} \ No newline at end of file diff --git a/layer-publisher/src/layer-publisher-stack.ts b/layers/src/layer-publisher-stack.ts similarity index 60% rename from layer-publisher/src/layer-publisher-stack.ts rename to layers/src/layer-publisher-stack.ts index 47708b30af..875bbd4abf 100644 --- a/layer-publisher/src/layer-publisher-stack.ts +++ b/layers/src/layer-publisher-stack.ts @@ -1,24 +1,42 @@ -import { CfnOutput, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; +import { + CfnOutput, + RemovalPolicy, + Stack, + StackProps +} from 'aws-cdk-lib'; import { Construct } from 'constructs'; -import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { + LayerVersion, + Code, + Runtime, + CfnLayerVersionPermission +} from 'aws-cdk-lib/aws-lambda'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; -import { CfnLayerVersionPermission } from 'aws-cdk-lib/aws-lambda'; -import { PowerToolsTypeScriptLayer } from './powertools-typescript-layer'; export interface LayerPublisherStackProps extends StackProps { readonly layerName?: string - readonly powerToolsPackageVersion?: string + readonly powertoolsPackageVersion?: string readonly ssmParameterLayerArn: string } export class LayerPublisherStack extends Stack { - public readonly lambdaLayerVersion: lambda.LayerVersion; + public readonly lambdaLayerVersion: LayerVersion; public constructor(scope: Construct, id: string, props: LayerPublisherStackProps) { super(scope, id, props); - this.lambdaLayerVersion = new PowerToolsTypeScriptLayer(this, 'LambdaPowertoolsLayer', { + const { layerName, powertoolsPackageVersion } = props; + + console.log(`publishing layer ${layerName} version : ${powertoolsPackageVersion}`); + + this.lambdaLayerVersion = new LayerVersion(this, 'LambdaPowertoolsLayer', { layerVersionName: props?.layerName, - version: props?.powerToolsPackageVersion, + description: `AWS Lambda Powertools for TypeScript version ${powertoolsPackageVersion}`, + compatibleRuntimes: [ + Runtime.NODEJS_14_X, + Runtime.NODEJS_16_X, + Runtime.NODEJS_18_X + ], + code: Code.fromAsset('../tmp'), }); const layerPermission = new CfnLayerVersionPermission(this, 'PublicLayerAccess', { diff --git a/layers/tests/e2e/constants.ts b/layers/tests/e2e/constants.ts new file mode 100644 index 0000000000..268910c49b --- /dev/null +++ b/layers/tests/e2e/constants.ts @@ -0,0 +1,5 @@ +export const RESOURCE_NAME_PREFIX = 'Layers-E2E'; +export const ONE_MINUTE = 60 * 1000; +export const TEST_CASE_TIMEOUT = 3 * ONE_MINUTE; +export const SETUP_TIMEOUT = 5 * ONE_MINUTE; +export const TEARDOWN_TIMEOUT = 5 * ONE_MINUTE; \ No newline at end of file diff --git a/layers/tests/e2e/layerPublisher.class.test.functionCode.ts b/layers/tests/e2e/layerPublisher.class.test.functionCode.ts new file mode 100644 index 0000000000..aaee959338 --- /dev/null +++ b/layers/tests/e2e/layerPublisher.class.test.functionCode.ts @@ -0,0 +1,46 @@ +import { readFileSync } from 'node:fs'; +import { Logger } from '@aws-lambda-powertools/logger'; +import { Metrics } from '@aws-lambda-powertools/metrics'; +import { Tracer } from '@aws-lambda-powertools/tracer'; + +const logger = new Logger({ + logLevel: 'DEBUG' +}); +const metrics = new Metrics(); +const tracer = new Tracer(); + +export const handler = (): void => { + + // Check that the packages version matches the expected one + try { + const packageJSON = JSON.parse( + readFileSync('/opt/nodejs/node_modules/@aws-lambda-powertools/logger/package.json', { + encoding: 'utf8', + flag: 'r', + }) + ); + + if (packageJSON.version != process.env.POWERTOOLS_PACKAGE_VERSION) { + throw new Error( + `Package version mismatch: ${packageJSON.version} != ${process.env.POWERTOOLS_PACKAGE_VERSION}` + ); + } + } catch (error) { + console.error(error); + } + + // Check that the logger is working + logger.debug('Hello World!'); + + // Check that the metrics is working + metrics.captureColdStartMetric(); + + // Check that the tracer is working + const segment = tracer.getSegment(); + const handlerSegment = segment.addNewSubsegment('### index.handler'); + tracer.setSegment(handlerSegment); + tracer.annotateColdStart(); + handlerSegment.close(); + tracer.setSegment(segment); + +}; \ No newline at end of file diff --git a/layers/tests/e2e/layerPublisher.test.ts b/layers/tests/e2e/layerPublisher.test.ts new file mode 100644 index 0000000000..378bfab1a2 --- /dev/null +++ b/layers/tests/e2e/layerPublisher.test.ts @@ -0,0 +1,147 @@ +/** + * Test LayerPublisherStack class + * + * @group e2e/layers/all + */ +import { App, Stack } from 'aws-cdk-lib'; +import { Tracing } from 'aws-cdk-lib/aws-lambda'; +import { LayerPublisherStack } from '../../src/layer-publisher-stack'; +import { + deployStack, + destroyStack +} from '../../../packages/commons/tests/utils/cdk-cli'; +import { + generateUniqueName, + invokeFunction, + isValidRuntimeKey, + createStackWithLambdaFunction +} from '../../../packages/commons/tests/utils/e2eUtils'; +import { + RESOURCE_NAME_PREFIX, + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT +} from './constants'; +import { + LEVEL, + InvocationLogs +} from '../../../packages/commons/tests/utils/InvocationLogs'; +import { v4 } from 'uuid'; +import path from 'path'; +import packageJson from '../../package.json'; + +const runtime: string = process.env.RUNTIME || 'nodejs1x'; + +if (!isValidRuntimeKey(runtime)) { + throw new Error(`Invalid runtime key: ${runtime}`); +} + +describe(`layers E2E tests (LayerPublisherStack) for runtime: ${runtime}`, () => { + + const uuid = v4(); + let invocationLogs: InvocationLogs[]; + const stackNameLayers = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'layerStack'); + const stackNameFunction = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'functionStack'); + const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'function'); + const ssmParameterLayerName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'parameter'); + const lambdaFunctionCodeFile = 'layerPublisher.class.test.functionCode.ts'; + + const invocationCount = 1; + + const integTestApp = new App(); + let stackLayer: LayerPublisherStack; + let stackFunction: Stack; + + const powerToolsPackageVersion = packageJson.version; + + beforeAll(async () => { + + const layerName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'layer'); + + stackLayer = new LayerPublisherStack( + integTestApp, + stackNameLayers, + { + layerName: layerName, + powertoolsPackageVersion: powerToolsPackageVersion, + ssmParameterLayerArn: ssmParameterLayerName, + } + ); + + stackFunction = createStackWithLambdaFunction({ + app: integTestApp, + stackName: stackNameFunction, + functionName: functionName, + functionEntry: path.join(__dirname, lambdaFunctionCodeFile), + tracing: Tracing.ACTIVE, + environment: { + UUID: uuid, + POWERTOOLS_PACKAGE_VERSION: powerToolsPackageVersion, + POWERTOOLS_SERVICE_NAME: 'LayerPublisherStack', + }, + runtime: runtime, + bundling: { + externalModules: [ + '@aws-lambda-powertools/commons', + '@aws-lambda-powertools/logger', + '@aws-lambda-powertools/metrics', + '@aws-lambda-powertools/tracer' + ] + }, + layers: [stackLayer.lambdaLayerVersion], + }); + + await deployStack(integTestApp, stackLayer); + await deployStack(integTestApp, stackFunction); + + invocationLogs = await invokeFunction(functionName, invocationCount, 'SEQUENTIAL'); + + }, SETUP_TIMEOUT); + + describe('LayerPublisherStack usage', () => { + + it('should have no errors in the logs, which indicates the pacakges version matches the expected one', () => { + + const logs = invocationLogs[0].getFunctionLogs(LEVEL.ERROR); + + expect(logs.length).toBe(0); + + }, TEST_CASE_TIMEOUT); + + it('should have one warning related to missing Metrics namespace', () => { + + const logs = invocationLogs[0].getFunctionLogs(LEVEL.WARN); + + expect(logs.length).toBe(1); + expect(logs[0]).toContain('Namespace should be defined, default used'); + + }, TEST_CASE_TIMEOUT); + + it('should have one info log related to coldstart metric', () => { + + const logs = invocationLogs[0].getFunctionLogs(LEVEL.INFO); + + expect(logs.length).toBe(1); + expect(logs[0]).toContain('ColdStart'); + + }, TEST_CASE_TIMEOUT); + + it('should have one debug log that says Hello World!', () => { + + const logs = invocationLogs[0].getFunctionLogs(LEVEL.DEBUG); + + expect(logs.length).toBe(1); + expect(logs[0]).toContain('Hello World!'); + + }, TEST_CASE_TIMEOUT); + + }); + + afterAll(async () => { + if (!process.env.DISABLE_TEARDOWN) { + await destroyStack(integTestApp, stackFunction); + await destroyStack(integTestApp, stackLayer); + } + }, TEARDOWN_TIMEOUT); + +}); \ No newline at end of file diff --git a/layers/tests/helpers/populateEnvironmentVariables.ts b/layers/tests/helpers/populateEnvironmentVariables.ts new file mode 100644 index 0000000000..7ff0774273 --- /dev/null +++ b/layers/tests/helpers/populateEnvironmentVariables.ts @@ -0,0 +1,7 @@ +// Reserved variables +process.env._X_AMZN_TRACE_ID = 'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1'; +process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-lambda-function'; +process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '128'; +if (process.env.AWS_REGION === undefined && process.env.CDK_DEFAULT_REGION === undefined) { + process.env.AWS_REGION = 'eu-west-1'; +} diff --git a/layers/tests/unit/layer-publisher.test.ts b/layers/tests/unit/layer-publisher.test.ts new file mode 100644 index 0000000000..d8df7773db --- /dev/null +++ b/layers/tests/unit/layer-publisher.test.ts @@ -0,0 +1,53 @@ +/** + * Test LayerPublisherStack class + * + * @group unit/layers/all + */ + +import { App } from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { + LayerPublisherStack +} from '../../src/layer-publisher-stack'; + +describe('Class: LayerPublisherStack', () => { + + it('creates the stack with a layer in it', () => { + + // Prepare + const app = new App(); + const stack = new LayerPublisherStack(app, 'MyTestStack', { + layerName: 'AWSLambdaPowertoolsTypeScript', + powertoolsPackageVersion: '1.0.1', + ssmParameterLayerArn: '/layers/powertools-layer-arn', + }); + + // Act + const template = Template.fromStack(stack); + + // Assess + template.resourceCountIs('AWS::Lambda::LayerVersion', 1); + template.hasResourceProperties('AWS::Lambda::LayerVersion', { + CompatibleRuntimes: [ + 'nodejs14.x', + 'nodejs16.x', + 'nodejs18.x' + ], + Description: 'AWS Lambda Powertools for TypeScript version 1.0.1', + LayerName: 'AWSLambdaPowertoolsTypeScript', + }); + + template.resourceCountIs('AWS::Lambda::LayerVersionPermission', 1); + template.hasResourceProperties('AWS::Lambda::LayerVersionPermission', { + Action: 'lambda:GetLayerVersion', + Principal: '*', + }); + + template.resourceCountIs('AWS::SSM::Parameter', 1); + template.hasResourceProperties('AWS::SSM::Parameter', { + Name: '/layers/powertools-layer-arn', + Type: 'String', + }); + }); + +}); \ No newline at end of file diff --git a/layer-publisher/tsconfig.es.json b/layers/tsconfig.es.json similarity index 100% rename from layer-publisher/tsconfig.es.json rename to layers/tsconfig.es.json diff --git a/layer-publisher/tsconfig.json b/layers/tsconfig.json similarity index 100% rename from layer-publisher/tsconfig.json rename to layers/tsconfig.json diff --git a/package-lock.json b/package-lock.json index 17a8ec3afb..02918f21f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,8 @@ "packages/tracer", "packages/parameters", "packages/idempotency", - "docs/snippets" + "docs/snippets", + "layers" ], "dependencies": { "hosted-git-info": "^6.1.1" @@ -84,6 +85,26 @@ "node": ">=14.0.0" } }, + "layers": { + "version": "1.5.1", + "license": "MIT-0", + "bin": { + "layer": "bin/layers.js" + }, + "devDependencies": { + "source-map-support": "^0.5.21" + } + }, + "layers/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -11661,6 +11682,10 @@ "node": ">=6" } }, + "node_modules/layers": { + "resolved": "layers", + "link": true + }, "node_modules/lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", @@ -16624,7 +16649,7 @@ }, "packages/commons": { "name": "@aws-lambda-powertools/commons", - "version": "1.2.1", + "version": "1.5.1", "license": "MIT-0" }, "packages/idempotency": { @@ -16642,13 +16667,11 @@ }, "packages/logger": { "name": "@aws-lambda-powertools/logger", - "version": "1.2.1", + "version": "1.5.1", "license": "MIT", "dependencies": { - "@aws-lambda-powertools/commons": "^1.2.1", - "lodash.clonedeep": "^4.5.0", - "lodash.merge": "^4.6.2", - "lodash.pickby": "^4.6.0" + "@aws-lambda-powertools/commons": "^1.5.1", + "lodash.merge": "^4.6.2" }, "devDependencies": { "@types/lodash.merge": "^4.6.7" @@ -16656,10 +16679,10 @@ }, "packages/metrics": { "name": "@aws-lambda-powertools/metrics", - "version": "1.2.1", + "version": "1.5.1", "license": "MIT-0", "dependencies": { - "@aws-lambda-powertools/commons": "^1.2.1" + "@aws-lambda-powertools/commons": "^1.5.1" }, "devDependencies": { "@types/promise-retry": "^1.1.3", @@ -16697,11 +16720,11 @@ }, "packages/tracer": { "name": "@aws-lambda-powertools/tracer", - "version": "1.2.1", + "version": "1.5.1", "license": "MIT-0", "dependencies": { - "@aws-lambda-powertools/commons": "^1.2.1", - "aws-xray-sdk-core": "^3.3.6" + "@aws-lambda-powertools/commons": "^1.5.1", + "aws-xray-sdk-core": "^3.4.0" }, "devDependencies": { "@aws-sdk/client-dynamodb": "^3.231.0", @@ -16946,8 +16969,7 @@ "@aws-lambda-powertools/logger": { "version": "file:packages/logger", "requires": { - "@aws-lambda-powertools/commons": "^1.2.1", - "@types/lodash.clonedeep": "^4.5.7", + "@aws-lambda-powertools/commons": "^1.5.1", "@types/lodash.merge": "^4.6.7", "lodash.merge": "^4.6.2" } @@ -16955,7 +16977,7 @@ "@aws-lambda-powertools/metrics": { "version": "file:packages/metrics", "requires": { - "@aws-lambda-powertools/commons": "^1.2.1", + "@aws-lambda-powertools/commons": "^1.5.1", "@types/promise-retry": "^1.1.3", "promise-retry": "^2.0.1" } @@ -16987,8 +17009,8 @@ "@aws-lambda-powertools/tracer": { "version": "file:packages/tracer", "requires": { - "@aws-lambda-powertools/commons": "^1.2.1", - "@aws-sdk/client-dynamodb": "^3.100.0", + "@aws-lambda-powertools/commons": "^1.5.1", + "@aws-sdk/client-dynamodb": "^3.231.0", "@types/promise-retry": "^1.1.3", "aws-sdk": "^2.1276.0", "aws-xray-sdk-core": "^3.4.0", @@ -23276,7 +23298,6 @@ "docs": { "version": "file:docs/snippets", "requires": { - "@aws-sdk/client-appconfigdata": "^3.245.0", "@aws-sdk/client-appconfigdata": "^3.245.0", "@aws-sdk/client-dynamodb": "^3.245.0", "@aws-sdk/client-secrets-manager": "^3.250.0", @@ -25956,6 +25977,24 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "layers": { + "version": "file:layers", + "requires": { + "source-map-support": "^0.5.21" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, "lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", diff --git a/package.json b/package.json index b937ffeb0a..dafe17ab5d 100644 --- a/package.json +++ b/package.json @@ -11,14 +11,15 @@ "packages/tracer", "packages/parameters", "packages/idempotency", - "docs/snippets" + "docs/snippets", + "layers" ], "scripts": { "init-environment": "husky install", "test": "npm t -ws", "commit": "commit", "package": "npm run package", - "setup-local": "export PROJECT_ROOT=$(pwd) && npm ci --foreground-scripts && cd $PROJECT_ROOT/examples/cdk && npm ci && cd $PROJECT_ROOT/examples/sam && npm ci && cd $PROJECT_ROOT/layer-publisher && npm ci && cd $PROJECT_ROOT/ && npm run init-environment", + "setup-local": "export PROJECT_ROOT=$(pwd) && npm ci --foreground-scripts && cd $PROJECT_ROOT/examples/cdk && npm ci && cd $PROJECT_ROOT/examples/sam && npm ci && cd $PROJECT_ROOT/ && npm run init-environment", "build": "npm run build -ws", "postversion": "git push && git push --tags", "docs-website-build-run": "npm run docs-buildDockerImage && npm run docs-runLocalDocker", @@ -86,4 +87,4 @@ "dependencies": { "hosted-git-info": "^6.1.1" } -} +} \ No newline at end of file diff --git a/packages/commons/tests/utils/e2eUtils.ts b/packages/commons/tests/utils/e2eUtils.ts index 8c94cb7b6f..5d2e75f4a5 100644 --- a/packages/commons/tests/utils/e2eUtils.ts +++ b/packages/commons/tests/utils/e2eUtils.ts @@ -6,8 +6,12 @@ * to interact with services. */ import { App, CfnOutput, Stack } from 'aws-cdk-lib'; -import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs'; +import { + NodejsFunction, + NodejsFunctionProps +} from 'aws-cdk-lib/aws-lambda-nodejs'; import { Runtime, Tracing } from 'aws-cdk-lib/aws-lambda'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; import * as AWS from 'aws-sdk'; import { InvocationLogs } from './InvocationLogs'; @@ -31,6 +35,8 @@ export type StackWithLambdaFunctionOptions = { environment: {[key: string]: string} logGroupOutputKey?: string runtime: string + bundling?: NodejsFunctionProps['bundling'] + layers?: NodejsFunctionProps['layers'] }; type FunctionPayload = {[key: string]: string | boolean | number}; @@ -40,12 +46,15 @@ export const isValidRuntimeKey = (runtime: string): runtime is TestRuntimesKey = export const createStackWithLambdaFunction = (params: StackWithLambdaFunctionOptions): Stack => { const stack = new Stack(params.app, params.stackName); - const testFunction = new lambda.NodejsFunction(stack, `testFunction`, { + const testFunction = new NodejsFunction(stack, `testFunction`, { functionName: params.functionName, entry: params.functionEntry, tracing: params.tracing, environment: params.environment, runtime: TEST_RUNTIMES[params.runtime as TestRuntimesKey], + bundling: params.bundling, + layers: params.layers, + logRetention: RetentionDays.ONE_DAY, }); if (params.logGroupOutputKey) { From 6294f9401c109be81af7b9ec684d182c5479942e Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 21 Feb 2023 12:10:29 +0100 Subject: [PATCH 30/56] tests(parameters): integration tests for `AppConfigProvider` (#1287) * tests: appconfigprovider e2e tests * tests: completed e2e tests * chore: added effect in policy * chore: add missing semicolon * chore: fix indentation * chore: documented explicit dependency --- ...pConfigProvider.class.test.functionCode.ts | 118 ++++++ .../tests/e2e/appConfigProvider.class.test.ts | 360 ++++++++++++++++++ .../tests/helpers/cdkAspectGrantAccess.ts | 30 +- .../tests/helpers/parametersUtils.ts | 114 +++++- .../helpers/sdkMiddlewareRequestCounter.ts | 13 +- 5 files changed, 626 insertions(+), 9 deletions(-) create mode 100644 packages/parameters/tests/e2e/appConfigProvider.class.test.functionCode.ts create mode 100644 packages/parameters/tests/e2e/appConfigProvider.class.test.ts diff --git a/packages/parameters/tests/e2e/appConfigProvider.class.test.functionCode.ts b/packages/parameters/tests/e2e/appConfigProvider.class.test.functionCode.ts new file mode 100644 index 0000000000..589fd475ed --- /dev/null +++ b/packages/parameters/tests/e2e/appConfigProvider.class.test.functionCode.ts @@ -0,0 +1,118 @@ +import { Context } from 'aws-lambda'; +import { + AppConfigProvider, +} from '../../src/appconfig'; +import { + AppConfigGetOptionsInterface, +} from '../../src/types'; +import { TinyLogger } from '../helpers/tinyLogger'; +import { middleware } from '../helpers/sdkMiddlewareRequestCounter'; +import { AppConfigDataClient } from '@aws-sdk/client-appconfigdata'; + +// We use a custom logger to log pure JSON objects to stdout +const logger = new TinyLogger(); + +const application = process.env.APPLICATION_NAME || 'my-app'; +const environment = process.env.ENVIRONMENT_NAME || 'my-env'; +const freeFormJsonName = process.env.FREEFORM_JSON_NAME || 'freeform-json'; +const freeFormYamlName = process.env.FREEFORM_YAML_NAME || 'freeform-yaml'; +const freeFormPlainTextNameA = process.env.FREEFORM_PLAIN_TEXT_NAME_A || 'freeform-plain-text'; +const freeFormPlainTextNameB = process.env.FREEFORM_PLAIN_TEXT_NAME_B || 'freeform-plain-text'; +const featureFlagName = process.env.FEATURE_FLAG_NAME || 'feature-flag'; + +const defaultProvider = new AppConfigProvider({ + application, + environment, +}); +// Provider test +const customClient = new AppConfigDataClient({}); +customClient.middlewareStack.use(middleware); +const providerWithMiddleware = new AppConfigProvider({ + awsSdkV3Client: customClient, + application, + environment, +}); + +// Use provider specified, or default to main one & return it with cache cleared +const resolveProvider = (provider?: AppConfigProvider): AppConfigProvider => { + const resolvedProvider = provider ? provider : defaultProvider; + resolvedProvider.clearCache(); + + return resolvedProvider; +}; + +// Helper function to call get() and log the result +const _call_get = async ( + paramName: string, + testName: string, + options?: AppConfigGetOptionsInterface, + provider?: AppConfigProvider, +): Promise => { + try { + const currentProvider = resolveProvider(provider); + + const parameterValue = await currentProvider.get(paramName, options); + logger.log({ + test: testName, + value: parameterValue + }); + } catch (err) { + logger.log({ + test: testName, + error: err.message + }); + } +}; + +export const handler = async (_event: unknown, _context: Context): Promise => { + // Test 1 - get a single parameter as-is (no transformation) + await _call_get(freeFormPlainTextNameA, 'get'); + + // Test 2 - get a free-form JSON and apply binary transformation (should return a stringified JSON) + await _call_get(freeFormJsonName, 'get-freeform-json-binary', { transform: 'binary' }); + + // Test 3 - get a free-form YAML and apply binary transformation (should return a string-encoded YAML) + await _call_get(freeFormYamlName, 'get-freeform-yaml-binary', { transform: 'binary' }); + + // Test 4 - get a free-form plain text and apply binary transformation (should return a string) + await _call_get(freeFormPlainTextNameB, 'get-freeform-plain-text-binary', { transform: 'binary' }); + + // Test 5 - get a feature flag and apply binary transformation (should return a stringified JSON) + await _call_get(featureFlagName, 'get-feature-flag-binary', { transform: 'binary' }); + + // Test 6 + // get parameter twice with middleware, which counts the number of requests, we check later if we only called AppConfig API once + try { + providerWithMiddleware.clearCache(); + middleware.counter = 0; + await providerWithMiddleware.get(freeFormPlainTextNameA); + await providerWithMiddleware.get(freeFormPlainTextNameA); + logger.log({ + test: 'get-cached', + value: middleware.counter // should be 1 + }); + } catch (err) { + logger.log({ + test: 'get-cached', + error: err.message + }); + } + + // Test 7 + // get parameter twice, but force fetch 2nd time, we count number of SDK requests and check that we made two API calls + try { + providerWithMiddleware.clearCache(); + middleware.counter = 0; + await providerWithMiddleware.get(freeFormPlainTextNameA); + await providerWithMiddleware.get(freeFormPlainTextNameA, { forceFetch: true }); + logger.log({ + test: 'get-forced', + value: middleware.counter // should be 2 + }); + } catch (err) { + logger.log({ + test: 'get-forced', + error: err.message + }); + } +}; \ No newline at end of file diff --git a/packages/parameters/tests/e2e/appConfigProvider.class.test.ts b/packages/parameters/tests/e2e/appConfigProvider.class.test.ts new file mode 100644 index 0000000000..b32504444a --- /dev/null +++ b/packages/parameters/tests/e2e/appConfigProvider.class.test.ts @@ -0,0 +1,360 @@ +/** + * Test AppConfigProvider class + * + * @group e2e/parameters/appconfig/class + */ +import path from 'path'; +import { App, Stack, Aspects } from 'aws-cdk-lib'; +import { v4 } from 'uuid'; +import { + generateUniqueName, + isValidRuntimeKey, + createStackWithLambdaFunction, + invokeFunction, +} from '../../../commons/tests/utils/e2eUtils'; +import { InvocationLogs } from '../../../commons/tests/utils/InvocationLogs'; +import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; +import { ResourceAccessGranter } from '../helpers/cdkAspectGrantAccess'; +import { + RESOURCE_NAME_PREFIX, + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT +} from './constants'; +import { + createBaseAppConfigResources, + createAppConfigConfigurationProfile, +} from '../helpers/parametersUtils'; + +const runtime: string = process.env.RUNTIME || 'nodejs18x'; + +if (!isValidRuntimeKey(runtime)) { + throw new Error(`Invalid runtime key value: ${runtime}`); +} + +const uuid = v4(); +const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'appConfigProvider'); +const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'appConfigProvider'); +const lambdaFunctionCodeFile = 'appConfigProvider.class.test.functionCode.ts'; + +const invocationCount = 1; + +const applicationName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'app'); +const environmentName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'env'); +const deploymentStrategyName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'immediate'); +const freeFormJsonName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'freeFormJson'); +const freeFormYamlName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'freeFormYaml'); +const freeFormPlainTextNameA = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'freeFormPlainTextA'); +const freeFormPlainTextNameB = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'freeFormPlainTextB'); +const featureFlagName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'featureFlag'); + +const freeFormJsonValue = { + foo: 'bar', +}; +const freeFormYamlValue = `foo: bar +`; +const freeFormPlainTextValue = 'foo'; +const featureFlagValue = { + version: '1', + flags: { + myFeatureFlag: { + 'name': 'myFeatureFlag', + } + }, + values: { + myFeatureFlag: { + enabled: true, + } + } +}; + +const integTestApp = new App(); +let stack: Stack; + +/** + * This test suite deploys a CDK stack with a Lambda function and a number of AppConfig parameters. + * The function code uses the Parameters utility to retrieve the parameters. + * It then logs the values to CloudWatch Logs as JSON objects. + * + * Once the stack is deployed, the Lambda function is invoked and the CloudWatch Logs are retrieved. + * The logs are then parsed and the values are checked against the expected values for each test case. + * + * The stack creates an AppConfig application and environment, and then creates a number configuration + * profiles, each with a different type of parameter. + * + * The parameters created are: + * - Free-form JSON + * - Free-form YAML + * - 2x Free-form plain text + * - Feature flag + * + * These parameters allow to retrieve the values and test some transformations. + * + * The tests are: + * + * Test 1 + * get a single parameter as-is (no transformation) + * + * Test 2 + * get a free-form JSON and apply binary transformation (should return a stringified JSON) + * + * Test 3 + * get a free-form YAML and apply binary transformation (should return a string-encoded YAML) + * + * Test 4 + * get a free-form plain text and apply binary transformation (should return a string) + * + * Test 5 + * get a feature flag and apply binary transformation (should return a stringified JSON) + * + * Test 6 + * get parameter twice with middleware, which counts the number of requests, + * we check later if we only called AppConfig API once + * + * Test 7 + * get parameter twice, but force fetch 2nd time, we count number of SDK requests and + * check that we made two API calls + * + * Note: To avoid race conditions, we add a dependency between each pair of configuration profiles. + * This allows us to influence the order of creation and ensure that each configuration profile + * is created after the previous one. This is necessary because we share the same AppConfig + * application and environment for all tests. + */ +describe(`parameters E2E tests (appConfigProvider) for runtime ${runtime}`, () => { + + let invocationLogs: InvocationLogs[]; + const encoder = new TextEncoder(); + + beforeAll(async () => { + // Create a stack with a Lambda function + stack = createStackWithLambdaFunction({ + app: integTestApp, + stackName, + functionName, + functionEntry: path.join(__dirname, lambdaFunctionCodeFile), + environment: { + UUID: uuid, + + // Values(s) to be used by Parameters in the Lambda function + APPLICATION_NAME: applicationName, + ENVIRONMENT_NAME: environmentName, + FREEFORM_JSON_NAME: freeFormJsonName, + FREEFORM_YAML_NAME: freeFormYamlName, + FREEFORM_PLAIN_TEXT_NAME_A: freeFormPlainTextNameA, + FREEFORM_PLAIN_TEXT_NAME_B: freeFormPlainTextNameB, + FEATURE_FLAG_NAME: featureFlagName, + }, + runtime, + }); + + // Create the base resources for an AppConfig application. + const { + application, + environment, + deploymentStrategy + } = createBaseAppConfigResources({ + stack, + applicationName, + environmentName, + deploymentStrategyName, + }); + + // Create configuration profiles for tests. + const freeFormJson = createAppConfigConfigurationProfile({ + stack, + application, + environment, + deploymentStrategy, + name: freeFormJsonName, + type: 'AWS.Freeform', + content: { + content: JSON.stringify(freeFormJsonValue), + contentType: 'application/json', + } + }); + + const freeFormYaml = createAppConfigConfigurationProfile({ + stack, + application, + environment, + deploymentStrategy, + name: freeFormYamlName, + type: 'AWS.Freeform', + content: { + content: freeFormYamlValue, + contentType: 'application/x-yaml', + } + }); + freeFormYaml.node.addDependency(freeFormJson); + + const freeFormPlainTextA = createAppConfigConfigurationProfile({ + stack, + application, + environment, + deploymentStrategy, + name: freeFormPlainTextNameA, + type: 'AWS.Freeform', + content: { + content: freeFormPlainTextValue, + contentType: 'text/plain', + } + }); + freeFormPlainTextA.node.addDependency(freeFormYaml); + + const freeFormPlainTextB = createAppConfigConfigurationProfile({ + stack, + application, + environment, + deploymentStrategy, + name: freeFormPlainTextNameB, + type: 'AWS.Freeform', + content: { + content: freeFormPlainTextValue, + contentType: 'text/plain', + } + }); + freeFormPlainTextB.node.addDependency(freeFormPlainTextA); + + const featureFlag = createAppConfigConfigurationProfile({ + stack, + application, + environment, + deploymentStrategy, + name: featureFlagName, + type: 'AWS.AppConfig.FeatureFlags', + content: { + content: JSON.stringify(featureFlagValue), + contentType: 'application/json', + } + }); + featureFlag.node.addDependency(freeFormPlainTextB); + + // Grant access to the Lambda function to the AppConfig resources. + Aspects.of(stack).add(new ResourceAccessGranter([ + freeFormJson, + freeFormYaml, + freeFormPlainTextA, + freeFormPlainTextB, + featureFlag, + ])); + + // Deploy the stack + await deployStack(integTestApp, stack); + + // and invoke the Lambda function + invocationLogs = await invokeFunction(functionName, invocationCount, 'SEQUENTIAL'); + + }, SETUP_TIMEOUT); + + describe('AppConfigProvider usage', () => { + + // Test 1 - get a single parameter as-is (no transformation) + it('should retrieve single parameter', () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[0]); + + expect(testLog).toStrictEqual({ + test: 'get', + value: JSON.parse( + JSON.stringify( + encoder.encode(freeFormPlainTextValue) + ) + ), + }); + + }); + + // Test 2 - get a free-form JSON and apply binary transformation + // (should return a stringified JSON) + it('should retrieve single free-form JSON parameter with binary transformation', () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[1]); + + expect(testLog).toStrictEqual({ + test: 'get-freeform-json-binary', + value: JSON.stringify(freeFormJsonValue), + }); + + }); + + // Test 3 - get a free-form YAML and apply binary transformation + // (should return a string-encoded YAML) + it('should retrieve single free-form YAML parameter with binary transformation', () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[2]); + + expect(testLog).toStrictEqual({ + test: 'get-freeform-yaml-binary', + value: freeFormYamlValue, + }); + + }); + + // Test 4 - get a free-form plain text and apply binary transformation + // (should return a string) + it('should retrieve single free-form plain text parameter with binary transformation', () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[3]); + + expect(testLog).toStrictEqual({ + test: 'get-freeform-plain-text-binary', + value: freeFormPlainTextValue, + }); + + }); + + // Test 5 - get a feature flag and apply binary transformation + // (should return a stringified JSON) + it('should retrieve single feature flag parameter with binary transformation', () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[4]); + + expect(testLog).toStrictEqual({ + test: 'get-feature-flag-binary', + value: JSON.stringify(featureFlagValue.values), + }); + + }); + + // Test 6 - get parameter twice with middleware, which counts the number + // of requests, we check later if we only called AppConfig API once + it('should retrieve single parameter cached', () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[5]); + + expect(testLog).toStrictEqual({ + test: 'get-cached', + value: 1 + }); + + }, TEST_CASE_TIMEOUT); + + // Test 7 - get parameter twice, but force fetch 2nd time, + // we count number of SDK requests and check that we made two API calls + it('should retrieve single parameter twice without caching', async () => { + + const logs = invocationLogs[0].getFunctionLogs(); + const testLog = InvocationLogs.parseFunctionLog(logs[6]); + + expect(testLog).toStrictEqual({ + test: 'get-forced', + value: 2 + }); + + }, TEST_CASE_TIMEOUT); + + }); + + afterAll(async () => { + if (!process.env.DISABLE_TEARDOWN) { + await destroyStack(integTestApp, stack); + } + }, TEARDOWN_TIMEOUT); + +}); \ No newline at end of file diff --git a/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts b/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts index f75848acd5..68f755a3fc 100644 --- a/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts +++ b/packages/parameters/tests/helpers/cdkAspectGrantAccess.ts @@ -1,9 +1,10 @@ -import { IAspect } from 'aws-cdk-lib'; +import { IAspect, Stack } from 'aws-cdk-lib'; import { IConstruct } from 'constructs'; import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; import { Table } from 'aws-cdk-lib/aws-dynamodb'; import { PolicyStatement, Effect } from 'aws-cdk-lib/aws-iam'; import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; +import { CfnDeployment } from 'aws-cdk-lib/aws-appconfig'; import { StringParameter, IStringParameter } from 'aws-cdk-lib/aws-ssm'; const isStringParameterGeneric = (parameter: IConstruct): parameter is StringParameter | IStringParameter => @@ -24,9 +25,9 @@ const isStringParameterGeneric = (parameter: IConstruct): parameter is StringPar * @see {@link https://docs.aws.amazon.com/cdk/v2/guide/aspects.html CDK Docs - Aspects} */ export class ResourceAccessGranter implements IAspect { - private readonly resources: Table[] | Secret[] | StringParameter[] | IStringParameter[]; + private readonly resources: Table[] | Secret[] | StringParameter[] | IStringParameter[] | CfnDeployment[]; - public constructor(resources: Table[] | Secret[] | StringParameter[] | IStringParameter[]) { + public constructor(resources: Table[] | Secret[] | StringParameter[] | IStringParameter[] | CfnDeployment[]) { this.resources = resources; } @@ -35,7 +36,7 @@ export class ResourceAccessGranter implements IAspect { if (node instanceof NodejsFunction) { // Grant access to the resources - this.resources.forEach((resource: Table | Secret | StringParameter | IStringParameter) => { + this.resources.forEach((resource: Table | Secret | StringParameter | IStringParameter | CfnDeployment) => { if (resource instanceof Table) { resource.grantReadData(node); @@ -45,6 +46,7 @@ export class ResourceAccessGranter implements IAspect { resource.grantRead(node); } else if (isStringParameterGeneric(resource)) { resource.grantRead(node); + // Grant access also to the path of the parameter node.addToRolePolicy( new PolicyStatement({ @@ -57,9 +59,27 @@ export class ResourceAccessGranter implements IAspect { ], }), ); + } else if (resource instanceof CfnDeployment) { + const appConfigConfigurationArn = Stack.of(node).formatArn({ + service: 'appconfig', + resource: `application/${resource.applicationId}/environment/${resource.environmentId}/configuration/${resource.configurationProfileId}`, + }); + + node.addToRolePolicy( + new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'appconfig:StartConfigurationSession', + 'appconfig:GetLatestConfiguration', + ], + resources: [ + appConfigConfigurationArn, + ], + }), + ); } }); } } -} \ No newline at end of file +} diff --git a/packages/parameters/tests/helpers/parametersUtils.ts b/packages/parameters/tests/helpers/parametersUtils.ts index e503fddd2b..3bd785bbed 100644 --- a/packages/parameters/tests/helpers/parametersUtils.ts +++ b/packages/parameters/tests/helpers/parametersUtils.ts @@ -6,6 +6,14 @@ import { Runtime } from 'aws-cdk-lib/aws-lambda'; import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; import { StringParameter, IStringParameter } from 'aws-cdk-lib/aws-ssm'; import { Table, TableProps, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; +import { + CfnApplication, + CfnConfigurationProfile, + CfnDeployment, + CfnDeploymentStrategy, + CfnEnvironment, + CfnHostedConfigurationVersion, +} from 'aws-cdk-lib/aws-appconfig'; export type CreateDynamoDBTableOptions = { stack: Stack @@ -23,6 +31,108 @@ const createDynamoDBTable = (options: CreateDynamoDBTableOptions): Table => { return new Table(stack, id, props); }; +export type AppConfigResourcesOptions = { + stack: Stack + applicationName: string + environmentName: string + deploymentStrategyName: string +}; + +type AppConfigResourcesOutput = { + application: CfnApplication + environment: CfnEnvironment + deploymentStrategy: CfnDeploymentStrategy +}; + +/** + * Utility function to create the base resources for an AppConfig application. + */ +const createBaseAppConfigResources = (options: AppConfigResourcesOptions): AppConfigResourcesOutput => { + const { + stack, + applicationName, + environmentName, + deploymentStrategyName, + } = options; + + // create a new app config application. + const application = new CfnApplication( + stack, + 'application', + { + name: applicationName, + } + ); + + const environment = new CfnEnvironment(stack, 'environment', { + name: environmentName, + applicationId: application.ref, + }); + + const deploymentStrategy = new CfnDeploymentStrategy(stack, 'deploymentStrategy', { + name: deploymentStrategyName, + deploymentDurationInMinutes: 0, + growthFactor: 100, + replicateTo: 'NONE', + finalBakeTimeInMinutes: 0, + }); + + return { + application, + environment, + deploymentStrategy, + }; +}; + +export type CreateAppConfigConfigurationProfileOptions = { + stack: Stack + name: string + application: CfnApplication + environment: CfnEnvironment + deploymentStrategy: CfnDeploymentStrategy + type: 'AWS.Freeform' | 'AWS.AppConfig.FeatureFlags' + content: { + contentType: 'application/json' | 'application/x-yaml' | 'text/plain' + content: string + } +}; + +/** + * Utility function to create an AppConfig configuration profile and deployment. + */ +const createAppConfigConfigurationProfile = (options: CreateAppConfigConfigurationProfileOptions): CfnDeployment => { + const { + stack, + name, + application, + environment, + deploymentStrategy, + type, + content, + } = options; + + const configProfile = new CfnConfigurationProfile(stack, `${name}-configProfile`, { + name, + applicationId: application.ref, + locationUri: 'hosted', + type, + }); + + const configVersion = new CfnHostedConfigurationVersion(stack, `${name}-configVersion`, { + applicationId: application.ref, + configurationProfileId: configProfile.ref, + ...content + }); + + return new CfnDeployment(stack, `${name}-deployment`, { + applicationId: application.ref, + configurationProfileId: configProfile.ref, + configurationVersion: configVersion.ref, + deploymentStrategyId: deploymentStrategy.ref, + environmentId: environment.ref, + }); +}; + export type CreateSecureStringProviderOptions = { stack: Stack parametersPrefix: string @@ -93,6 +203,8 @@ const createSSMSecureString = (options: CreateSSMSecureStringOptions): IStringPa export { createDynamoDBTable, + createBaseAppConfigResources, + createAppConfigConfigurationProfile, createSSMSecureString, createSecureStringProvider, -}; \ No newline at end of file +}; diff --git a/packages/parameters/tests/helpers/sdkMiddlewareRequestCounter.ts b/packages/parameters/tests/helpers/sdkMiddlewareRequestCounter.ts index f71a6173eb..0fbff71084 100644 --- a/packages/parameters/tests/helpers/sdkMiddlewareRequestCounter.ts +++ b/packages/parameters/tests/helpers/sdkMiddlewareRequestCounter.ts @@ -17,9 +17,16 @@ export const middleware = { applyToStack: (stack) => { // Middleware added to mark start and end of an complete API call. stack.add( - (next, _context) => async (args) => { - // Increment counter - middleware.counter++; + (next, context) => async (args) => { + // We only want to count API calls to retrieve data, + // not the StartConfigurationSessionCommand + if ( + context.clientName !== 'AppConfigDataClient' || + context.commandName !== 'StartConfigurationSessionCommand' + ) { + // Increment counter + middleware.counter++; + } // Call next middleware return await next(args); From bc9b1d435536792aa4cfb9462b94669d2815fcbb Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 21 Feb 2023 16:04:34 +0100 Subject: [PATCH 31/56] refactor(parameters): move table seeding into `AwsCustomResource` (#1317) * chore: add run e2e * Revert "chore: add run e2e" This reverts commit 6ba8d6145f5919667821ff75d0b1bdcca785e8be. * tests: refactored table seeding * chore: fix linting --- .../tests/e2e/dynamoDBProvider.class.test.ts | 134 ++++++++++-------- .../tests/helpers/parametersUtils.ts | 34 ++++- 2 files changed, 108 insertions(+), 60 deletions(-) diff --git a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts index ec468cd2b5..62e8bd6c39 100644 --- a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts +++ b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts @@ -6,8 +6,6 @@ import path from 'path'; import { AttributeType } from 'aws-cdk-lib/aws-dynamodb'; import { App, Stack, Aspects } from 'aws-cdk-lib'; -import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb'; -import { marshall } from '@aws-sdk/util-dynamodb'; import { v4 } from 'uuid'; import { generateUniqueName, @@ -24,7 +22,7 @@ import { TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT } from './constants'; -import { createDynamoDBTable } from '../helpers/parametersUtils'; +import { createDynamoDBTable, putDynamoDBItem } from '../helpers/parametersUtils'; const runtime: string = process.env.RUNTIME || 'nodejs18x'; @@ -37,8 +35,6 @@ const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'dynam const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'dynamoDBProvider'); const lambdaFunctionCodeFile = 'dynamoDBProvider.class.test.functionCode.ts'; -const dynamoDBClient = new DynamoDBClient({}); - const invocationCount = 1; // Parameters to be used by Parameters in the Lambda function @@ -209,102 +205,122 @@ describe(`parameters E2E tests (dynamoDBProvider) for runtime: ${runtime}`, () = ddbTabelGetMultipleCustomKeys, ])); - // Deploy the stack - await deployStack(integTestApp, stack); - // Seed tables with test data // Test 1 - await dynamoDBClient.send(new PutItemCommand({ - TableName: tableGet, - Item: marshall({ + putDynamoDBItem({ + stack, + id: 'my-param-test1', + table: ddbTableGet, + item: { id: 'my-param', value: 'foo', - }), - })); + }, + }); // Test 2 - await dynamoDBClient.send(new PutItemCommand({ - TableName: tableGetMultiple, - Item: marshall({ + putDynamoDBItem({ + stack, + id: 'my-param-test2-a', + table: ddbTableGetMultiple, + item: { id: 'my-params', sk: 'config', value: 'bar', - }), - })); - await dynamoDBClient.send(new PutItemCommand({ - TableName: tableGetMultiple, - Item: marshall({ + }, + }); + putDynamoDBItem({ + stack, + id: 'my-param-test2-b', + table: ddbTableGetMultiple, + item: { id: 'my-params', sk: 'key', value: 'baz', - }), - })); + }, + }); // Test 3 - await dynamoDBClient.send(new PutItemCommand({ - TableName: tableGetCustomkeys, - Item: marshall({ + putDynamoDBItem({ + stack, + id: 'my-param-test3', + table: ddbTableGetCustomKeys, + item: { [keyAttr]: 'my-param', [valueAttr]: 'foo', - }), - })); + }, + }); // Test 4 - await dynamoDBClient.send(new PutItemCommand({ - TableName: tableGetMultipleCustomkeys, - Item: marshall({ + putDynamoDBItem({ + stack, + id: 'my-param-test4-a', + table: ddbTabelGetMultipleCustomKeys, + item: { [keyAttr]: 'my-params', [sortAttr]: 'config', [valueAttr]: 'bar', - }), - })); - await dynamoDBClient.send(new PutItemCommand({ - TableName: tableGetMultipleCustomkeys, - Item: marshall({ + }, + }); + putDynamoDBItem({ + stack, + id: 'my-param-test4-b', + table: ddbTabelGetMultipleCustomKeys, + item: { [keyAttr]: 'my-params', [sortAttr]: 'key', [valueAttr]: 'baz', - }), - })); + }, + }); // Test 5 - await dynamoDBClient.send(new PutItemCommand({ - TableName: tableGet, - Item: marshall({ + putDynamoDBItem({ + stack, + id: 'my-param-test5', + table: ddbTableGet, + item: { id: 'my-param-json', value: JSON.stringify({ foo: 'bar' }), - }), - })); + }, + }); // Test 6 - await dynamoDBClient.send(new PutItemCommand({ - TableName: tableGet, - Item: marshall({ + putDynamoDBItem({ + stack, + id: 'my-param-test6', + table: ddbTableGet, + item: { id: 'my-param-binary', value: 'YmF6', // base64 encoded 'baz' - }), - })); - + }, + }); + // Test 7 - await dynamoDBClient.send(new PutItemCommand({ - TableName: tableGetMultiple, - Item: marshall({ + putDynamoDBItem({ + stack, + id: 'my-param-test7-a', + table: ddbTableGetMultiple, + item: { id: 'my-encoded-params', sk: 'config.json', value: JSON.stringify({ foo: 'bar' }), - }), - })); - await dynamoDBClient.send(new PutItemCommand({ - TableName: tableGetMultiple, - Item: marshall({ + }, + }); + putDynamoDBItem({ + stack, + id: 'my-param-test7-b', + table: ddbTableGetMultiple, + item: { id: 'my-encoded-params', sk: 'key.binary', value: 'YmF6', // base64 encoded 'baz' - }), - })); + }, + }); // Test 8 & 9 use the same items as Test 1 + // Deploy the stack + await deployStack(integTestApp, stack); + // and invoke the Lambda function invocationLogs = await invokeFunction(functionName, invocationCount, 'SEQUENTIAL'); diff --git a/packages/parameters/tests/helpers/parametersUtils.ts b/packages/parameters/tests/helpers/parametersUtils.ts index 3bd785bbed..3e769f9d3e 100644 --- a/packages/parameters/tests/helpers/parametersUtils.ts +++ b/packages/parameters/tests/helpers/parametersUtils.ts @@ -1,5 +1,5 @@ import { Stack, RemovalPolicy, CustomResource, Duration } from 'aws-cdk-lib'; -import { Provider } from 'aws-cdk-lib/custom-resources'; +import { PhysicalResourceId, Provider } from 'aws-cdk-lib/custom-resources'; import { RetentionDays } from 'aws-cdk-lib/aws-logs'; import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; import { Runtime } from 'aws-cdk-lib/aws-lambda'; @@ -14,6 +14,11 @@ import { CfnEnvironment, CfnHostedConfigurationVersion, } from 'aws-cdk-lib/aws-appconfig'; +import { + AwsCustomResource, + AwsCustomResourcePolicy +} from 'aws-cdk-lib/custom-resources'; +import { marshall } from '@aws-sdk/util-dynamodb'; export type CreateDynamoDBTableOptions = { stack: Stack @@ -201,10 +206,37 @@ const createSSMSecureString = (options: CreateSSMSecureStringOptions): IStringPa return param; }; +export type PutDynamoDBItemOptions = { + stack: Stack + id: string + table: Table + item: Record +}; + +const putDynamoDBItem = async (options: PutDynamoDBItemOptions): Promise => { + const { stack, id, table, item } = options; + + new AwsCustomResource(stack, id, { + onCreate: { + service: 'DynamoDB', + action: 'putItem', + parameters: { + TableName: table.tableName, + Item: marshall(item), + }, + physicalResourceId: PhysicalResourceId.of(id), + }, + policy: AwsCustomResourcePolicy.fromSdkCalls({ + resources: [table.tableArn], + }), + }); +}; + export { createDynamoDBTable, createBaseAppConfigResources, createAppConfigConfigurationProfile, createSSMSecureString, createSecureStringProvider, + putDynamoDBItem, }; From af280dc3df6495bbfaa95d29026d2dea1b8c01e9 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 21 Feb 2023 16:21:45 +0100 Subject: [PATCH 32/56] tests(parameters): update package to run as part of e2e tests (#1318) * tests: update package.json to run e2e tests * chore: add package to test matrix --- .github/workflows/run-e2e-tests.yml | 2 +- packages/parameters/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 55775c82e1..ff56739b19 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -19,7 +19,7 @@ jobs: contents: read strategy: matrix: - package: [logger, metrics, tracer] + package: [logger, metrics, tracer, parameters] version: [14, 16, 18] fail-fast: false steps: diff --git a/packages/parameters/package.json b/packages/parameters/package.json index 32c9d56d69..469e72f70b 100644 --- a/packages/parameters/package.json +++ b/packages/parameters/package.json @@ -16,7 +16,7 @@ "test:e2e:nodejs14x": "RUNTIME=nodejs14x jest --group=e2e", "test:e2e:nodejs16x": "RUNTIME=nodejs16x jest --group=e2e", "test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e", - "test:e2e": "echo \"Not implemented\"", + "test:e2e": "jest --group=e2e", "watch": "jest --watch", "build": "tsc", "lint": "eslint --ext .ts --no-error-on-unmatched-pattern src tests", From 4530be253b26dc0aca9b8ca0c316de14d0d099af Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 21 Feb 2023 16:48:24 +0100 Subject: [PATCH 33/56] refactor(parameters): moved ssm resource creation to `AwsCustomResource` (#1319) * refactor: moved ssm resource creation to AwsCustomResource * chore: removed unused function --- .../tests/e2e/ssmProvider.class.test.ts | 14 +--- .../tests/helpers/parametersUtils.ts | 80 ++++++------------- .../tests/helpers/ssmSecureStringCdk.ts | 54 ------------- 3 files changed, 25 insertions(+), 123 deletions(-) delete mode 100644 packages/parameters/tests/helpers/ssmSecureStringCdk.ts diff --git a/packages/parameters/tests/e2e/ssmProvider.class.test.ts b/packages/parameters/tests/e2e/ssmProvider.class.test.ts index 51701649e5..fb7f3e56f4 100644 --- a/packages/parameters/tests/e2e/ssmProvider.class.test.ts +++ b/packages/parameters/tests/e2e/ssmProvider.class.test.ts @@ -22,10 +22,7 @@ import { TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT } from './constants'; -import { - createSecureStringProvider, - createSSMSecureString -} from '../helpers/parametersUtils'; +import { createSSMSecureString } from '../helpers/parametersUtils'; const runtime: string = process.env.RUNTIME || 'nodejs18x'; @@ -127,13 +124,6 @@ describe(`parameters E2E tests (ssmProvider) for runtime: ${runtime}`, () => { runtime, }); - // Create Custom Resource provider: - // will be used to create some SSM parameters not supported by CDK - const provider = createSecureStringProvider({ - stack, - parametersPrefix: `${RESOURCE_NAME_PREFIX}-${runtime}-${uuid.substring(0,5)}` - }); - // Create SSM parameters const parameterGetA = new StringParameter(stack, 'Param-a', { parameterName: paramA, @@ -146,7 +136,6 @@ describe(`parameters E2E tests (ssmProvider) for runtime: ${runtime}`, () => { const parameterEncryptedA = createSSMSecureString({ stack, - provider, id: 'Param-encrypted-a', name: paramEncryptedA, value: paramEncryptedAValue, @@ -154,7 +143,6 @@ describe(`parameters E2E tests (ssmProvider) for runtime: ${runtime}`, () => { const parameterEncryptedB = createSSMSecureString({ stack, - provider, id: 'Param-encrypted-b', name: paramEncryptedB, value: paramEncryptedBValue, diff --git a/packages/parameters/tests/helpers/parametersUtils.ts b/packages/parameters/tests/helpers/parametersUtils.ts index 3e769f9d3e..89a8c52efc 100644 --- a/packages/parameters/tests/helpers/parametersUtils.ts +++ b/packages/parameters/tests/helpers/parametersUtils.ts @@ -1,9 +1,5 @@ -import { Stack, RemovalPolicy, CustomResource, Duration } from 'aws-cdk-lib'; -import { PhysicalResourceId, Provider } from 'aws-cdk-lib/custom-resources'; -import { RetentionDays } from 'aws-cdk-lib/aws-logs'; -import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; -import { Runtime } from 'aws-cdk-lib/aws-lambda'; -import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; +import { Stack, RemovalPolicy } from 'aws-cdk-lib'; +import { PhysicalResourceId } from 'aws-cdk-lib/custom-resources'; import { StringParameter, IStringParameter } from 'aws-cdk-lib/aws-ssm'; import { Table, TableProps, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; import { @@ -138,70 +134,43 @@ const createAppConfigConfigurationProfile = (options: CreateAppConfigConfigurati }); }; -export type CreateSecureStringProviderOptions = { - stack: Stack - parametersPrefix: string -}; - -const createSecureStringProvider = (options: CreateSecureStringProviderOptions): Provider => { - const { stack, parametersPrefix } = options; - - const ssmSecureStringHandlerFn = new NodejsFunction( - stack, - 'ssm-securestring-handler', - { - entry: 'tests/helpers/ssmSecureStringCdk.ts', - handler: 'handler', - bundling: { - minify: true, - sourceMap: true, - target: 'es2020', - externalModules: [], - }, - runtime: Runtime.NODEJS_18_X, - timeout: Duration.seconds(15), - }); - ssmSecureStringHandlerFn.addToRolePolicy( - new PolicyStatement({ - actions: [ - 'ssm:PutParameter', - 'ssm:DeleteParameter', - ], - resources: [ - `arn:aws:ssm:${stack.region}:${stack.account}:parameter/${parametersPrefix}*`, - ], - }), - ); - - return new Provider(stack, 'ssm-secure-string-provider', { - onEventHandler: ssmSecureStringHandlerFn, - logRetention: RetentionDays.ONE_DAY, - }); -}; - export type CreateSSMSecureStringOptions = { stack: Stack - provider: Provider id: string name: string value: string }; const createSSMSecureString = (options: CreateSSMSecureStringOptions): IStringParameter => { - const { stack, provider, id, name, value } = options; + const { stack, id, name, value } = options; - new CustomResource(stack, `custom-${id}`, { - serviceToken: provider.serviceToken, - properties: { - Name: name, - Value: value, + const paramCreator = new AwsCustomResource(stack, `create-${id}`, { + onCreate: { + service: 'SSM', + action: 'putParameter', + parameters: { + Name: name, + Value: value, + Type: 'SecureString', + }, + physicalResourceId: PhysicalResourceId.of(id), }, + onDelete: { + service: 'SSM', + action: 'deleteParameter', + parameters: { + Name: name, + }, + }, + policy: AwsCustomResourcePolicy.fromSdkCalls({ + resources: AwsCustomResourcePolicy.ANY_RESOURCE, + }), }); const param = StringParameter.fromSecureStringParameterAttributes(stack, id, { parameterName: name, }); - param.node.addDependency(provider); + param.node.addDependency(paramCreator); return param; }; @@ -237,6 +206,5 @@ export { createBaseAppConfigResources, createAppConfigConfigurationProfile, createSSMSecureString, - createSecureStringProvider, putDynamoDBItem, }; diff --git a/packages/parameters/tests/helpers/ssmSecureStringCdk.ts b/packages/parameters/tests/helpers/ssmSecureStringCdk.ts deleted file mode 100644 index 2076c25c47..0000000000 --- a/packages/parameters/tests/helpers/ssmSecureStringCdk.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { - Context, - CloudFormationCustomResourceEvent -} from 'aws-lambda'; -import { - SSMClient, - PutParameterCommand, - DeleteParameterCommand -} from '@aws-sdk/client-ssm'; - -const client = new SSMClient({}); - -/** - * Create a new SSM SecureString parameter. - */ -const createResource = async (event: CloudFormationCustomResourceEvent): Promise => { - const { ResourceProperties } = event; - const { Name, Value } = ResourceProperties; - - await client.send(new PutParameterCommand({ - Name, - Value, - Type: 'SecureString', - })); -}; - -/** - * Delete an existing SSM parameter. - */ -const deleteResource = async (event: CloudFormationCustomResourceEvent): Promise => { - const { ResourceProperties } = event; - const { Name } = ResourceProperties; - - await client.send(new DeleteParameterCommand({ - Name, - })); -}; - -/** - * Custom resource handler for creating and deleting SSM SecureString parameters. This is used by - * CDK to create and delete the SSM SecureString parameters that are used to test the SSMProvider. - * - * We need a custom resource because CDK does not support creating SSM SecureString parameters. - */ -export const handler = async (event: CloudFormationCustomResourceEvent, _context: Context): Promise => { - if (event.RequestType === 'Create') { - await createResource(event); - } else if (event.RequestType === 'Delete') { - await deleteResource(event); - } else { - console.error('Unknown or unsupported request type', event); - throw new Error('Unknown or unsupported request type'); - } -}; \ No newline at end of file From bb50c04f5b2e6a144295b453577a7ea1a15ac011 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Wed, 22 Feb 2023 18:01:32 +0100 Subject: [PATCH 34/56] fix(parameters): handle base64/binaries in transformer (#1326) --- packages/parameters/src/BaseProvider.ts | 16 +-- ...pConfigProvider.class.test.functionCode.ts | 30 +++-- .../tests/e2e/appConfigProvider.class.test.ts | 103 ++++++------------ .../tests/unit/BaseProvider.test.ts | 10 +- .../tests/unit/getAppConfig.test.ts | 8 +- 5 files changed, 63 insertions(+), 104 deletions(-) diff --git a/packages/parameters/src/BaseProvider.ts b/packages/parameters/src/BaseProvider.ts index 3e4b2a1474..b2fc4efc1c 100644 --- a/packages/parameters/src/BaseProvider.ts +++ b/packages/parameters/src/BaseProvider.ts @@ -131,6 +131,11 @@ abstract class BaseProvider implements BaseProviderInterface { const transformValue = (value: string | Uint8Array | undefined, transform: TransformOptions, throwOnTransformError: boolean, key: string): string | Record | undefined => { try { const normalizedTransform = transform.toLowerCase(); + + if (value instanceof Uint8Array) { + value = new TextDecoder('utf-8').decode(value); + } + if ( (normalizedTransform === TRANSFORM_METHOD_JSON || (normalizedTransform === 'auto' && key.toLowerCase().endsWith(`.${TRANSFORM_METHOD_JSON}`))) && @@ -139,15 +144,12 @@ const transformValue = (value: string | Uint8Array | undefined, transform: Trans return JSON.parse(value) as Record; } else if ( (normalizedTransform === TRANSFORM_METHOD_BINARY || - (normalizedTransform === 'auto' && key.toLowerCase().endsWith(`.${TRANSFORM_METHOD_BINARY}`))) + (normalizedTransform === 'auto' && key.toLowerCase().endsWith(`.${TRANSFORM_METHOD_BINARY}`))) && + typeof value === 'string' ) { - if (typeof value === 'string') { - return new TextDecoder('utf-8').decode(fromBase64(value)); - } else { - return new TextDecoder('utf-8').decode(value); - } + return new TextDecoder('utf-8').decode(fromBase64(value)); } else { - return value as string; + return value; } } catch (error) { if (throwOnTransformError) diff --git a/packages/parameters/tests/e2e/appConfigProvider.class.test.functionCode.ts b/packages/parameters/tests/e2e/appConfigProvider.class.test.functionCode.ts index 589fd475ed..11d8feb01f 100644 --- a/packages/parameters/tests/e2e/appConfigProvider.class.test.functionCode.ts +++ b/packages/parameters/tests/e2e/appConfigProvider.class.test.functionCode.ts @@ -16,8 +16,7 @@ const application = process.env.APPLICATION_NAME || 'my-app'; const environment = process.env.ENVIRONMENT_NAME || 'my-env'; const freeFormJsonName = process.env.FREEFORM_JSON_NAME || 'freeform-json'; const freeFormYamlName = process.env.FREEFORM_YAML_NAME || 'freeform-yaml'; -const freeFormPlainTextNameA = process.env.FREEFORM_PLAIN_TEXT_NAME_A || 'freeform-plain-text'; -const freeFormPlainTextNameB = process.env.FREEFORM_PLAIN_TEXT_NAME_B || 'freeform-plain-text'; +const freeFormBase64encodedPlainText = process.env.FREEFORM_BASE64_ENCODED_PLAIN_TEXT_NAME || 'freeform-plain-text'; const featureFlagName = process.env.FEATURE_FLAG_NAME || 'feature-flag'; const defaultProvider = new AppConfigProvider({ @@ -65,28 +64,25 @@ const _call_get = async ( }; export const handler = async (_event: unknown, _context: Context): Promise => { - // Test 1 - get a single parameter as-is (no transformation) - await _call_get(freeFormPlainTextNameA, 'get'); + // Test 1 - get a single parameter as-is (no transformation - should return an Uint8Array) + await _call_get(freeFormYamlName, 'get'); - // Test 2 - get a free-form JSON and apply binary transformation (should return a stringified JSON) - await _call_get(freeFormJsonName, 'get-freeform-json-binary', { transform: 'binary' }); + // Test 2 - get a free-form JSON and apply json transformation (should return an object) + await _call_get(freeFormJsonName, 'get-freeform-json-binary', { transform: 'json' }); - // Test 3 - get a free-form YAML and apply binary transformation (should return a string-encoded YAML) - await _call_get(freeFormYamlName, 'get-freeform-yaml-binary', { transform: 'binary' }); + // Test 3 - get a free-form base64-encoded plain text and apply binary transformation (should return a decoded string) + await _call_get(freeFormBase64encodedPlainText, 'get-freeform-base64-plaintext-binary', { transform: 'binary' }); - // Test 4 - get a free-form plain text and apply binary transformation (should return a string) - await _call_get(freeFormPlainTextNameB, 'get-freeform-plain-text-binary', { transform: 'binary' }); - - // Test 5 - get a feature flag and apply binary transformation (should return a stringified JSON) - await _call_get(featureFlagName, 'get-feature-flag-binary', { transform: 'binary' }); + // Test 5 - get a feature flag and apply json transformation (should return an object) + await _call_get(featureFlagName, 'get-feature-flag-binary', { transform: 'json' }); // Test 6 // get parameter twice with middleware, which counts the number of requests, we check later if we only called AppConfig API once try { providerWithMiddleware.clearCache(); middleware.counter = 0; - await providerWithMiddleware.get(freeFormPlainTextNameA); - await providerWithMiddleware.get(freeFormPlainTextNameA); + await providerWithMiddleware.get(freeFormBase64encodedPlainText); + await providerWithMiddleware.get(freeFormBase64encodedPlainText); logger.log({ test: 'get-cached', value: middleware.counter // should be 1 @@ -103,8 +99,8 @@ export const handler = async (_event: unknown, _context: Context): Promise try { providerWithMiddleware.clearCache(); middleware.counter = 0; - await providerWithMiddleware.get(freeFormPlainTextNameA); - await providerWithMiddleware.get(freeFormPlainTextNameA, { forceFetch: true }); + await providerWithMiddleware.get(freeFormBase64encodedPlainText); + await providerWithMiddleware.get(freeFormBase64encodedPlainText, { forceFetch: true }); logger.log({ test: 'get-forced', value: middleware.counter // should be 2 diff --git a/packages/parameters/tests/e2e/appConfigProvider.class.test.ts b/packages/parameters/tests/e2e/appConfigProvider.class.test.ts index b32504444a..02b2de3291 100644 --- a/packages/parameters/tests/e2e/appConfigProvider.class.test.ts +++ b/packages/parameters/tests/e2e/appConfigProvider.class.test.ts @@ -5,6 +5,7 @@ */ import path from 'path'; import { App, Stack, Aspects } from 'aws-cdk-lib'; +import { toBase64 } from '@aws-sdk/util-base64-node'; import { v4 } from 'uuid'; import { generateUniqueName, @@ -44,8 +45,7 @@ const environmentName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, const deploymentStrategyName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'immediate'); const freeFormJsonName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'freeFormJson'); const freeFormYamlName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'freeFormYaml'); -const freeFormPlainTextNameA = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'freeFormPlainTextA'); -const freeFormPlainTextNameB = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'freeFormPlainTextB'); +const freeFormBase64PlainTextName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'freeFormBase64PlainText'); const featureFlagName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'featureFlag'); const freeFormJsonValue = { @@ -85,7 +85,7 @@ let stack: Stack; * The parameters created are: * - Free-form JSON * - Free-form YAML - * - 2x Free-form plain text + * - Free-form plain text base64-encoded string * - Feature flag * * These parameters allow to retrieve the values and test some transformations. @@ -93,25 +93,22 @@ let stack: Stack; * The tests are: * * Test 1 - * get a single parameter as-is (no transformation) + * get a single parameter as-is (no transformation - should return an Uint8Array) * * Test 2 - * get a free-form JSON and apply binary transformation (should return a stringified JSON) + * get a free-form JSON and apply json transformation (should return an object) * * Test 3 - * get a free-form YAML and apply binary transformation (should return a string-encoded YAML) + * get a free-form base64-encoded plain text and apply binary transformation (should return a decoded string) * * Test 4 - * get a free-form plain text and apply binary transformation (should return a string) + * get a feature flag and apply json transformation (should return an object) * * Test 5 - * get a feature flag and apply binary transformation (should return a stringified JSON) - * - * Test 6 * get parameter twice with middleware, which counts the number of requests, * we check later if we only called AppConfig API once * - * Test 7 + * Test 6 * get parameter twice, but force fetch 2nd time, we count number of SDK requests and * check that we made two API calls * @@ -140,8 +137,7 @@ describe(`parameters E2E tests (appConfigProvider) for runtime ${runtime}`, () = ENVIRONMENT_NAME: environmentName, FREEFORM_JSON_NAME: freeFormJsonName, FREEFORM_YAML_NAME: freeFormYamlName, - FREEFORM_PLAIN_TEXT_NAME_A: freeFormPlainTextNameA, - FREEFORM_PLAIN_TEXT_NAME_B: freeFormPlainTextNameB, + FREEFORM_BASE64_ENCODED_PLAIN_TEXT_NAME: freeFormBase64PlainTextName, FEATURE_FLAG_NAME: featureFlagName, }, runtime, @@ -187,33 +183,19 @@ describe(`parameters E2E tests (appConfigProvider) for runtime ${runtime}`, () = }); freeFormYaml.node.addDependency(freeFormJson); - const freeFormPlainTextA = createAppConfigConfigurationProfile({ + const freeFormBase64PlainText = createAppConfigConfigurationProfile({ stack, application, environment, deploymentStrategy, - name: freeFormPlainTextNameA, + name: freeFormBase64PlainTextName, type: 'AWS.Freeform', content: { - content: freeFormPlainTextValue, + content: toBase64(new TextEncoder().encode(freeFormPlainTextValue)), contentType: 'text/plain', } }); - freeFormPlainTextA.node.addDependency(freeFormYaml); - - const freeFormPlainTextB = createAppConfigConfigurationProfile({ - stack, - application, - environment, - deploymentStrategy, - name: freeFormPlainTextNameB, - type: 'AWS.Freeform', - content: { - content: freeFormPlainTextValue, - contentType: 'text/plain', - } - }); - freeFormPlainTextB.node.addDependency(freeFormPlainTextA); + freeFormBase64PlainText.node.addDependency(freeFormYaml); const featureFlag = createAppConfigConfigurationProfile({ stack, @@ -227,14 +209,13 @@ describe(`parameters E2E tests (appConfigProvider) for runtime ${runtime}`, () = contentType: 'application/json', } }); - featureFlag.node.addDependency(freeFormPlainTextB); + featureFlag.node.addDependency(freeFormBase64PlainText); // Grant access to the Lambda function to the AppConfig resources. Aspects.of(stack).add(new ResourceAccessGranter([ freeFormJson, freeFormYaml, - freeFormPlainTextA, - freeFormPlainTextB, + freeFormBase64PlainText, featureFlag, ])); @@ -248,8 +229,8 @@ describe(`parameters E2E tests (appConfigProvider) for runtime ${runtime}`, () = describe('AppConfigProvider usage', () => { - // Test 1 - get a single parameter as-is (no transformation) - it('should retrieve single parameter', () => { + // Test 1 - get a single parameter as-is (no transformation - should return an Uint8Array) + it('should retrieve single parameter as-is', () => { const logs = invocationLogs[0].getFunctionLogs(); const testLog = InvocationLogs.parseFunctionLog(logs[0]); @@ -258,75 +239,59 @@ describe(`parameters E2E tests (appConfigProvider) for runtime ${runtime}`, () = test: 'get', value: JSON.parse( JSON.stringify( - encoder.encode(freeFormPlainTextValue) + encoder.encode(freeFormYamlValue) ) ), }); }); - // Test 2 - get a free-form JSON and apply binary transformation - // (should return a stringified JSON) - it('should retrieve single free-form JSON parameter with binary transformation', () => { + // Test 2 - get a free-form JSON and apply json transformation (should return an object) + it('should retrieve a free-form JSON parameter with JSON transformation', () => { const logs = invocationLogs[0].getFunctionLogs(); const testLog = InvocationLogs.parseFunctionLog(logs[1]); expect(testLog).toStrictEqual({ test: 'get-freeform-json-binary', - value: JSON.stringify(freeFormJsonValue), + value: freeFormJsonValue, }); }); - // Test 3 - get a free-form YAML and apply binary transformation - // (should return a string-encoded YAML) - it('should retrieve single free-form YAML parameter with binary transformation', () => { + // Test 3 - get a free-form base64-encoded plain text and apply binary transformation + // (should return a decoded string) + it('should retrieve a base64-encoded plain text parameter with binary transformation', () => { const logs = invocationLogs[0].getFunctionLogs(); const testLog = InvocationLogs.parseFunctionLog(logs[2]); expect(testLog).toStrictEqual({ - test: 'get-freeform-yaml-binary', - value: freeFormYamlValue, - }); - - }); - - // Test 4 - get a free-form plain text and apply binary transformation - // (should return a string) - it('should retrieve single free-form plain text parameter with binary transformation', () => { - - const logs = invocationLogs[0].getFunctionLogs(); - const testLog = InvocationLogs.parseFunctionLog(logs[3]); - - expect(testLog).toStrictEqual({ - test: 'get-freeform-plain-text-binary', + test: 'get-freeform-base64-plaintext-binary', value: freeFormPlainTextValue, }); }); - // Test 5 - get a feature flag and apply binary transformation - // (should return a stringified JSON) - it('should retrieve single feature flag parameter with binary transformation', () => { + // Test 4 - get a feature flag and apply json transformation (should return an object) + it('should retrieve a feature flag parameter with JSON transformation', () => { const logs = invocationLogs[0].getFunctionLogs(); - const testLog = InvocationLogs.parseFunctionLog(logs[4]); + const testLog = InvocationLogs.parseFunctionLog(logs[3]); expect(testLog).toStrictEqual({ test: 'get-feature-flag-binary', - value: JSON.stringify(featureFlagValue.values), + value: featureFlagValue.values, }); }); - - // Test 6 - get parameter twice with middleware, which counts the number + + // Test 5 - get parameter twice with middleware, which counts the number // of requests, we check later if we only called AppConfig API once it('should retrieve single parameter cached', () => { const logs = invocationLogs[0].getFunctionLogs(); - const testLog = InvocationLogs.parseFunctionLog(logs[5]); + const testLog = InvocationLogs.parseFunctionLog(logs[4]); expect(testLog).toStrictEqual({ test: 'get-cached', @@ -335,12 +300,12 @@ describe(`parameters E2E tests (appConfigProvider) for runtime ${runtime}`, () = }, TEST_CASE_TIMEOUT); - // Test 7 - get parameter twice, but force fetch 2nd time, + // Test 6 - get parameter twice, but force fetch 2nd time, // we count number of SDK requests and check that we made two API calls it('should retrieve single parameter twice without caching', async () => { const logs = invocationLogs[0].getFunctionLogs(); - const testLog = InvocationLogs.parseFunctionLog(logs[6]); + const testLog = InvocationLogs.parseFunctionLog(logs[5]); expect(testLog).toStrictEqual({ test: 'get-forced', diff --git a/packages/parameters/tests/unit/BaseProvider.test.ts b/packages/parameters/tests/unit/BaseProvider.test.ts index 3b8f029d64..186e0c1cdc 100644 --- a/packages/parameters/tests/unit/BaseProvider.test.ts +++ b/packages/parameters/tests/unit/BaseProvider.test.ts @@ -206,19 +206,15 @@ describe('Class: BaseProvider', () => { }); - test('when called with a binary transform, and the value is a valid binary, it returns the decoded value', async () => { + test('when called with a binary transform, and the value is a valid binary but NOT base64 encoded, it throws', async () => { // Prepare const mockData = encoder.encode('my-value'); const provider = new TestProvider(); jest.spyOn(provider, '_get').mockImplementation(() => new Promise((resolve, _reject) => resolve(mockData as unknown as string))); - // Act - const value = await provider.get('my-parameter', { transform: 'binary' }); - - // Assess - expect(typeof value).toBe('string'); - expect(value).toEqual('my-value'); + // Act & Assess + await expect(provider.get('my-parameter', { transform: 'binary' })).rejects.toThrowError(TransformParameterError); }); diff --git a/packages/parameters/tests/unit/getAppConfig.test.ts b/packages/parameters/tests/unit/getAppConfig.test.ts index af03a8ec32..e729d23dfd 100644 --- a/packages/parameters/tests/unit/getAppConfig.test.ts +++ b/packages/parameters/tests/unit/getAppConfig.test.ts @@ -16,11 +16,11 @@ import { import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; import type { GetAppConfigCombinedInterface } from '../../src/types/AppConfigProvider'; +import { toBase64 } from '@aws-sdk/util-base64-node'; describe('Function: getAppConfig', () => { const client = mockClient(AppConfigDataClient); const encoder = new TextEncoder(); - const decoder = new TextDecoder(); beforeEach(() => { jest.clearAllMocks(); @@ -103,8 +103,8 @@ describe('Function: getAppConfig', () => { 'AYADeNgfsRxdKiJ37A12OZ9vN2cAXwABABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREF1RzlLMTg1Tkx2Wjk4OGV2UXkyQ1'; const mockNextToken = 'ImRmyljpZnxt7FfxeEOE5H8xQF1SfOlWZFnHujbzJmIvNeSAAA8/qA9ivK0ElRMwpvx96damGxt125XtMkmYf6a0OWSqnBw=='; - const mockData = encoder.encode('myAppConfiguration'); - const decodedData = decoder.decode(mockData); + const expectedValue = 'my-value'; + const mockData = encoder.encode(toBase64(encoder.encode(expectedValue))); client .on(StartConfigurationSessionCommand) @@ -121,6 +121,6 @@ describe('Function: getAppConfig', () => { const result = await getAppConfig(name, options); // Assess - expect(result).toBe(decodedData); + expect(result).toBe(expectedValue); }); }); From 618613b9a69166553dd9ef8d5b92f89e1cdf79d0 Mon Sep 17 00:00:00 2001 From: Alexander Schueren Date: Thu, 23 Feb 2023 15:03:08 +0100 Subject: [PATCH 35/56] feat(layers): add new regions (#1322) * add new regions * fix husky pre push command * add new layer regions and versions * small fix * add documentation to readme how to add new region --- .../workflows/reusable_deploy_layer_stack.yml | 7 ++- .husky/pre-push | 10 +-- docs/index.md | 62 +++++++++++-------- layers/README.md | 22 ++++++- 4 files changed, 66 insertions(+), 35 deletions(-) diff --git a/.github/workflows/reusable_deploy_layer_stack.yml b/.github/workflows/reusable_deploy_layer_stack.yml index 0fc9a542fc..9d56cd9678 100644 --- a/.github/workflows/reusable_deploy_layer_stack.yml +++ b/.github/workflows/reusable_deploy_layer_stack.yml @@ -27,6 +27,7 @@ jobs: [ "af-south-1", "eu-central-1", + "eu-central-2", "us-east-1", "us-east-2", "us-west-1", @@ -35,17 +36,19 @@ jobs: "ap-south-1", "ap-northeast-1", "ap-northeast-2", + "ap-northeast-3", "ap-southeast-1", "ap-southeast-2", + "ap-southeast-3", + "ap-southeast-4", "ca-central-1", "eu-west-1", "eu-west-2", "eu-west-3", "eu-south-1", + "eu-south-2" "eu-north-1", "sa-east-1", - "ap-southeast-3", - "ap-northeast-3", "me-south-1", ] steps: diff --git a/.husky/pre-push b/.husky/pre-push index d975532720..3b6d564252 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -2,9 +2,9 @@ . "$(dirname "$0")/_/husky.sh" npm t \ - -w packages/commons && \ - -w packages/logger && \ - -w packages/metrics && \ - -w packages/tracer && \ - -w packages/idempotency && \ + -w packages/commons \ + -w packages/logger \ + -w packages/metrics \ + -w packages/tracer \ + -w packages/idempotency \ -w packages/parameters \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index a6ea959862..63e7698abb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,7 +26,7 @@ You can use Powertools in both TypeScript and JavaScript code bases. Powertools is available in the following formats: -* **Lambda Layer**: [**arn:aws:lambda:{region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7**](#){: .copyMe}:clipboard: +* **Lambda Layer**: [**arn:aws:lambda:{region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:8**](#){: .copyMe}:clipboard: * **npm**: **`npm install @aws-lambda-powertools/tracer @aws-lambda-powertools/metrics @aws-lambda-powertools/logger`** ### Lambda Layer @@ -37,25 +37,33 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: ??? note "Note: Click to expand and copy any regional Lambda Layer ARN" - | Region | Layer ARN | + | Region | Layer ARN | | ---------------- | ----------------------------------------------------------------------------------------------------------- | - | `us-east-1` | [arn:aws:lambda:us-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `us-east-2` | [arn:aws:lambda:us-east-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `us-west-1` | [arn:aws:lambda:us-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `us-west-2` | [arn:aws:lambda:us-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `ap-south-1` | [arn:aws:lambda:ap-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `eu-central-1` | [arn:aws:lambda:eu-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `eu-west-1` | [arn:aws:lambda:eu-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `eu-west-2` | [arn:aws:lambda:eu-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `eu-west-3` | [arn:aws:lambda:eu-west-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `eu-north-1` | [arn:aws:lambda:eu-north-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `ca-central-1` | [arn:aws:lambda:ca-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | - | `sa-east-1` | [arn:aws:lambda:sa-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:7](#){: .copyMe}:clipboard: | + | `us-east-1` | [arn:aws:lambda:us-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `us-east-2` | [arn:aws:lambda:us-east-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `us-west-1` | [arn:aws:lambda:us-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `us-west-2` | [arn:aws:lambda:us-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `ap-south-1` | [arn:aws:lambda:ap-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `ap-east-1` | [arn:aws:lambda:ap-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `ap-southeast-4` | [arn:aws:lambda:ap-southeast-4:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `eu-central-1` | [arn:aws:lambda:eu-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `eu-central-2` | [arn:aws:lambda:eu-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `eu-west-1` | [arn:aws:lambda:eu-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `eu-west-2` | [arn:aws:lambda:eu-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `eu-west-3` | [arn:aws:lambda:eu-west-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `eu-north-1` | [arn:aws:lambda:eu-north-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `eu-south-1` | [arn:aws:lambda:eu-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `eu-south-2` | [arn:aws:lambda:eu-south-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `ca-central-1` | [arn:aws:lambda:ca-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `sa-east-1` | [arn:aws:lambda:sa-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `af-south-1` | [arn:aws:lambda:af-south-11:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `me-south-1` | [arn:aws:lambda:me-south-11:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | ??? note "Note: Click to expand and copy code snippets for popular frameworks" @@ -66,7 +74,7 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: Type: AWS::Serverless::Function Properties: Layers: - - !Sub arn:aws:lambda:${AWS::Region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7 + - !Sub arn:aws:lambda:${AWS::Region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:8 ``` If you use `esbuild` to bundle your code, make sure to exclude `@aws-lambda-powertools` from being bundled since the packages will be already present the Layer: @@ -97,7 +105,7 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: hello: handler: lambda_function.lambda_handler layers: - - arn:aws:lambda:${aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7 + - arn:aws:lambda:${aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:8 ``` If you use `esbuild` to bundle your code, make sure to exclude `@aws-lambda-powertools` from being bundled since the packages will be already present the Layer: @@ -129,7 +137,7 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: const powertoolsLayer = lambda.LayerVersion.fromLayerVersionArn( this, 'PowertoolsLayer', - `arn:aws:lambda:${cdk.Stack.of(this).region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7` + `arn:aws:lambda:${cdk.Stack.of(this).region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:8` ); new lambda.Function(this, 'Function', { @@ -181,7 +189,7 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: role = ... handler = "index.handler" runtime = "nodejs16.x" - layers = ["arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7"] + layers = ["arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:8"] source_code_hash = filebase64sha256("lambda_function_payload.zip") } ``` @@ -199,7 +207,7 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: const lambdaFunction = new aws.lambda.Function("function", { layers: [ - pulumi.interpolate`arn:aws:lambda:${aws.getRegionOutput().name}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7` + pulumi.interpolate`arn:aws:lambda:${aws.getRegionOutput().name}:094274105915:layer:AWSLambdaPowertoolsTypeScript:8` ], code: new pulumi.asset.FileArchive("lambda_function_payload.zip"), tracingConfig: { @@ -223,7 +231,7 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: ? Do you want to configure advanced settings? Yes ... ? Do you want to enable Lambda layers for this function? Yes - ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7 + ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:8 ❯ amplify push -y # Updating an existing function and add the layer @@ -233,13 +241,13 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: - Name: ? Which setting do you want to update? Lambda layers configuration ? Do you want to enable Lambda layers for this function? Yes - ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7 + ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:8 ? Do you want to edit the local lambda function now? No ``` === "Get the Layer .zip contents" ```bash title="AWS CLI" - aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:7 --region {region} + aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:8 --region {region} ``` The pre-signed URL to download this Lambda Layer will be within `Location` key. diff --git a/layers/README.md b/layers/README.md index 5a49b01293..efc0cf3419 100644 --- a/layers/README.md +++ b/layers/README.md @@ -35,4 +35,24 @@ PS: You can force * the Powertools version with VERSION env variable ```sh RUNTIME=node12.x VERSION=0.9.0 npm run test:e2e -``` \ No newline at end of file +``` + + +# How to add new region + +* activate new region in your TEST and PROD accounts +* bootstrap a CDKToolkit stack in the new region +```shell + cdk bootstrap aws://AWS_ACCOUNT/NEW_REGION +``` +* build the layer folder from the project root directory +```shell +bash ./.github/scripts/setup_tmp_layer_files.sh +``` +* deploy the first layer version to the new region, make sure to set the NEW_REGION in your AWS CLI configuration correctly, otherwise you will deploy to the wrong region +```shell +npm run cdk -w layers -- deploy --app cdk.out --context region=NEW_REGION 'LayerPublisherStack' --require-approval never --verbose +``` +* Run the bumper script to bring all layers to the same version across all regions +* Add new region to the worklflow in `./github/workflows/reusable_deploy_layer_stack.yml` +* Document new region in `docs/index.md` \ No newline at end of file From ade5923194bb99594695af885de97c269dd11e2f Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 23 Feb 2023 15:27:52 +0100 Subject: [PATCH 36/56] chore(layers): fix workflow + linting/housekeeping (#1327) * chore: added missing comma * chore: linting mkdown --- .github/workflows/reusable_deploy_layer_stack.yml | 2 +- docs/index.md | 12 ++++++------ layers/README.md | 9 ++++----- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/reusable_deploy_layer_stack.yml b/.github/workflows/reusable_deploy_layer_stack.yml index 9d56cd9678..082d6962cc 100644 --- a/.github/workflows/reusable_deploy_layer_stack.yml +++ b/.github/workflows/reusable_deploy_layer_stack.yml @@ -46,7 +46,7 @@ jobs: "eu-west-2", "eu-west-3", "eu-south-1", - "eu-south-2" + "eu-south-2", "eu-north-1", "sa-east-1", "me-south-1", diff --git a/docs/index.md b/docs/index.md index 63e7698abb..2f9eb56ff0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -37,14 +37,14 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: ??? note "Note: Click to expand and copy any regional Lambda Layer ARN" - | Region | Layer ARN | + | Region | Layer ARN | | ---------------- | ----------------------------------------------------------------------------------------------------------- | | `us-east-1` | [arn:aws:lambda:us-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | | `us-east-2` | [arn:aws:lambda:us-east-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | | `us-west-1` | [arn:aws:lambda:us-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | | `us-west-2` | [arn:aws:lambda:us-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | | `ap-south-1` | [arn:aws:lambda:ap-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | - | `ap-east-1` | [arn:aws:lambda:ap-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `ap-east-1` | [arn:aws:lambda:ap-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | @@ -58,12 +58,12 @@ You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https: | `eu-west-2` | [arn:aws:lambda:eu-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | | `eu-west-3` | [arn:aws:lambda:eu-west-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | | `eu-north-1` | [arn:aws:lambda:eu-north-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | - | `eu-south-1` | [arn:aws:lambda:eu-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | - | `eu-south-2` | [arn:aws:lambda:eu-south-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `eu-south-1` | [arn:aws:lambda:eu-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `eu-south-2` | [arn:aws:lambda:eu-south-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | | `ca-central-1` | [arn:aws:lambda:ca-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | | `sa-east-1` | [arn:aws:lambda:sa-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | - | `af-south-1` | [arn:aws:lambda:af-south-11:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | - | `me-south-1` | [arn:aws:lambda:me-south-11:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `af-south-1` | [arn:aws:lambda:af-south-11:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | + | `me-south-1` | [arn:aws:lambda:me-south-11:094274105915:layer:AWSLambdaPowertoolsTypeScript:8](#){: .copyMe}:clipboard: | ??? note "Note: Click to expand and copy code snippets for popular frameworks" diff --git a/layers/README.md b/layers/README.md index efc0cf3419..1a3f5602b9 100644 --- a/layers/README.md +++ b/layers/README.md @@ -37,19 +37,18 @@ PS: You can force RUNTIME=node12.x VERSION=0.9.0 npm run test:e2e ``` - # How to add new region -* activate new region in your TEST and PROD accounts -* bootstrap a CDKToolkit stack in the new region +* Activate new region in your TEST and PROD accounts +* Bootstrap a CDKToolkit stack in the new region ```shell cdk bootstrap aws://AWS_ACCOUNT/NEW_REGION ``` -* build the layer folder from the project root directory +* Build the layer folder from the project root directory ```shell bash ./.github/scripts/setup_tmp_layer_files.sh ``` -* deploy the first layer version to the new region, make sure to set the NEW_REGION in your AWS CLI configuration correctly, otherwise you will deploy to the wrong region +* Deploy the first layer version to the new region, make sure to set the NEW_REGION in your AWS CLI configuration correctly, otherwise you will deploy to the wrong region ```shell npm run cdk -w layers -- deploy --app cdk.out --context region=NEW_REGION 'LayerPublisherStack' --require-approval never --verbose ``` From af55f0131b5c452d709efe7fb47ff43f22fad0a1 Mon Sep 17 00:00:00 2001 From: Alexander Schueren Date: Sat, 25 Feb 2023 18:18:15 +0100 Subject: [PATCH 37/56] chore(ci): automatically update docs after layer publish during release (#1324) --- .github/scripts/update_layer_arn.sh | 72 ++++++++++++++ .github/workflows/make-release.yml | 20 ++++ .github/workflows/on-merge-to-main.yml | 9 -- .github/workflows/on_doc_merge.yml | 24 +++++ .github/workflows/publish-docs-on-release.yml | 23 ----- .github/workflows/publish_layer.yaml | 48 ++++++++-- .github/workflows/reusable-publish-docs.yml | 96 ++++++++----------- .../workflows/reusable_deploy_layer_stack.yml | 29 +++++- .../reusable_update_layer_arn_docs.yml | 52 ++++++++++ 9 files changed, 279 insertions(+), 94 deletions(-) create mode 100755 .github/scripts/update_layer_arn.sh create mode 100644 .github/workflows/on_doc_merge.yml delete mode 100644 .github/workflows/publish-docs-on-release.yml create mode 100644 .github/workflows/reusable_update_layer_arn_docs.yml diff --git a/.github/scripts/update_layer_arn.sh b/.github/scripts/update_layer_arn.sh new file mode 100755 index 0000000000..51a7149880 --- /dev/null +++ b/.github/scripts/update_layer_arn.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# This script is run during the reusable_update_v2_layer_arn_docs CI job, +# and it is responsible for replacing the layer ARN in our documentation, +# based on the output files generated by CDK when deploying to each pseudo_region. +# +# see .github/workflows/reusable_deploy_v2_layer_stack.yml + +set -eo pipefail + +if [[ $# -ne 1 ]]; then + cat < line + # sed doesn't support \d+ in a portable way, so we cheat with (:digit: :digit: *) + sed -i -e "s/$prefix:[[:digit:]][[:digit:]]*/$line/g" docs/index.md + + # We use the eu-central-1 layer as the version for all the frameworks (SAM, CDK, SLS, etc) + # We could have used any other region. What's important is the version at the end. + + # Examples of strings found in the documentation with pseudo regions: + # arn:aws:lambda:{region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:39 + # arn:aws:lambda:${AWS::Region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:39 + # arn:aws:lambda:${aws:region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:39 + # arn:aws:lambda:{env.region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:39 + if [[ "$line" == *"eu-central-1"* ]]; then + # These are all the framework pseudo parameters currently found in the docs + for pseudo_region in '{region}' '${AWS::Region}' '${aws::region}' '{aws::region}' '{env.region}' '${cdk.Stack.of(this).region}' '${aws.getRegionOutput().name}'; do + prefix_pseudo_region=$(echo "$prefix" | sed "s/eu-central-1/${pseudo_region}/") + # prefix_pseudo_region = arn:aws:lambda:${AWS::Region}:094274105915:layer:AWSLambdaPowertoolsTypeScript + + line_pseudo_region=$(echo "$line" | sed "s/eu-central-1/${pseudo_region}/") + # line_pseudo_region = arn:aws:lambda:${AWS::Region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:49 + + # Replace all the "prefix_pseudo_region"'s in the file + # prefix_pseudo_region:\d+ ==> line_pseudo_region + sed -i -e "s/$prefix_pseudo_region:[[:digit:]][[:digit:]]*/$line_pseudo_region/g" docs/index.md + done + fi + done +done diff --git a/.github/workflows/make-release.yml b/.github/workflows/make-release.yml index 838081df3c..812c0e93c4 100644 --- a/.github/workflows/make-release.yml +++ b/.github/workflows/make-release.yml @@ -9,6 +9,8 @@ jobs: publish-npm: needs: run-unit-tests runs-on: ubuntu-latest + outputs: + RELEASE_VERSION: ${{ steps.set-release-version.outputs.RELEASE_VERSION }} steps: - name: Checkout code uses: actions/checkout@v3 @@ -47,3 +49,21 @@ jobs: git remote set-url origin https://x-access-token:${GH_TOKEN}@github.com/$GITHUB_REPOSITORY npx lerna version --conventional-commits --force-publish --yes npx lerna publish from-git --no-verify-access --yes + - name: Set release version + id: set-release-version + run: | + RELEASE=$(npm view @aws-lambda-powertools/tracer version) + echo RELEASE_VERSION="$RELEASE_VERSION" >> "$GITHUB_OUTPUT" + + # NOTE: Watch out for the depth limit of 4 nested workflow_calls. + # publish_layer -> reusable_deploy_layer_stack -> reusable_update_layer_arn_docs + publish_layer: + needs: publish-npm + secrets: inherit + permissions: + id-token: write + contents: write + pages: write + uses: ./.github/workflows/publish_layer.yml + with: + latest_published_version: ${{ needs.publish-npm.outputs.RELEASE_VERSION }} diff --git a/.github/workflows/on-merge-to-main.yml b/.github/workflows/on-merge-to-main.yml index 411cd14ee8..8ebd13984a 100644 --- a/.github/workflows/on-merge-to-main.yml +++ b/.github/workflows/on-merge-to-main.yml @@ -21,15 +21,6 @@ jobs: needs: get_pr_details if: ${{ needs.get_pr_details.outputs.prIsMerged == 'true' }} uses: ./.github/workflows/reusable-run-linting-check-and-unit-tests.yml - publish: - needs: - [get_pr_details, run-unit-tests] - uses: ./.github/workflows/reusable-publish-docs.yml - with: - workflow_origin: ${{ github.event.repository.full_name }} - prIsMerged: ${{ needs.get_pr_details.outputs.prIsMerged }} - secrets: - token: ${{ secrets.GITHUB_TOKEN }} update-release-draft: needs: publish runs-on: ubuntu-latest diff --git a/.github/workflows/on_doc_merge.yml b/.github/workflows/on_doc_merge.yml new file mode 100644 index 0000000000..d412082ee7 --- /dev/null +++ b/.github/workflows/on_doc_merge.yml @@ -0,0 +1,24 @@ +name: Docs + +on: + push: + branches: + - main + paths: + - "docs/**" + - "mkdocs.yml" + - "examples/**" + +jobs: + release-docs: + needs: changelog + permissions: + contents: write + pages: write + uses: ./.github/workflows/reusable-publish-docs.yml + with: + workflow_origin: ${{ github.event.repository.full_name }} + version: dev + alias: stage + secrets: + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/publish-docs-on-release.yml b/.github/workflows/publish-docs-on-release.yml deleted file mode 100644 index 7d5a4fdcc3..0000000000 --- a/.github/workflows/publish-docs-on-release.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Publish docs on release - -on: - # Triggered manually - workflow_dispatch: - inputs: - versionNumber: - required: true - type: string - description: "If running this manually please insert a version number that corresponds to the latest published in the GitHub releases (i.e. v1.1.1)" - # Or triggered as result of a release - release: - types: [released] - -jobs: - publish-docs: - uses: ./.github/workflows/reusable-publish-docs.yml - with: - workflow_origin: ${{ github.event.repository.full_name }} - isRelease: "true" - versionNumber: ${{ inputs.versionNumber }} - secrets: - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish_layer.yaml b/.github/workflows/publish_layer.yaml index 0131612aa6..0f3968dba8 100644 --- a/.github/workflows/publish_layer.yaml +++ b/.github/workflows/publish_layer.yaml @@ -12,11 +12,18 @@ on: description: "Latest npm published version to rebuild corresponding layer for, e.g. v1.0.2" default: "v1.0.2" required: true - # Automatic trigger after release - workflow_run: - workflows: ["Make Release"] - types: - - completed + + workflow_call: + inputs: + latest_published_version: + type: string + description: "Latest npm published version to rebuild latest docs for, e.g. 2.0.0, 2.0.0a1 (pre-release)" + required: true + pre_release: + description: "Publishes documentation using a pre-release tag (2.0.0a1)." + default: false + type: boolean + required: false jobs: # Build layer by running cdk synth in layer-publisher directory and uploading cdk.out for deployment @@ -73,6 +80,7 @@ jobs: with: stage: "BETA" artifact-name: "cdk-layer-artifact" + latest_published_version: ${{ inputs.latest_published_version }} secrets: target-account-role: ${{ secrets.AWS_LAYERS_BETA_ROLE_ARN }} @@ -84,5 +92,33 @@ jobs: with: stage: "PROD" artifact-name: "cdk-layer-artifact" + latest_published_version: ${{ inputs.latest_published_version }} secrets: - target-account-role: ${{ secrets.AWS_LAYERS_PROD_ROLE_ARN }} \ No newline at end of file + target-account-role: ${{ secrets.AWS_LAYERS_PROD_ROLE_ARN }} + + prepare_docs_alias: + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + DOCS_ALIAS: ${{ steps.set-alias.outputs.DOCS_ALIAS }} + steps: + - name: Set docs alias + id: set-alias + run: | + DOCS_ALIAS=latest + if [[ "${{ inputs.pre_release }}" == true ]] ; then + DOCS_ALIAS=alpha + fi + echo DOCS_ALIAS="$DOCS_ALIAS" >> "$GITHUB_OUTPUT" + + release-docs: + needs: [ prod, prepare_docs_alias ] + permissions: + contents: write + pages: write + uses: ./.github/workflows/reusable_publish_docs.yml + with: + version: ${{ inputs.latest_published_version }} + alias: ${{ needs.prepare_docs_alias.outputs.DOCS_ALIAS }} + detached_mode: true diff --git a/.github/workflows/reusable-publish-docs.yml b/.github/workflows/reusable-publish-docs.yml index b2d3aed5da..6c1990a0bb 100644 --- a/.github/workflows/reusable-publish-docs.yml +++ b/.github/workflows/reusable-publish-docs.yml @@ -1,22 +1,28 @@ name: Reusable Publish docs +env: + BRANCH: main + ORIGIN: awslabs/aws-lambda-powertools-typescript + + on: workflow_call: inputs: - workflow_origin: # see https://github.com/awslabs/aws-lambda-powertools-python/issues/1349 + version: + description: "Version to build and publish docs (1.28.0, develop)" required: true type: string - prIsMerged: - required: false - default: "false" - type: string - isRelease: - required: false - default: "false" + alias: + description: "Alias to associate version (latest, stage)" + required: true type: string - versionNumber: + detached_mode: + description: "Whether it's running in git detached mode to ensure git is sync'd" required: false - default: "" + default: false + type: boolean + workflow_origin: # see https://github.com/awslabs/aws-lambda-powertools-python/issues/1349 + required: true type: string secrets: token: @@ -65,32 +71,6 @@ jobs: uses: actions/setup-python@v4 with: python-version: "3.8" - # We run this step only when the workflow has been triggered by a release - # in this case we publish the docs to `/latest` - - name: (Conditional) Set RELEASE_VERSION env var to `latest` - if: ${{ inputs.isRelease == 'true' }} - run: | - RELEASE_VERSION=$(echo ${{ github.ref_name }} | sed 's/v//') - EXPLICIT_RELEASE_VERSION=$(echo ${{ inputs.versionNumber }} | sed 's/v//') - if [ $EXPLICIT_RELEASE_VERSION != "" ]; then - echo "RELEASE_VERSION=${EXPLICIT_RELEASE_VERSION}" - echo "RELEASE_VERSION=${EXPLICIT_RELEASE_VERSION}" >> $GITHUB_ENV - else - echo "RELEASE_VERSION=${RELEASE_VERSION}" - echo "RELEASE_VERSION=${RELEASE_VERSION}" >> $GITHUB_ENV - fi - # We run this step only when the workflow has been triggered by a PR merge - # in this case we publish the docs to `/dev` - - name: (Conditional) Set RELEASE_VERSION env var to `dev` - if: ${{ inputs.prIsMerged == 'true' }} - run: | - echo "RELEASE_VERSION=dev" >> $GITHUB_ENV - - name: Check RELEASE_VERSION env var - if: ${{ env.RELEASE_VERSION == '' }} - uses: actions/github-script@v3 - with: - script: | - core.setFailed('RELEASE_VERSION env var is empty.') - name: Install doc generation dependencies run: | pip install --upgrade pip @@ -98,35 +78,41 @@ jobs: - name: Setup doc deploy run: | git config --global user.name Docs deploy - git config --global user.email docs@dummy.bot.com - - name: Publish docs to latest if isRelease - if: ${{ env.RELEASE_VERSION != 'dev' }} + git config --global user.email aws-devax-open-source@amazon.com + - name: Git refresh tip (detached mode) + # Git Detached mode (release notes) doesn't have origin + if: ${{ inputs.detached_mode }} + run: | + git config pull.rebase true + git config remote.origin.url >&- || git remote add origin https://github.com/"$ORIGIN" + git pull origin "$BRANCH" + - name: Build docs website and API reference + run: | + make release-docs VERSION="$VERSION" ALIAS="$ALIAS" + poetry run mike set-default --push latest + - name: Build docs website and API reference + env: + VERSION: ${{ inputs.version }} + ALIAS: ${{ inputs.alias }} run: | rm -rf site mkdocs build - mike deploy --push --update-aliases --no-redirect "${{ env.RELEASE_VERSION }}" "latest" + mike deploy --push --update-aliases --no-redirect ${{ env.VERSION }} ${{ env.ALIAS }}" # Set latest version as a default mike set-default --push latest - - name: Publish docs to dev - if: ${{ env.RELEASE_VERSION == 'dev' }} - run: | - rm -rf site - mkdocs build - mike deploy --push dev - - name: Build API docs - run: | - rm -rf api - npm run docs-generateApiDoc + - name: Release API docs - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@bd8c6b06eba6b3d25d72b7a1767993c0aeee42e7 # v3.9.2 + env: + VERSION: ${{ inputs.version }} with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./api keep_files: true - destination_dir: ${{ env.RELEASE_VERSION }}/api - - name: Release API docs to latest if isRelease - if: ${{ env.RELEASE_VERSION != 'dev' }} - uses: peaceiris/actions-gh-pages@v3 + destination_dir: ${{ env.VERSION }}/api + - name: Release API docs to latest + if: ${{ input.alias == 'latest' }} + uses: peaceiris/actions-gh-pages@bd8c6b06eba6b3d25d72b7a1767993c0aeee42e7 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./api diff --git a/.github/workflows/reusable_deploy_layer_stack.yml b/.github/workflows/reusable_deploy_layer_stack.yml index 082d6962cc..380c3b22ad 100644 --- a/.github/workflows/reusable_deploy_layer_stack.yml +++ b/.github/workflows/reusable_deploy_layer_stack.yml @@ -8,15 +8,22 @@ on: workflow_call: inputs: stage: + description: "Deployment stage (BETA, PROD)" required: true type: string artifact-name: + description: "CDK Layer artifact name to download" + required: true + type: string + latest_published_version: + description: "Latest version that is published" required: true type: string secrets: target-account-role: required: true + jobs: deploy-cdk-stack: runs-on: ubuntu-latest @@ -84,4 +91,24 @@ jobs: - name: Unzip artifact run: unzip cdk.out.zip - name: Deploy Layer - run: npm run cdk -w layers -- deploy --app cdk.out --context region=${{ matrix.region }} 'LayerPublisherStack' --require-approval never --verbose + run: npm run cdk -w layers -- deploy --app cdk.out --context region=${{ matrix.region }} 'LayerPublisherStack' --require-approval never --verbose --outputs-file cdk-outputs.json + - name: Store latest Layer ARN + if: ${{ inputs.stage == 'PROD' }} + run: | + mkdir cdk-layer-stack + jq -r -c '.LayerPublisherStack.LatestLayerArn' cdk-outputs.json > cdk-layer-stack/${{ matrix.region }}-layer-version.txt + cat cdk-layer-stack/${{ matrix.region }}-layer-version.txt + - name: Save Layer ARN artifact + if: ${{ inputs.stage == 'PROD' }} + uses: actions/upload-artifact@v3 + with: + name: cdk-layer-stack + path: ./layer/cdk-layer-stack/* # NOTE: upload-artifact does not inherit working-directory setting. + if-no-files-found: error + retention-days: 1 + update_v2_layer_arn_docs: + needs: deploy-cdk-stack + if: ${{ inputs.stage == 'PROD' }} + uses: ./.github/workflows/reusable_update_layer_arn_docs.yml + with: + latest_published_version: ${{ inputs.latest_published_version }} \ No newline at end of file diff --git a/.github/workflows/reusable_update_layer_arn_docs.yml b/.github/workflows/reusable_update_layer_arn_docs.yml new file mode 100644 index 0000000000..db63440d43 --- /dev/null +++ b/.github/workflows/reusable_update_layer_arn_docs.yml @@ -0,0 +1,52 @@ +name: Update V2 Layer ARN Docs + +on: + workflow_call: + inputs: + latest_published_version: + description: "Latest NPM published version to rebuild latest docs for, e.g. 1.5.1" + type: string + required: true + +permissions: + contents: write + +env: + BRANCH: main + +jobs: + publish_layer_arn: + # Force Github action to run only a single job at a time (based on the group name) + # This is to prevent race-condition and inconsistencies with changelog push + concurrency: + group: changelog-build + runs-on: ubuntu-latest + steps: + - name: Checkout repository # reusable workflows start clean, so we need to checkout again + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Git client setup and refresh tip + run: | + git config user.name "Release bot" + git config user.email "aws-devax-open-source@amazon.com" + git config pull.rebase true + git config remote.origin.url >&- || git remote add origin https://github.com/"${origin}" # Git Detached mode (release notes) doesn't have origin + git pull origin "${BRANCH}" + - name: Download CDK layer artifact + uses: actions/download-artifact@v3 + with: + name: cdk-layer-stack + path: cdk-layer-stack/ + - name: Replace layer versions in documentation + run: | + ls -la cdk-layer-stack/ + ./.github/scripts/update_layer_arn.sh cdk-layer-stack + - name: Update documentation in trunk + run: | + HAS_CHANGE=$(git status --porcelain) + test -z "${HAS_CHANGE}" && echo "Nothing to update" && exit 0 + git add docs/index.md + git commit -m "chore: update layer ARN on documentation" + git pull origin "${BRANCH}" # prevents concurrent branch update failing push + git push origin HEAD:refs/heads/"${BRANCH}" From 179fc27e872af8629408db90a32c71555b5af137 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Sun, 26 Feb 2023 02:19:40 +0100 Subject: [PATCH 38/56] chore(ci): fix workflow dependencies (#1333) --- .github/workflows/on-merge-to-main.yml | 2 +- .github/workflows/on_doc_merge.yml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/on-merge-to-main.yml b/.github/workflows/on-merge-to-main.yml index 8ebd13984a..e062ff447f 100644 --- a/.github/workflows/on-merge-to-main.yml +++ b/.github/workflows/on-merge-to-main.yml @@ -22,7 +22,7 @@ jobs: if: ${{ needs.get_pr_details.outputs.prIsMerged == 'true' }} uses: ./.github/workflows/reusable-run-linting-check-and-unit-tests.yml update-release-draft: - needs: publish + needs: run-unit-tests runs-on: ubuntu-latest steps: - name: Checkout code diff --git a/.github/workflows/on_doc_merge.yml b/.github/workflows/on_doc_merge.yml index d412082ee7..9e454a834d 100644 --- a/.github/workflows/on_doc_merge.yml +++ b/.github/workflows/on_doc_merge.yml @@ -11,7 +11,6 @@ on: jobs: release-docs: - needs: changelog permissions: contents: write pages: write From 4c002e504c6f518d0b800b0a20e3fa921995e564 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Sun, 26 Feb 2023 16:51:54 +0100 Subject: [PATCH 39/56] chore: revisited issue & PR templates (#1336) --- .github/ISSUE_TEMPLATE/bug_report.yml | 26 ++++++++++++++-------- .github/ISSUE_TEMPLATE/config.yml | 5 ++++- .github/ISSUE_TEMPLATE/feature_request.yml | 12 ++++++---- .github/ISSUE_TEMPLATE/maintenance.yml | 12 ++++++---- .github/ISSUE_TEMPLATE/rfc.yml | 10 +++++++-- .github/PULL_REQUEST_TEMPLATE.md | 7 ++---- 6 files changed, 47 insertions(+), 25 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index efaecdb144..ef199d42c0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -28,23 +28,31 @@ body: attributes: label: Code snippet description: Please share a code snippet to help us reproduce the issue - render: JavaScript + placeholder: | + ```typescript + some code here + ``` validations: required: true - - type: textarea - id: solution - attributes: - label: Possible Solution - description: If known, please suggest a potential resolution - validations: - required: false - type: textarea id: steps attributes: label: Steps to Reproduce description: Please share how we might be able to reproduce this issue + placeholder: | + 1. In this environment... + 2. With this config... + 3. Run '...' + 4. See error... validations: required: true + - type: textarea + id: solution + attributes: + label: Possible Solution + description: If known, please suggest a potential resolution + validations: + required: false - type: input id: version attributes: @@ -69,7 +77,7 @@ body: label: Packaging format used options: - Lambda Layers - - Npm + - npm multiple: true validations: required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 1aac771b9b..59579b9724 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,4 +2,7 @@ blank_issues_enabled: false contact_links: - name: Ask a question url: https://github.com/awslabs/aws-lambda-powertools-typescript/discussions/new - about: Ask a general question about Lambda Powertools \ No newline at end of file + about: Ask a general question about Lambda Powertools + - name: Join Community Discord Server + url: https://discord.gg/B8zZKbbyET + about: "Check out the #typescript channel" \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 964a4cc897..eb77d4d838 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -7,8 +7,6 @@ body: attributes: value: | Thank you for taking the time to suggest an idea to the Lambda Powertools project. - - *Future readers*: Please react with 👍 and your use case to help us understand customer demand. - type: textarea id: problem attributes: @@ -38,11 +36,17 @@ body: options: - label: This feature request meets [Lambda Powertools Tenets](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#tenets) required: true - - label: Should this be considered in other Lambda Powertools languages? i.e. [Python](https://github.com/awslabs/aws-lambda-powertools-python/), [Java](https://github.com/awslabs/aws-lambda-powertools-java/) + - label: Should this be considered in other Lambda Powertools languages? i.e. [Python](https://github.com/awslabs/aws-lambda-powertools-python/), [Java](https://github.com/awslabs/aws-lambda-powertools-java/), and [.NET](https://github.com/awslabs/aws-lambda-powertools-dotnet/) required: false - type: markdown attributes: value: | --- - **Disclaimer**: After creating an issue, please wait until it is triaged and confirmed by a maintainer before implementing it. This will reduce amount of rework and the chance that a pull request gets rejected. \ No newline at end of file + **Disclaimer**: After creating an issue, please wait until it is triaged and confirmed by a maintainer before implementing it. This will reduce amount of rework and the chance that a pull request gets rejected. + - type: input + id: notes + attributes: + label: Future readers + description: Please not edit this field + value: "Please react with 👍 and your use case to help us understand customer demand." \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/maintenance.yml b/.github/ISSUE_TEMPLATE/maintenance.yml index cfd98ca876..505a284360 100644 --- a/.github/ISSUE_TEMPLATE/maintenance.yml +++ b/.github/ISSUE_TEMPLATE/maintenance.yml @@ -7,8 +7,6 @@ body: attributes: value: | Thank you for taking the time to help us improve this project. - - *Future readers*: Please react with 👍 and your use case to help us understand customer demand. - type: textarea id: activity attributes: @@ -52,11 +50,17 @@ body: options: - label: This request meets [Lambda Powertools Tenets](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#tenets) required: true - - label: Should this be considered in other Lambda Powertools languages? i.e. [Python](https://github.com/awslabs/aws-lambda-powertools-python/), [Java](https://github.com/awslabs/aws-lambda-powertools-java/) + - label: Should this be considered in other Lambda Powertools languages? i.e. [Python](https://github.com/awslabs/aws-lambda-powertools-python/), [Java](https://github.com/awslabs/aws-lambda-powertools-java/), and [.NET](https://github.com/awslabs/aws-lambda-powertools-dotnet/) required: false - type: markdown attributes: value: | --- - **Disclaimer**: After creating an issue, please wait until it is triaged and confirmed by a maintainer before implementing it. This will reduce amount of rework and the chance that a pull request gets rejected. \ No newline at end of file + **Disclaimer**: After creating an issue, please wait until it is triaged and confirmed by a maintainer before implementing it. This will reduce amount of rework and the chance that a pull request gets rejected. + - type: input + id: notes + attributes: + label: Future readers + description: Please not edit this field + value: "Please react with 👍 and your use case to help us understand customer demand." \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/rfc.yml b/.github/ISSUE_TEMPLATE/rfc.yml index 8f6c075476..ea9e5a2b98 100644 --- a/.github/ISSUE_TEMPLATE/rfc.yml +++ b/.github/ISSUE_TEMPLATE/rfc.yml @@ -83,7 +83,7 @@ body: options: - label: This feature request meets [Lambda Powertools Tenets](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#tenets) required: true - - label: Should this be considered in other Lambda Powertools languages? i.e. [Python](https://github.com/awslabs/aws-lambda-powertools-python/), [Java](https://github.com/awslabs/aws-lambda-powertools-java/) + - label: Should this be considered in other Lambda Powertools languages? i.e. [Python](https://github.com/awslabs/aws-lambda-powertools-python/), [Java](https://github.com/awslabs/aws-lambda-powertools-java/), and [.NET](https://github.com/awslabs/aws-lambda-powertools-dotnet/) required: false - type: markdown attributes: @@ -96,4 +96,10 @@ body: * RFC PR: * Approved by: '' - * Reviewed by: '' \ No newline at end of file + * Reviewed by: '' + - type: input + id: notes + attributes: + label: Future readers + description: Please not edit this field + value: "Please react with 👍 and your use case to help us understand customer demand." \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 02304c842c..70037b5ca3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -25,11 +25,6 @@ **Issue number:** -### PR status - -***Is this ready for review?:*** NO -***Is it a breaking change?:*** NO - ## Checklist - [ ] [My changes meet the tenets criteria](https://awslabs.github.io/aws-lambda-powertools-typescript/#tenets) @@ -46,6 +41,8 @@ ### Breaking change checklist +***Is it a breaking change?:*** NO + - [ ] I have documented the migration process - [ ] I have added, implemented necessary warnings (if it can live side by side) From d28582fda70b27cc9f2617448c2b779baf530d77 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Sun, 26 Feb 2023 17:08:22 +0100 Subject: [PATCH 40/56] chore: updated homepage field in package.json files (#1338) --- packages/commons/package.json | 2 +- packages/idempotency/package.json | 2 +- packages/logger/package.json | 2 +- packages/metrics/package.json | 2 +- packages/tracer/package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/commons/package.json b/packages/commons/package.json index 701198d398..2bb3651b09 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -23,7 +23,7 @@ "prepare": "npm run build", "postversion": "git push --tags" }, - "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript/tree/master/packages/metrics#readme", + "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript/tree/main/packages/metrics#readme", "license": "MIT-0", "main": "./lib/index.js", "types": "./lib/index.d.ts", diff --git a/packages/idempotency/package.json b/packages/idempotency/package.json index 5d5fc5e0ba..762e57dffb 100644 --- a/packages/idempotency/package.json +++ b/packages/idempotency/package.json @@ -26,7 +26,7 @@ "prepare": "npm run build", "postversion": "echo \"Not implemented\"" }, - "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript/tree/master/packages/idempotency#readme", + "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript/tree/main/packages/idempotency#readme", "license": "MIT", "main": "./lib/index.js", "types": "./lib/index.d.ts", diff --git a/packages/logger/package.json b/packages/logger/package.json index af2d580bfe..19c60437b1 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -26,7 +26,7 @@ "prepare": "npm run build", "postversion": "git push --tags" }, - "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript/tree/master/packages/logging#readme", + "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript/tree/main/packages/logging#readme", "license": "MIT", "main": "./lib/index.js", "types": "./lib/index.d.ts", diff --git a/packages/metrics/package.json b/packages/metrics/package.json index 901dfba5a5..b1d3a367bf 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -26,7 +26,7 @@ "prepare": "npm run build", "postversion": "git push --tags" }, - "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript/tree/master/packages/metrics#readme", + "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript/tree/main/packages/metrics#readme", "license": "MIT-0", "main": "./lib/index.js", "types": "./lib/index.d.ts", diff --git a/packages/tracer/package.json b/packages/tracer/package.json index 80469ccf06..2226877822 100644 --- a/packages/tracer/package.json +++ b/packages/tracer/package.json @@ -26,7 +26,7 @@ "prepare": "npm run build", "postversion": "git push --tags" }, - "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript/tree/master/packages/tracer#readme", + "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript/tree/main/packages/tracer#readme", "license": "MIT-0", "main": "./lib/index.js", "types": "./lib/index.d.ts", From 907a1f089a94e0d4169a17f556fb31156889538b Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Sun, 26 Feb 2023 22:30:13 +0100 Subject: [PATCH 41/56] tests(logger): revisit Logger unit tests (#1339) * chore: formatting * tests: removed context and revisited test names * tests: formatting * tests: formatting * tests: formatting * tests: simplified names & standardized --- packages/logger/tests/unit/Logger.test.ts | 207 +++++++------- .../EnvironmentVariablesService.test.ts | 44 +-- .../formatter/PowertoolLogFormatter.test.ts | 48 ++-- packages/logger/tests/unit/helpers.test.ts | 9 +- .../tests/unit/middleware/middy.test.ts | 263 +++++------------- 5 files changed, 228 insertions(+), 343 deletions(-) diff --git a/packages/logger/tests/unit/Logger.test.ts b/packages/logger/tests/unit/Logger.test.ts index 4b0775421d..ae1bc74172 100644 --- a/packages/logger/tests/unit/Logger.test.ts +++ b/packages/logger/tests/unit/Logger.test.ts @@ -3,8 +3,10 @@ * * @group unit/logger/all */ - -import { ContextExamples as dummyContext, Events as dummyEvent, LambdaInterface } from '@aws-lambda-powertools/commons'; +import { + ContextExamples as dummyContext, + Events as dummyEvent, LambdaInterface +} from '@aws-lambda-powertools/commons'; import { createLogger, Logger } from '../../src'; import { EnvironmentVariablesService } from '../../src/config'; import { PowertoolLogFormatter } from '../../src/formatter'; @@ -16,6 +18,7 @@ const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); describe('Class: Logger', () => { + const ENVIRONMENT_VARIABLES = process.env; const context = dummyContext.helloworldContext; const event = dummyEvent.Custom.CustomEvent; @@ -45,6 +48,7 @@ describe('Class: Logger', () => { ) => { describe('Feature: log level', () => { + const methodOfLogger = method as keyof ClassThatLogs; test('when the Logger\'s log level is DEBUG, it ' + debugAction + ' print to stdout', () => { @@ -207,7 +211,7 @@ describe('Class: Logger', () => { }); - describe('Feature: capture Lambda context information and add it in the printed logs', () => { + describe('Feature: inject context', () => { const methodOfLogger = method as keyof ClassThatLogs; @@ -231,6 +235,7 @@ describe('Class: Logger', () => { timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', })); + }); test('when the Lambda context is captured, it returns a valid ' + method.toUpperCase() + ' log', () => { @@ -398,7 +403,9 @@ describe('Class: Logger', () => { } } })); + }); + }); describe('Feature: persistent log attributes', () => { @@ -433,6 +440,7 @@ describe('Class: Logger', () => { aws_account_id: '123456789012', aws_region: 'eu-west-1', })); + }); }); @@ -463,6 +471,7 @@ describe('Class: Logger', () => { timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', })); + }); test('when the `_X_AMZN_TRACE_ID` environment variable is NOT set it parses it correctly and adds the Trace ID to the log', () => { @@ -487,6 +496,7 @@ describe('Class: Logger', () => { service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', })); + }); }); @@ -533,7 +543,7 @@ describe('Class: Logger', () => { }); - test('when a logged item has BigInt value, it doen\'t throw TypeError', () => { + test('when a logged item has BigInt value, it doesn\'t throw TypeError', () => { // Prepare const logger = new Logger(); @@ -600,28 +610,13 @@ describe('Class: Logger', () => { describe('Method: addContext', () => { - const baseContext = { - callbackWaitsForEmptyEventLoop: true, - functionVersion: '$LATEST', - functionName: 'foo-bar-function-with-cold-start', - memoryLimitInMB: '128', - logGroupName: '/aws/lambda/foo-bar-function-with-cold-start', - logStreamName: '2021/03/09/[$LATEST]1-5759e988-bd862e3fe1be46a994272793', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function-with-cold-start', - awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', - getRemainingTimeInMillis: () => 1234, - done: () => console.log('Done!'), - fail: () => console.log('Failed!'), - succeed: () => console.log('Succeeded!'), - }; - - test('when called during a COLD START invocation, it populates the logger\'s PowertoolLogData object with coldstart set to true', () => { + test('when called during a cold start invocation, it populates the logger\'s PowertoolLogData object with coldStart set to TRUE', () => { // Prepare const logger = new Logger(); // Act - logger.addContext(baseContext); + logger.addContext(context); // Assess expect(logger).toEqual({ @@ -648,23 +643,25 @@ describe('Class: Logger', () => { lambdaContext: { awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', coldStart: true, - functionName: 'foo-bar-function-with-cold-start', + functionName: 'foo-bar-function', functionVersion: '$LATEST', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function-with-cold-start', + invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', memoryLimitInMB: 128, }, sampleRateValue: undefined, serviceName: 'hello-world', }, + }); + }); test('when called with a context object, the object is not mutated', () => { // Prepare const logger = new Logger(); - const context1 = { ...baseContext, awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678' }; - const context2 = { ...baseContext, awsRequestId: 'd40c98a9-91c4-478c-a179-433c4b978289' }; + const context1 = { ...context, awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678' }; + const context2 = { ...context, awsRequestId: 'd40c98a9-91c4-478c-a179-433c4b978289' }; // Act logger.addContext(context1); @@ -673,14 +670,15 @@ describe('Class: Logger', () => { // Assess expect(context1.awsRequestId).toEqual('c6af9ac6-7b61-11e6-9a41-93e812345678'); expect(context2.awsRequestId).toEqual('d40c98a9-91c4-478c-a179-433c4b978289'); + }); test('when called multiple times, the newer values override earlier values', () => { // Prepare const logger = new Logger(); - const context1 = { ...baseContext, awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678' }; - const context2 = { ...baseContext, awsRequestId: 'd40c98a9-91c4-478c-a179-433c4b978289' }; + const context1 = { ...context, awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678' }; + const context2 = { ...context, awsRequestId: 'd40c98a9-91c4-478c-a179-433c4b978289' }; // Act logger.addContext(context1); @@ -696,7 +694,9 @@ describe('Class: Logger', () => { }) }) ); + }); + }); describe('Method: appendKeys', () => { @@ -727,6 +727,7 @@ describe('Class: Logger', () => { }, }, })); + }); test('when called with user-provided attribute objects, the objects are not mutated', () => { @@ -743,6 +744,7 @@ describe('Class: Logger', () => { // Assess expect(attributes1).toEqual({ keyOne: 'abc' }); expect(attributes2).toEqual({ keyTwo: 'def' }); + }); test('when called multiple times, the newer values override earlier values', () => { @@ -764,7 +766,9 @@ describe('Class: Logger', () => { duplicateKey: 'two' } })); + }); + }); describe('Method: removeKeys', () => { @@ -794,6 +798,7 @@ describe('Class: Logger', () => { }, }, })); + }); test('when called with non-existing keys, the logger\'s property persistentLogAttributes is not mutated and it does not throw an error', () => { @@ -825,6 +830,7 @@ describe('Class: Logger', () => { }, }, })); + }); test('when called multiple times with the same keys, the outcome is the same', () => { @@ -853,6 +859,7 @@ describe('Class: Logger', () => { }, }, })); + }); }); @@ -860,11 +867,10 @@ describe('Class: Logger', () => { describe('Method: injectLambdaContext', () => { beforeEach(() => { - // eslint-disable-next-line @typescript-eslint/no-empty-function - jest.spyOn(console, 'log').mockImplementation(() => { }); + jest.spyOn(console, 'log').mockImplementation(() => ({})); }); - test('when used as decorator, it returns a function with the correct scope of the decorated class', async () => { + test('it returns a decorated method with the correct scope of the decorated class', async () => { // Prepare @@ -875,7 +881,7 @@ describe('Class: Logger', () => { @logger.injectLambdaContext() // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - public handler(_event: TEvent, _context: Context, _callback: Callback): void | Promise { + public async handler(_event: TEvent, _context: Context): Promise { this.myClassMethod(); } @@ -884,9 +890,11 @@ describe('Class: Logger', () => { } } + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); // Act - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); + await handler(event, context); // Assess expect(consoleSpy).toBeCalledTimes(1); @@ -905,7 +913,7 @@ describe('Class: Logger', () => { }); - test('when used as decorator, it returns a function that captures Lambda\'s context information and adds it in the printed logs', async () => { + test('it captures Lambda\'s context information and adds it in the printed logs', async () => { // Prepare const logger = new Logger(); @@ -915,14 +923,16 @@ describe('Class: Logger', () => { @logger.injectLambdaContext() // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - public handler(_event: TEvent, _context: Context, _callback: Callback): void | Promise { + public async handler(_event: TEvent, _context: Context): Promise { logger.info('This is an INFO log with some context'); } } + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); // Act logger.info('An INFO log without context!'); - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); + await handler(event, context); // Assess @@ -949,7 +959,7 @@ describe('Class: Logger', () => { }); - test('when used as decorator on an async handler without context, it returns a function that captures Lambda\'s context information and adds it in the printed logs', async () => { + test('it captures Lambda\'s context information and adds it in the printed logs for async methods', async () => { // Prepare const expectedReturnValue = 'Lambda invoked!'; @@ -966,10 +976,12 @@ describe('Class: Logger', () => { return expectedReturnValue; } } + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); // Act logger.info('An INFO log without context!'); - const actualResult = await new LambdaFunction().handler(event, context); + const actualResult = await handler(event, context); // Assess @@ -997,7 +1009,7 @@ describe('Class: Logger', () => { }); - test('when used as decorator with the clear state flag enabled, the persistent log attributes added in the handler are removed after the handler\'s code is executed', async () => { + test('when clearState is enabled, the persistent log attributes added in the handler are cleared when the method returns', async () => { // Prepare const logger = new Logger({ @@ -1007,40 +1019,38 @@ describe('Class: Logger', () => { biz: 'baz' } }); - - type CustomEvent = { user_id: string }; - class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext({ clearState: true }) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - public handler(event: CustomEvent, _context: Context, _callback: Callback): void | Promise { + public async handler(_event: TEvent, _context: Context): Promise { // Only add these persistent for the scope of this lambda handler logger.appendKeys({ - details: { user_id: event['user_id'] } + details: { user_id: '1234' } }); logger.debug('This is a DEBUG log with the user_id'); logger.debug('This is another DEBUG log with the user_id'); } } - - const persistentAttribs = { ...logger.getPersistentLogAttributes() }; + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); + const persistentAttribsBeforeInvocation = { ...logger.getPersistentLogAttributes() }; // Act - await new LambdaFunction().handler({ user_id: '123456' }, context, () => console.log('Lambda invoked!')); + await handler(event, context); const persistentAttribsAfterInvocation = { ...logger.getPersistentLogAttributes() }; // Assess - expect(persistentAttribs).toEqual({ + expect(persistentAttribsBeforeInvocation).toEqual({ foo: 'bar', biz: 'baz' }); - expect(persistentAttribsAfterInvocation).toEqual(persistentAttribs); + expect(persistentAttribsAfterInvocation).toEqual(persistentAttribsBeforeInvocation); }); - test('when used as decorator with the clear state flag enabled and the handler throws an error, the persistent log attributes added in the handler are removed after the handler\'s code is executed', async () => { + test('when clearState is enabled, the persistent log attributes added in the handler are cleared when the method throws', async () => { // Prepare const logger = new Logger({ @@ -1050,18 +1060,15 @@ describe('Class: Logger', () => { biz: 'baz' } }); - - type CustomEvent = { user_id: string }; - class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext({ clearState: true }) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - public handler(event: CustomEvent, _context: Context, _callback: Callback): void | Promise { + public async handler(_event: TEvent, _context: Context): Promise { // Only add these persistent for the scope of this lambda handler logger.appendKeys({ - details: { user_id: event['user_id'] } + details: { user_id: '1234' } }); logger.debug('This is a DEBUG log with the user_id'); logger.debug('This is another DEBUG log with the user_id'); @@ -1069,45 +1076,42 @@ describe('Class: Logger', () => { throw new Error('Unexpected error occurred!'); } } - - const persistentAttribs = { ...logger.getPersistentLogAttributes() }; + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); + const persistentAttribsBeforeInvocation = { ...logger.getPersistentLogAttributes() }; // Act & Assess - const executeLambdaHandler = async (): Promise => { - await new LambdaFunction().handler({ user_id: '123456' }, context, () => console.log('Lambda invoked!')); - }; - await expect(executeLambdaHandler()).rejects.toThrow('Unexpected error occurred!'); + await expect(handler(event, context)).rejects.toThrow(); const persistentAttribsAfterInvocation = { ...logger.getPersistentLogAttributes() }; - expect(persistentAttribs).toEqual({ + expect(persistentAttribsBeforeInvocation).toEqual({ foo: 'bar', biz: 'baz' }); - expect(persistentAttribsAfterInvocation).toEqual(persistentAttribs); + expect(persistentAttribsAfterInvocation).toEqual(persistentAttribsBeforeInvocation); }); - test('when used as decorator with the log event flag enabled, it logs the event', async () => { + test('when logEvent is enabled, it logs the event in the first log', async () => { // Prepare const logger = new Logger({ logLevel: 'DEBUG', }); const consoleSpy = jest.spyOn(logger['console'], 'info').mockImplementation(); - - type CustomEvent = { user_id: string }; - class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext({ logEvent: true }) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - public handler(_event: CustomEvent, _context: Context, _callback: Callback): void | Promise { + public async handler(_event: TEvent, _context: Context): Promise { return; } } + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); // Act - await new LambdaFunction().handler({ user_id: '123456' }, context, () => console.log('Lambda invoked!')); + await handler(event, context); // Assess expect(consoleSpy).toBeCalledTimes(1); @@ -1123,13 +1127,15 @@ describe('Class: Logger', () => { timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', event: { - user_id: '123456' + key1: 'value1', + key2: 'value2', + key3: 'value3' } })); }); - test('when used as decorator without options, but POWERTOOLS_LOGGER_LOG_EVENT env var is set to true, it logs the event', async () => { + test('when logEvent is enabled via POWERTOOLS_LOGGER_LOG_EVENT env var, it logs the event', async () => { // Prepare process.env.POWERTOOLS_LOGGER_LOG_EVENT = 'true'; @@ -1138,20 +1144,20 @@ describe('Class: Logger', () => { }); const consoleSpy = jest.spyOn(logger['console'], 'info').mockImplementation(); - type CustomEvent = { user_id: string }; - class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext() // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - public handler(_event: CustomEvent, _context: Context, _callback: Callback): void | Promise { + public async handler(_event: TEvent, _context: Context): Promise { return; } } + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); // Act - await new LambdaFunction().handler({ user_id: '123456' }, context, () => console.log('Lambda invoked!')); + await handler(event, context); // Assess expect(consoleSpy).toBeCalledTimes(1); @@ -1167,13 +1173,15 @@ describe('Class: Logger', () => { timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', event: { - user_id: '123456' + key1: 'value1', + key2: 'value2', + key3: 'value3' } })); }); - test('when used as decorator the value of `this` is preserved on the decorated method/class', async () => { + test('it preserves the value of `this` of the decorated method/class', async () => { // Prepare const logger = new Logger({ @@ -1191,7 +1199,7 @@ describe('Class: Logger', () => { @logger.injectLambdaContext() // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - public handler(_event: unknown, _context: Context, _callback: Callback): void | Promise { + public async handler(_event: TEvent, _context: Context): Promise { this.dummyMethod(); return; @@ -1201,11 +1209,11 @@ describe('Class: Logger', () => { logger.info({ message: `memberVariable:${this.memberVariable}` }); } } - - // Act const lambda = new LambdaFunction('someValue'); const handler = lambda.handler.bind(lambda); - await handler({}, context, () => console.log('Lambda invoked!')); + + // Act + await handler({}, context); // Assess expect(consoleSpy).toBeCalledTimes(1); @@ -1224,7 +1232,7 @@ describe('Class: Logger', () => { }); - test('when used as decorator on an async method, the method is awaited correctly', async () => { + test('it awaits the decorated method correctly', async () => { // Prepare const injectLambdaContextAfterOrOnErrorMock = jest.fn().mockReturnValue('worked'); @@ -1270,7 +1278,7 @@ describe('Class: Logger', () => { describe('Method: refreshSampleRateCalculation', () => { - test('when called, it recalculates whether the current Lambda invocation\'s logs will be printed or not', () => { + test('it recalculates whether the current Lambda invocation\'s logs will be printed or not', () => { // Prepare const logger = new Logger({ @@ -1302,7 +1310,7 @@ describe('Class: Logger', () => { const INDENTATION = LogJsonIndent.COMPACT; const loggerOptions = { serviceName: 'parent-service-name', - sampleRateValue: 0.01, + sampleRateValue: 0, }; const parentLogger = new Logger(loggerOptions); @@ -1339,7 +1347,7 @@ describe('Class: Logger', () => { powertoolLogData: { awsRegion: 'eu-west-1', environment: '', - sampleRateValue: 0.01, + sampleRateValue: undefined, serviceName: 'parent-service-name', }, }); @@ -1655,22 +1663,8 @@ describe('Class: Logger', () => { serviceName: 'hello-world', }, }); - }); - const context = { - callbackWaitsForEmptyEventLoop: true, - functionVersion: '$LATEST', - functionName: 'foo-bar-function-with-cold-start', - memoryLimitInMB: '128', - logGroupName: '/aws/lambda/foo-bar-function-with-cold-start', - logStreamName: '2021/03/09/[$LATEST]1-5759e988-bd862e3fe1be46a994272793', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function-with-cold-start', - awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', - getRemainingTimeInMillis: () => 1234, - done: () => console.log('Done!'), - fail: () => console.log('Failed!'), - succeed: () => console.log('Succeeded!'), - }; + }); test('child logger should have parent\'s context in PowertoolLogData', () => { @@ -1706,15 +1700,16 @@ describe('Class: Logger', () => { lambdaContext: { awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', coldStart: true, - functionName: 'foo-bar-function-with-cold-start', + functionName: 'foo-bar-function', functionVersion: '$LATEST', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function-with-cold-start', + invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', memoryLimitInMB: 128, }, sampleRateValue: undefined, serviceName: 'hello-world', }, }); + }); test('child logger should have parent\'s logFormatter', () => { @@ -1734,6 +1729,7 @@ describe('Class: Logger', () => { logFormatter: expect.any(MyCustomLogFormatter), }) ); + }); test('child logger with custom logFormatter in options should have provided logFormatter', () => { @@ -1759,6 +1755,7 @@ describe('Class: Logger', () => { logFormatter: expect.any(MyCustomLogFormatter), }) ); + }); test('child logger should have exact same attributes as the parent logger created with all non-default options', () => { @@ -1819,8 +1816,8 @@ describe('Class: Logger', () => { logger.logEventIfEnabled(event); // Assess - expect(consoleSpy).toBeCalledTimes(0); + }); test('When the feature is enabled via overwrite flag, it DOES log the event', () => { @@ -1848,6 +1845,7 @@ describe('Class: Logger', () => { } }, )); + }); }); @@ -1873,6 +1871,7 @@ describe('Class: Logger', () => { timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', }, null, INDENTATION)); + }); test('when the `POWERTOOLS_DEV` env var is NOT SET it makes log output as one-liner', () => { @@ -1893,7 +1892,9 @@ describe('Class: Logger', () => { timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', })); + }); + }); describe('Method: setConsole()', () => { @@ -1916,7 +1917,9 @@ describe('Class: Logger', () => { ...logger, console: console, }); + }); + }); }); diff --git a/packages/logger/tests/unit/config/EnvironmentVariablesService.test.ts b/packages/logger/tests/unit/config/EnvironmentVariablesService.test.ts index b9aa387614..637c9ca615 100644 --- a/packages/logger/tests/unit/config/EnvironmentVariablesService.test.ts +++ b/packages/logger/tests/unit/config/EnvironmentVariablesService.test.ts @@ -3,7 +3,6 @@ * * @group unit/logger/all */ - import { EnvironmentVariablesService } from '../../../src/config'; describe('Class: EnvironmentVariablesService', () => { @@ -21,7 +20,7 @@ describe('Class: EnvironmentVariablesService', () => { describe('Method: getAwsRegion', () => { - test('It returns the value of the environment variable AWS_REGION', () => { + test('it returns the value of the environment variable AWS_REGION', () => { // Prepare process.env.AWS_REGION = 'us-east-1'; @@ -32,13 +31,14 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual('us-east-1'); + }); }); describe('Method: getCurrentEnvironment', () => { - test('It returns the value of the environment variable AWS_REGION', () => { + test('it returns the value of the environment variable AWS_REGION', () => { // Prepare process.env.ENVIRONMENT = 'stage'; @@ -55,7 +55,7 @@ describe('Class: EnvironmentVariablesService', () => { describe('Method: getFunctionMemory', () => { - test('It returns the value of the environment variable AWS_LAMBDA_FUNCTION_MEMORY_SIZE', () => { + test('it returns the value of the environment variable AWS_LAMBDA_FUNCTION_MEMORY_SIZE', () => { // Prepare process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '123456'; @@ -66,13 +66,14 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toBe(123456); + }); }); describe('Method: getFunctionName', () => { - test('It returns the value of the environment variable AWS_LAMBDA_FUNCTION_NAME', () => { + test('it returns the value of the environment variable AWS_LAMBDA_FUNCTION_NAME', () => { // Prepare process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-lambda-function'; @@ -83,13 +84,14 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual('my-lambda-function'); + }); }); describe('Method: getFunctionVersion', () => { - test('It returns the value of the environment variable AWS_LAMBDA_FUNCTION_VERSION', () => { + test('it returns the value of the environment variable AWS_LAMBDA_FUNCTION_VERSION', () => { // Prepare process.env.AWS_LAMBDA_FUNCTION_VERSION = '1.4.0'; @@ -100,13 +102,14 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual('1.4.0'); + }); }); describe('Method: getLogEvent', () => { - test('It returns true if the environment variable POWERTOOLS_LOGGER_LOG_EVENT is "true"', () => { + test('it returns true if the environment variable POWERTOOLS_LOGGER_LOG_EVENT is "true"', () => { // Prepare process.env.POWERTOOLS_LOGGER_LOG_EVENT = 'true'; @@ -117,9 +120,10 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual(true); + }); - test('It returns false if the environment variable POWERTOOLS_LOGGER_LOG_EVENT is "false"', () => { + test('it returns false if the environment variable POWERTOOLS_LOGGER_LOG_EVENT is "false"', () => { // Prepare process.env.POWERTOOLS_LOGGER_LOG_EVENT = 'false'; @@ -130,9 +134,10 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual(false); + }); - test('It returns false if the environment variable POWERTOOLS_LOGGER_LOG_EVENT is "somethingsilly"', () => { + test('it returns false if the environment variable POWERTOOLS_LOGGER_LOG_EVENT is "somethingsilly"', () => { // Prepare process.env.POWERTOOLS_LOGGER_LOG_EVENT = 'somethingsilly'; @@ -143,13 +148,14 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual(false); + }); }); describe('Method: getLogLevel', () => { - test('It returns the value of the environment variable LOG_LEVEL', () => { + test('it returns the value of the environment variable LOG_LEVEL', () => { // Prepare process.env.LOG_LEVEL = 'ERROR'; @@ -160,13 +166,14 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual('ERROR'); + }); }); describe('Method: getSampleRateValue', () => { - test('It returns the value of the environment variable POWERTOOLS_LOGGER_SAMPLE_RATE', () => { + test('it returns the value of the environment variable POWERTOOLS_LOGGER_SAMPLE_RATE', () => { // Prepare process.env.POWERTOOLS_LOGGER_SAMPLE_RATE = '0.01'; @@ -177,13 +184,14 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual(0.01); + }); }); describe('Method: isDevMode', () => { - test('It returns true if the environment variable POWERTOOLS_DEV is "true"', () => { + test('it returns true if the environment variable POWERTOOLS_DEV is "true"', () => { // Prepare process.env.POWERTOOLS_DEV = 'true'; @@ -194,9 +202,10 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual(true); + }); - test('It returns false if the environment variable POWERTOOLS_DEV is "false"', () => { + test('it returns false if the environment variable POWERTOOLS_DEV is "false"', () => { // Prepare process.env.POWERTOOLS_DEV = 'false'; @@ -207,9 +216,10 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual(false); + }); - test('It returns false if the environment variable POWERTOOLS_DEV is NOT set', () => { + test('it returns false if the environment variable POWERTOOLS_DEV is NOT set', () => { // Prepare process.env.POWERTOOLS_DEV = 'somethingsilly'; @@ -220,9 +230,10 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual(false); + }); - test('It returns false if the environment variable POWERTOOLS_DEV is "somethingsilly"', () => { + test('it returns false if the environment variable POWERTOOLS_DEV is "somethingsilly"', () => { // Prepare process.env.POWERTOOLS_DEV = 'somethingsilly'; @@ -233,6 +244,7 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual(false); + }); }); @@ -253,7 +265,7 @@ describe('Class: EnvironmentVariablesService', () => { [ '0', false ] ]; - test.each(valuesToTest)('It takes string "%s" and returns %s', (input, output) => { + test.each(valuesToTest)('it takes string "%s" and returns %s', (input, output) => { // Prepare const service = new EnvironmentVariablesService(); // Act diff --git a/packages/logger/tests/unit/formatter/PowertoolLogFormatter.test.ts b/packages/logger/tests/unit/formatter/PowertoolLogFormatter.test.ts index 8b7f52a82e..4d8d4100f5 100644 --- a/packages/logger/tests/unit/formatter/PowertoolLogFormatter.test.ts +++ b/packages/logger/tests/unit/formatter/PowertoolLogFormatter.test.ts @@ -3,7 +3,6 @@ * * @group unit/logger/all */ - import { AssertionError, strictEqual } from 'assert'; import { PowertoolLogFormatter } from '../../../src/formatter'; import { UnformattedAttributes } from '../../../src/types'; @@ -19,7 +18,7 @@ describe('Class: PowertoolLogFormatter', () => { describe('Method: formatAttributes', () => { - test('When optional parameters DO NOT have a value set, it returns an object with expected structure and values', () => { + test('when optional parameters DO NOT have a value set, it returns an object with expected structure and values', () => { // Prepare const formatter = new PowertoolLogFormatter(); @@ -51,9 +50,10 @@ describe('Class: PowertoolLogFormatter', () => { timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', }); + }); - test('When optional parameters DO have a value set, it returns an object with expected structure and values', () => { + test('when optional parameters DO have a value set, it returns an object with expected structure and values', () => { // Prepare const formatter = new PowertoolLogFormatter(); @@ -94,19 +94,18 @@ describe('Class: PowertoolLogFormatter', () => { timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', }); + }); }); describe('Method: formatError', () => { - test('When an error of type Error is passed, it returns an object with expected structure and values', () => { + test('when an error of type Error is passed, it returns an object with expected structure and values', () => { // Prepare const formatter = new PowertoolLogFormatter(); const shouldThrow = (): void => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore throw new Error('Ouch!'); }; @@ -126,13 +125,15 @@ describe('Class: PowertoolLogFormatter', () => { } expect(shouldThrow).toThrowError(expect.any(Error)); + }); - test('When an error of type ReferenceError is passed, it returns an object with expected structure and values', () => { + test('when an error of type ReferenceError is passed, it returns an object with expected structure and values', () => { // Prepare const formatter = new PowertoolLogFormatter(); const shouldThrow = (): void => { + // This is a reference error purposely to test the formatter // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore doesNotExist; @@ -157,13 +158,11 @@ describe('Class: PowertoolLogFormatter', () => { }); - test('When an error of type AssertionError is passed, it returns an object with expected structure and values', () => { + test('when an error of type AssertionError is passed, it returns an object with expected structure and values', () => { // Prepare const formatter = new PowertoolLogFormatter(); const shouldThrow = (): void => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore strictEqual(1, 2); }; @@ -186,13 +185,11 @@ describe('Class: PowertoolLogFormatter', () => { }); - test('When an error of type RangeError is passed, it returns an object with expected structure and values', () => { + test('when an error of type RangeError is passed, it returns an object with expected structure and values', () => { // Prepare const formatter = new PowertoolLogFormatter(); const shouldThrow = (): void => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore throw new RangeError('The argument must be between 10 and 20'); }; @@ -214,13 +211,11 @@ describe('Class: PowertoolLogFormatter', () => { expect(shouldThrow).toThrowError(expect.any(RangeError)); }); - test('When an error of type SyntaxError is passed, it returns an object with expected structure and values', () => { + test('when an error of type SyntaxError is passed, it returns an object with expected structure and values', () => { // Prepare const formatter = new PowertoolLogFormatter(); const shouldThrow = (): void => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore eval('foo bar'); }; @@ -243,11 +238,12 @@ describe('Class: PowertoolLogFormatter', () => { }); - test('When an error of type TypeError is passed, it returns an object with expected structure and values', () => { + test('when an error of type TypeError is passed, it returns an object with expected structure and values', () => { // Prepare const formatter = new PowertoolLogFormatter(); const shouldThrow = (): void => { + // This is a reference error purposely to test the formatter // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore null.foo(); @@ -270,15 +266,14 @@ describe('Class: PowertoolLogFormatter', () => { } expect(shouldThrow).toThrowError(expect.any(TypeError)); + }); - test('When an error of type URIError is passed, it returns an object with expected structure and values', () => { + test('when an error of type URIError is passed, it returns an object with expected structure and values', () => { // Prepare const formatter = new PowertoolLogFormatter(); const shouldThrow = (): void => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore decodeURIComponent('%'); }; @@ -298,13 +293,14 @@ describe('Class: PowertoolLogFormatter', () => { } expect(shouldThrow).toThrowError(expect.any(URIError)); + }); }); describe('Method: formatTimestamp', () => { - test('It returns a datetime value ISO 8601 compliant', () => { + test('it returns a datetime value ISO 8601 compliant', () => { // Prepare const formatter = new PowertoolLogFormatter(); @@ -314,13 +310,14 @@ describe('Class: PowertoolLogFormatter', () => { // Assess expect(timestamp).toEqual('2016-06-20T12:08:10.000Z'); + }); }); describe('Method: getCodeLocation', () => { - test('When the stack IS present, it returns a datetime value ISO 8601 compliant', () => { + test('when the stack IS present, it returns a datetime value ISO 8601 compliant', () => { // Prepare const formatter = new PowertoolLogFormatter(); @@ -333,9 +330,10 @@ describe('Class: PowertoolLogFormatter', () => { // Assess expect(errorLocation).toEqual('/home/foo/bar/some-file.ts:154'); + }); - test('When the stack IS NOT present, it returns a datetime value ISO 8601 compliant', () => { + test('when the stack IS NOT present, it returns a datetime value ISO 8601 compliant', () => { // Prepare const formatter = new PowertoolLogFormatter(); @@ -346,9 +344,10 @@ describe('Class: PowertoolLogFormatter', () => { // Assess expect(errorLocation).toEqual(''); + }); - test('When the stack IS NOT present, it returns a datetime value ISO 8601 compliant', () => { + test('when the stack IS NOT present, it returns a datetime value ISO 8601 compliant', () => { // Prepare const formatter = new PowertoolLogFormatter(); @@ -359,6 +358,7 @@ describe('Class: PowertoolLogFormatter', () => { // Assess expect(errorLocation).toEqual(''); + }); }); diff --git a/packages/logger/tests/unit/helpers.test.ts b/packages/logger/tests/unit/helpers.test.ts index 4f953620fc..7235dec4ab 100644 --- a/packages/logger/tests/unit/helpers.test.ts +++ b/packages/logger/tests/unit/helpers.test.ts @@ -3,7 +3,6 @@ * * @group unit/logger/all */ - import { Console } from 'console'; import { ConfigServiceInterface, EnvironmentVariablesService } from '../../src/config'; import { LogFormatter, PowertoolLogFormatter } from '../../src/formatter'; @@ -197,6 +196,7 @@ describe('Helper: createLogger function', () => { logLevel: 'DEBUG', logFormatter: {}, })); + }); test('when a custom logLevel is passed, returns a Logger instance with the correct properties', () => { @@ -225,6 +225,7 @@ describe('Helper: createLogger function', () => { logLevel: 'ERROR', logFormatter: expect.any(PowertoolLogFormatter), })); + }); test('when no log level is set, returns a Logger instance with INFO level', () => { @@ -263,6 +264,7 @@ describe('Helper: createLogger function', () => { serviceName: 'hello-world', }, }); + }); test('when a custom sampleRateValue is passed, returns a Logger instance with the correct properties', () => { @@ -291,6 +293,7 @@ describe('Helper: createLogger function', () => { logLevel: 'DEBUG', logFormatter: {}, })); + }); test('when a custom customConfigService is passed, returns a Logger instance with the correct properties', () => { @@ -346,6 +349,7 @@ describe('Helper: createLogger function', () => { logLevel: 'INFO', logFormatter: {}, })); + }); test('when custom persistentLogAttributes is passed, returns a Logger instance with the correct properties', () => { @@ -388,6 +392,7 @@ describe('Helper: createLogger function', () => { logLevel: 'DEBUG', logFormatter: {}, })); + }); test('when a custom environment is passed, returns a Logger instance with the correct properties', () => { @@ -416,7 +421,9 @@ describe('Helper: createLogger function', () => { logLevel: 'DEBUG', logFormatter: {}, })); + }); + }); }); \ No newline at end of file diff --git a/packages/logger/tests/unit/middleware/middy.test.ts b/packages/logger/tests/unit/middleware/middy.test.ts index fff1960b1b..ed4abdb262 100644 --- a/packages/logger/tests/unit/middleware/middy.test.ts +++ b/packages/logger/tests/unit/middleware/middy.test.ts @@ -4,6 +4,7 @@ * @group unit/logger/all */ +import { ContextExamples as dummyContext, Events as dummyEvent } from '@aws-lambda-powertools/commons'; import { ConfigServiceInterface, EnvironmentVariablesService } from '../../../src/config'; import { injectLambdaContext } from '../../../src/middleware/middy'; import { Logger } from './../../../src'; @@ -17,6 +18,8 @@ const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); describe('Middy middleware', () => { const ENVIRONMENT_VARIABLES = process.env; + const context = dummyContext.helloworldContext; + const event = dummyEvent.Custom.CustomEvent; beforeEach(() => { jest.resetModules(); @@ -37,31 +40,12 @@ describe('Middy middleware', () => { // Prepare const logger = new Logger(); - const lambdaHandler = (): void => { + const handler = middy((): void => { logger.info('This is an INFO log with some context'); - }; - const handler = middy(lambdaHandler).use(injectLambdaContext(logger)); - const event = { foo: 'bar' }; - const getRandomInt = (): number => Math.floor(Math.random() * 1000000000); - - const awsRequestId = getRandomInt().toString(); - const context = { - callbackWaitsForEmptyEventLoop: true, - functionVersion: '$LATEST', - functionName: 'foo-bar-function', - memoryLimitInMB: '128', - logGroupName: '/aws/lambda/foo-bar-function', - logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - awsRequestId: awsRequestId, - getRemainingTimeInMillis: () => 1234, - done: () => console.log('Done!'), - fail: () => console.log('Failed!'), - succeed: () => console.log('Succeeded!'), - }; + }).use(injectLambdaContext(logger)); // Act - await handler(event, context, () => console.log('Lambda invoked!')); + await handler(event, context); // Assess expect(logger).toEqual(expect.objectContaining({ @@ -72,7 +56,7 @@ describe('Middy middleware', () => { awsRegion: 'eu-west-1', environment: '', lambdaContext: { - awsRequestId: awsRequestId, + awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', coldStart: true, functionName: 'foo-bar-function', functionVersion: '$LATEST', @@ -94,34 +78,13 @@ describe('Middy middleware', () => { // Prepare const logger = new Logger(); const anotherLogger = new Logger(); - const lambdaHandler = (): void => { + const handler = middy((): void => { logger.info('This is an INFO log with some context'); anotherLogger.info('This is an INFO log with some context'); - }; - const handler = middy(lambdaHandler).use(injectLambdaContext([ logger, anotherLogger ])); - const event = { foo: 'bar' }; - - const getRandomInt = (): number => Math.floor(Math.random() * 1000000000); - - const awsRequestId = getRandomInt().toString(); - - const context = { - callbackWaitsForEmptyEventLoop: true, - functionVersion: '$LATEST', - functionName: 'foo-bar-function', - memoryLimitInMB: '128', - logGroupName: '/aws/lambda/foo-bar-function', - logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - awsRequestId: awsRequestId, - getRemainingTimeInMillis: () => 1234, - done: () => console.log('Done!'), - fail: () => console.log('Failed!'), - succeed: () => console.log('Succeeded!'), - }; + }).use(injectLambdaContext([ logger, anotherLogger ])); // Act - await handler(event, context, () => console.log('Lambda invoked!')); + await handler(event, context); // Assess const expectation = expect.objectContaining({ @@ -132,7 +95,7 @@ describe('Middy middleware', () => { awsRegion: 'eu-west-1', environment: '', lambdaContext: { - awsRequestId: awsRequestId, + awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', coldStart: true, functionName: 'foo-bar-function', functionVersion: '$LATEST', @@ -168,46 +131,31 @@ describe('Middy middleware', () => { biz: 'baz' } }); - const context = { - callbackWaitsForEmptyEventLoop: true, - functionVersion: '$LATEST', - functionName: 'foo-bar-function', - memoryLimitInMB: '128', - logGroupName: '/aws/lambda/foo-bar-function', - logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - awsRequestId: 'abcdef123456abcdef123456', - getRemainingTimeInMillis: () => 1234, - done: () => console.log('Done!'), - fail: () => console.log('Failed!'), - succeed: () => console.log('Succeeded!'), - }; - const lambdaHandler = (event: { user_id: string }): void => { + const handler = middy((): void => { // Only add these persistent for the scope of this lambda handler logger.appendKeys({ - details: { user_id: event['user_id'] } + details: { user_id: '1234' } }); logger.debug('This is a DEBUG log with the user_id'); logger.debug('This is another DEBUG log with the user_id'); - }; - const handler = middy(lambdaHandler).use(injectLambdaContext(logger, { clearState: true })); - const persistentAttribs = { ...logger.getPersistentLogAttributes() }; - + }).use(injectLambdaContext(logger, { clearState: true })); + const persistentAttribsBeforeInvocation = { ...logger.getPersistentLogAttributes() }; + // Act - await handler({ user_id: '123456' }, context, () => console.log('Lambda invoked!')); - const persistentAttribsAfterInvocation = { ...logger.getPersistentLogAttributes() }; - + await handler(event, context); + // Assess - expect(persistentAttribs).toEqual({ + const persistentAttribsAfterInvocation = { ...logger.getPersistentLogAttributes() }; + expect(persistentAttribsBeforeInvocation).toEqual({ foo: 'bar', biz: 'baz' }); - expect(persistentAttribsAfterInvocation).toEqual(persistentAttribs); + expect(persistentAttribsAfterInvocation).toEqual(persistentAttribsBeforeInvocation); }); - test('when enabled and the handler throws an error, the persistent log attributes added within the handler scope are removed after the invocation ends', async () => { + test('when enabled, the persistent log attributes added within the handler scope are removed after the invocation ends even if an error is thrown', async () => { // Prepare const logger = new Logger({ @@ -217,46 +165,27 @@ describe('Middy middleware', () => { biz: 'baz' } }); - const context = { - callbackWaitsForEmptyEventLoop: true, - functionVersion: '$LATEST', - functionName: 'foo-bar-function', - memoryLimitInMB: '128', - logGroupName: '/aws/lambda/foo-bar-function', - logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - awsRequestId: 'abcdef123456abcdef123456', - getRemainingTimeInMillis: () => 1234, - done: () => console.log('Done!'), - fail: () => console.log('Failed!'), - succeed: () => console.log('Succeeded!'), - }; - - const lambdaHandler = (event: { user_id: string }): void => { + const handler = middy((): void => { // Only add these persistent for the scope of this lambda handler logger.appendKeys({ - details: { user_id: event['user_id'] } + details: { user_id: '1234' } }); logger.debug('This is a DEBUG log with the user_id'); logger.debug('This is another DEBUG log with the user_id'); throw new Error('Unexpected error occurred!'); - }; - - const persistentAttribs = { ...logger.getPersistentLogAttributes() }; - const handler = middy(lambdaHandler).use(injectLambdaContext(logger, { clearState: true })); - + }).use(injectLambdaContext(logger, { clearState: true })); + const persistentAttribsBeforeInvocation = { ...logger.getPersistentLogAttributes() }; + // Act & Assess - const executeLambdaHandler = async (): Promise => { - await handler({ user_id: '123456' }, context, () => console.log('Lambda invoked!')); - }; - await expect(executeLambdaHandler()).rejects.toThrow('Unexpected error occurred!'); + await expect(handler(event, context)) + .rejects.toThrow(); const persistentAttribsAfterInvocation = { ...logger.getPersistentLogAttributes() }; - expect(persistentAttribs).toEqual({ + expect(persistentAttribsBeforeInvocation).toEqual({ foo: 'bar', biz: 'baz' }); - expect(persistentAttribsAfterInvocation).toEqual(persistentAttribs); + expect(persistentAttribsAfterInvocation).toEqual(persistentAttribsBeforeInvocation); }); @@ -264,35 +193,17 @@ describe('Middy middleware', () => { describe('Feature: log event', () => { - test('when a logger is passed with option logEvent set to true, it logs the event', async () => { + test('when enabled, it logs the event', async () => { // Prepare const logger = new Logger(); const consoleSpy = jest.spyOn(logger['console'], 'info').mockImplementation(); - const lambdaHandler = (): void => { + const handler = middy((): void => { logger.info('This is an INFO log with some context'); - }; - const handler = middy(lambdaHandler).use(injectLambdaContext(logger , { logEvent: true })); - const event = { foo: 'bar' }; - const getRandomInt = (): number => Math.floor(Math.random() * 1000000000); - const awsRequestId = getRandomInt().toString(); - const context = { - callbackWaitsForEmptyEventLoop: true, - functionVersion: '$LATEST', - functionName: 'foo-bar-function', - memoryLimitInMB: '128', - logGroupName: '/aws/lambda/foo-bar-function', - logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - awsRequestId: awsRequestId, - getRemainingTimeInMillis: () => 1234, - done: () => console.log('Done!'), - fail: () => console.log('Failed!'), - succeed: () => console.log('Succeeded!'), - }; + }).use(injectLambdaContext(logger , { logEvent: true })); // Act - await handler(event, context, () => console.log('Lambda invoked!')); + await handler(event, context); // Assess expect(consoleSpy).toBeCalledTimes(2); @@ -301,20 +212,22 @@ describe('Middy middleware', () => { function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', function_memory_size: 128, function_name: 'foo-bar-function', - function_request_id: awsRequestId, + function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'Lambda invocation event', service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', event: { - foo: 'bar' + key1: 'value1', + key2: 'value2', + key3: 'value3', } })); }); - test('when a logger is passed with option logEvent set to true, while also having a custom configService, it logs the event', async () => { + test('when enabled, while also having a custom configService, it logs the event', async () => { // Prepare const configService: ConfigServiceInterface = { @@ -348,30 +261,12 @@ describe('Middy middleware', () => { customConfigService: configService, }); const consoleSpy = jest.spyOn(logger['console'], 'info').mockImplementation(); - const lambdaHandler = (): void => { + const handler = middy((): void => { logger.info('This is an INFO log with some context'); - }; - const handler = middy(lambdaHandler).use(injectLambdaContext(logger , { logEvent: true })); - const event = { foo: 'bar' }; - const getRandomInt = (): number => Math.floor(Math.random() * 1000000000); - const awsRequestId = getRandomInt().toString(); - const context = { - callbackWaitsForEmptyEventLoop: true, - functionVersion: '$LATEST', - functionName: 'foo-bar-function', - memoryLimitInMB: '128', - logGroupName: '/aws/lambda/foo-bar-function', - logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - awsRequestId: awsRequestId, - getRemainingTimeInMillis: () => 1234, - done: () => console.log('Done!'), - fail: () => console.log('Failed!'), - succeed: () => console.log('Succeeded!'), - }; - + }).use(injectLambdaContext(logger , { logEvent: true })); + // Act - await handler(event, context, () => console.log('Lambda invoked!')); + await handler(event, context); // Assess expect(consoleSpy).toBeCalledTimes(2); @@ -380,49 +275,33 @@ describe('Middy middleware', () => { function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', function_memory_size: 128, function_name: 'foo-bar-function', - function_request_id: awsRequestId, + function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'Lambda invocation event', service: 'my-backend-service', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', event: { - foo: 'bar' + key1: 'value1', + key2: 'value2', + key3: 'value3', } })); }); - test('when a logger is passed without options, but POWERTOOLS_LOGGER_LOG_EVENT env var is set to true, it logs the event', async () => { + test('when enabled via POWERTOOLS_LOGGER_LOG_EVENT env var, it logs the event', async () => { // Prepare process.env.POWERTOOLS_LOGGER_LOG_EVENT = 'true'; const logger = new Logger(); const consoleSpy = jest.spyOn(logger['console'], 'info').mockImplementation(); - const lambdaHandler = (): void => { + const handler = middy((): void => { logger.info('This is an INFO log with some context'); - }; - const handler = middy(lambdaHandler).use(injectLambdaContext(logger)); - const event = { foo: 'bar' }; - const getRandomInt = (): number => Math.floor(Math.random() * 1000000000); - const awsRequestId = getRandomInt().toString(); - const context = { - callbackWaitsForEmptyEventLoop: true, - functionVersion: '$LATEST', - functionName: 'foo-bar-function', - memoryLimitInMB: '128', - logGroupName: '/aws/lambda/foo-bar-function', - logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - awsRequestId: awsRequestId, - getRemainingTimeInMillis: () => 1234, - done: () => console.log('Done!'), - fail: () => console.log('Failed!'), - succeed: () => console.log('Succeeded!'), - }; - + }).use(injectLambdaContext(logger)); + // Act - await handler(event, context, () => console.log('Lambda invoked!')); + await handler(event, context); // Assess expect(consoleSpy).toBeCalledTimes(2); @@ -431,49 +310,33 @@ describe('Middy middleware', () => { function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', function_memory_size: 128, function_name: 'foo-bar-function', - function_request_id: awsRequestId, + function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'Lambda invocation event', service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', event: { - foo: 'bar' + key1: 'value1', + key2: 'value2', + key3: 'value3', } })); }); - test('when a logger is passed with option logEvent set to false, but POWERTOOLS_LOGGER_LOG_EVENT env var is set to true, it does not log the event', async () => { + test('when disabled in the middleware, but enabled via POWERTOOLS_LOGGER_LOG_EVENT env var, it still doesn\'t log the event', async () => { // Prepare process.env.POWERTOOLS_LOGGER_LOG_EVENT = 'true'; const logger = new Logger(); const consoleSpy = jest.spyOn(logger['console'], 'info').mockImplementation(); - const lambdaHandler = (): void => { + const handler = middy((): void => { logger.info('This is an INFO log'); - }; - const handler = middy(lambdaHandler).use(injectLambdaContext(logger, { logEvent: false })); - const event = { foo: 'bar' }; - const getRandomInt = (): number => Math.floor(Math.random() * 1000000000); - const awsRequestId = getRandomInt().toString(); - const context = { - callbackWaitsForEmptyEventLoop: true, - functionVersion: '$LATEST', - functionName: 'foo-bar-function', - memoryLimitInMB: '128', - logGroupName: '/aws/lambda/foo-bar-function', - logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - awsRequestId: awsRequestId, - getRemainingTimeInMillis: () => 1234, - done: () => console.log('Done!'), - fail: () => console.log('Failed!'), - succeed: () => console.log('Succeeded!'), - }; - + }).use(injectLambdaContext(logger, { logEvent: false })); + // Act - await handler(event, context, () => console.log('Lambda invoked!')); + await handler(event, context); // Assess expect(consoleSpy).toBeCalledTimes(1); @@ -482,7 +345,7 @@ describe('Middy middleware', () => { function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', function_memory_size: 128, function_name: 'foo-bar-function', - function_request_id: awsRequestId, + function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'This is an INFO log', service: 'hello-world', From 6b3230489895dc1abdfc6ad56daeeb555fda529f Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 27 Feb 2023 09:25:04 +0100 Subject: [PATCH 42/56] fix(logger): middleware stores initial persistent attributes correctly (#1329) --- packages/logger/src/middleware/middy.ts | 4 +- packages/logger/tests/unit/Logger.test.ts | 85 ++++++++++++++++--- .../tests/unit/middleware/middy.test.ts | 49 +++++++++++ 3 files changed, 123 insertions(+), 15 deletions(-) diff --git a/packages/logger/src/middleware/middy.ts b/packages/logger/src/middleware/middy.ts index d0c622aaf5..9b0140a76f 100644 --- a/packages/logger/src/middleware/middy.ts +++ b/packages/logger/src/middleware/middy.ts @@ -35,9 +35,9 @@ const injectLambdaContext = (target: Logger | Logger[], options?: HandlerOptions const persistentAttributes: LogAttributes[] = []; const injectLambdaContextBefore = async (request: MiddyLikeRequest): Promise => { - loggers.forEach((logger: Logger) => { + loggers.forEach((logger: Logger, index: number) => { if (options && options.clearState === true) { - persistentAttributes.push({ ...logger.getPersistentLogAttributes() }); + persistentAttributes[index] = ({ ...logger.getPersistentLogAttributes() }); } Logger.injectLambdaContextBefore(logger, request.event, request.context, options); }); diff --git a/packages/logger/tests/unit/Logger.test.ts b/packages/logger/tests/unit/Logger.test.ts index ae1bc74172..5c2b3fd95e 100644 --- a/packages/logger/tests/unit/Logger.test.ts +++ b/packages/logger/tests/unit/Logger.test.ts @@ -1235,11 +1235,10 @@ describe('Class: Logger', () => { test('it awaits the decorated method correctly', async () => { // Prepare - const injectLambdaContextAfterOrOnErrorMock = jest.fn().mockReturnValue('worked'); - // Temporarily override the cleanup static method so that we can "spy" on it. - // This method is always called after the handler has returned in the decorator - // implementation. - Logger.injectLambdaContextAfterOrOnError = injectLambdaContextAfterOrOnErrorMock; + const injectLambdaContextAfterOrOnErrorSpy = jest.spyOn( + Logger, + 'injectLambdaContextAfterOrOnError' + ); const logger = new Logger({ logLevel: 'DEBUG', }); @@ -1248,7 +1247,7 @@ describe('Class: Logger', () => { @logger.injectLambdaContext() // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - public async handler(_event: unknown, _context: Context, _callback: Callback): void | Promise { + public async handler(_event: unknown, _context: unknown): Promise { await this.dummyMethod(); logger.info('This is a DEBUG log'); @@ -1259,18 +1258,78 @@ describe('Class: Logger', () => { return; } } - - // Act const lambda = new LambdaFunction(); const handler = lambda.handler.bind(lambda); - await handler({}, context, () => console.log('Lambda invoked!')); + + // Act + await handler({}, context); // Assess expect(consoleSpy).toBeCalledTimes(1); - // Here we assert that the logger.info method is called before the cleanup function that should awlays - // be called after the handler has returned. If logger.info is called after it means the decorator is - // NOT awaiting the handler which would cause the test to fail. - expect(consoleSpy.mock.invocationCallOrder[0]).toBeLessThan(injectLambdaContextAfterOrOnErrorMock.mock.invocationCallOrder[0]); + // Here we assert that the logger.info method is called before the cleanup function that should always + // be called ONLY after the handler has returned. If logger.info is called after the cleanup function + // it means the decorator is NOT awaiting the handler which would cause the test to fail. + expect(consoleSpy.mock.invocationCallOrder[0]) + .toBeLessThan(injectLambdaContextAfterOrOnErrorSpy.mock.invocationCallOrder[0]); + + }); + + test('when logEvent and clearState are both TRUE, and the logger has persistent attributes, any key added in the handler is cleared properly', async () => { + + // Prepare + const logger = new Logger({ + persistentLogAttributes: { + version: '1.0.0', + } + }); + const consoleSpy = jest.spyOn(logger['console'], 'info').mockImplementation(); + class LambdaFunction implements LambdaInterface { + @logger.injectLambdaContext({ clearState: true, logEvent: true }) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + public async handler(event: { foo: string }, _context: unknown): Promise { + logger.appendKeys({ foo: event.foo }); + + return; + } + } + const lambda = new LambdaFunction(); + const handler = lambda.handler.bind(lambda); + + // Act + await handler({ foo: 'bar' }, {} as Context); + await handler({ foo: 'baz' }, {} as Context); + await handler({ foo: 'biz' }, {} as Context); + await handler({ foo: 'buz' }, {} as Context); + await handler({ foo: 'boz' }, {} as Context); + + expect(consoleSpy).toBeCalledTimes(5); + for (let i = 1; i === 5; i++) { + expect(consoleSpy).toHaveBeenNthCalledWith( + i, + expect.stringContaining('\"message\":\"Lambda invocation event\"'), + ); + expect(consoleSpy).toHaveBeenNthCalledWith( + i, + expect.stringContaining('\"version\":\"1.0.0\"'), + ); + } + expect(consoleSpy).toHaveBeenNthCalledWith( + 2, + expect.not.stringContaining('\"foo\":\"bar\"') + ); + expect(consoleSpy).toHaveBeenNthCalledWith( + 3, + expect.not.stringContaining('\"foo\":\"baz\"') + ); + expect(consoleSpy).toHaveBeenNthCalledWith( + 4, + expect.not.stringContaining('\"foo\":\"biz\"') + ); + expect(consoleSpy).toHaveBeenNthCalledWith( + 5, + expect.not.stringContaining('\"foo\":\"buz\"') + ); }); diff --git a/packages/logger/tests/unit/middleware/middy.test.ts b/packages/logger/tests/unit/middleware/middy.test.ts index ed4abdb262..c12680a1c5 100644 --- a/packages/logger/tests/unit/middleware/middy.test.ts +++ b/packages/logger/tests/unit/middleware/middy.test.ts @@ -11,6 +11,7 @@ import { Logger } from './../../../src'; import middy from '@middy/core'; import { PowertoolLogFormatter } from '../../../src/formatter'; import { Console } from 'console'; +import { Context } from 'aws-lambda'; const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); @@ -354,6 +355,54 @@ describe('Middy middleware', () => { })); }); + test('when logEvent and clearState are both TRUE, and the logger has persistent attributes, any key added in the handler is cleared properly', async () => { + + const logger = new Logger({ + persistentLogAttributes: { + version: '1.0.0', + } + }); + const consoleSpy = jest.spyOn(logger['console'], 'info').mockImplementation(); + const handler = middy(async (event: { foo: string }, _context: Context) => { + logger.appendKeys({ foo: event.foo }); + }).use(injectLambdaContext(logger, { logEvent: true, clearState: true })); + + await handler({ foo: 'bar' }, {} as Context); + await handler({ foo: 'baz' }, {} as Context); + await handler({ foo: 'biz' }, {} as Context); + await handler({ foo: 'buz' }, {} as Context); + await handler({ foo: 'boz' }, {} as Context); + + expect(consoleSpy).toBeCalledTimes(5); + for (let i = 1; i === 5; i++) { + expect(consoleSpy).toHaveBeenNthCalledWith( + i, + expect.stringContaining('\"message\":\"Lambda invocation event\"'), + ); + expect(consoleSpy).toHaveBeenNthCalledWith( + i, + expect.stringContaining('\"version\":\"1.0.0\"'), + ); + } + expect(consoleSpy).toHaveBeenNthCalledWith( + 2, + expect.not.stringContaining('\"foo\":\"bar\"') + ); + expect(consoleSpy).toHaveBeenNthCalledWith( + 3, + expect.not.stringContaining('\"foo\":\"baz\"') + ); + expect(consoleSpy).toHaveBeenNthCalledWith( + 4, + expect.not.stringContaining('\"foo\":\"biz\"') + ); + expect(consoleSpy).toHaveBeenNthCalledWith( + 5, + expect.not.stringContaining('\"foo\":\"buz\"') + ); + + }); + }); }); From 919853e36b8e156108d18074cf8ac028dc765037 Mon Sep 17 00:00:00 2001 From: Alexander Schueren Date: Mon, 27 Feb 2023 09:35:41 +0100 Subject: [PATCH 43/56] chore(ci): pin 3rd party actions to sha commit (#1335) --- .github/workflows/make-release.yml | 2 +- .github/workflows/measure-packages-size.yml | 2 +- .github/workflows/on-merge-to-main.yml | 2 +- .github/workflows/on-workflows-push-pr.yml | 32 +++++++++++++++++++ .../{publish_layer.yaml => publish_layer.yml} | 2 +- .github/workflows/reusable-publish-docs.yml | 2 +- ...sable-run-linting-check-and-unit-tests.yml | 8 ++--- .../workflows/reusable_deploy_layer_stack.yml | 4 +-- .github/workflows/run-e2e-tests.yml | 6 ++-- 9 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/on-workflows-push-pr.yml rename .github/workflows/{publish_layer.yaml => publish_layer.yml} (98%) diff --git a/.github/workflows/make-release.yml b/.github/workflows/make-release.yml index 812c0e93c4..c5b5a78ee9 100644 --- a/.github/workflows/make-release.yml +++ b/.github/workflows/make-release.yml @@ -30,7 +30,7 @@ jobs: npm set "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" - name: Cache node modules id: cache-node-modules - uses: actions/cache@v3 + uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6 with: path: "./node_modules" # Use the combo between node version, name, and SHA-256 hash of the lock file as cache key so that diff --git a/.github/workflows/measure-packages-size.yml b/.github/workflows/measure-packages-size.yml index 58d6c2df3d..e8b15b3480 100644 --- a/.github/workflows/measure-packages-size.yml +++ b/.github/workflows/measure-packages-size.yml @@ -31,7 +31,7 @@ jobs: with: ref: ${{ steps.extract_PR_details.outputs.headSHA }} - name: Packages size report - uses: flochaz/pkg-size-action@v2.0.0 + uses: flochaz/pkg-size-action@e41584e9396375027c8a3c68909e3eca55719e47 # v.2.0.0 with: build-command: mkdir dist && npm run package -w packages/logger -w packages/tracer -w packages/metrics -w packages/commons -w packages/parameters && npm run package-bundle -w packages/logger -w packages/tracer -w packages/metrics -w packages/commons -w packages/parameters && bash -c "mv ./packages/*/dist/* dist/" && ls dist dist-directory: /dist diff --git a/.github/workflows/on-merge-to-main.yml b/.github/workflows/on-merge-to-main.yml index e062ff447f..71567ad1cb 100644 --- a/.github/workflows/on-merge-to-main.yml +++ b/.github/workflows/on-merge-to-main.yml @@ -28,7 +28,7 @@ jobs: - name: Checkout code uses: actions/checkout@v3 - name: Update release draft - uses: release-drafter/release-drafter@v5.20.0 + uses: release-drafter/release-drafter@569eb7ee3a85817ab916c8f8ff03a5bd96c9c83e # v5.23.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} release_label_on_merge: diff --git a/.github/workflows/on-workflows-push-pr.yml b/.github/workflows/on-workflows-push-pr.yml new file mode 100644 index 0000000000..8664c52c81 --- /dev/null +++ b/.github/workflows/on-workflows-push-pr.yml @@ -0,0 +1,32 @@ +name: Lockdown untrusted workflows + +on: + push: + paths: + - ".github/workflows/**" + pull_request: + paths: + - ".github/workflows/**" + +jobs: + enforce_pinned_workflows: + name: Harden Security + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Ensure 3rd party workflows have SHA pinned + uses: zgosalvez/github-actions-ensure-sha-pinned-actions@b9ddf6a5153efe6fb94f071c8915175afdce60fa # v2.1.0 + with: + # Trusted GitHub Actions and/or organizations + allowlist: | + aws-actions/ + actions/checkout + actions/github-script + actions/setup-node + actions/setup-python + actions/upload-artifact + actions/download-artifact + github/codeql-action/init + github/codeql-action/analyze + dependabot/fetch-metadata \ No newline at end of file diff --git a/.github/workflows/publish_layer.yaml b/.github/workflows/publish_layer.yml similarity index 98% rename from .github/workflows/publish_layer.yaml rename to .github/workflows/publish_layer.yml index 0f3968dba8..ae5c4bc2c0 100644 --- a/.github/workflows/publish_layer.yaml +++ b/.github/workflows/publish_layer.yml @@ -47,7 +47,7 @@ jobs: echo "RELEASE_TAG_VERSION=${RELEASE_TAG_VERSION:1}" >> $GITHUB_ENV - name: Cache node modules id: cache-node-modules - uses: actions/cache@v3 + uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6 with: path: "./node_modules" # Use the combo between node version, name, and SHA-256 hash of the lock file as cache key so that diff --git a/.github/workflows/reusable-publish-docs.yml b/.github/workflows/reusable-publish-docs.yml index 6c1990a0bb..e64d6e92f3 100644 --- a/.github/workflows/reusable-publish-docs.yml +++ b/.github/workflows/reusable-publish-docs.yml @@ -50,7 +50,7 @@ jobs: # if one of them changes the cache is invalidated/discarded - name: Cache node modules id: cache-node-modules - uses: actions/cache@v3 + uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6 with: path: "./node_modules" key: 18-cache-utils-node-modules-${{ hashFiles('./package-lock.json') }} diff --git a/.github/workflows/reusable-run-linting-check-and-unit-tests.yml b/.github/workflows/reusable-run-linting-check-and-unit-tests.yml index c0280cd34e..1d54928957 100644 --- a/.github/workflows/reusable-run-linting-check-and-unit-tests.yml +++ b/.github/workflows/reusable-run-linting-check-and-unit-tests.yml @@ -24,7 +24,7 @@ jobs: run: npm i -g npm@next-8 - name: Cache node modules id: cache-node-modules - uses: actions/cache@v3 + uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6 with: path: "./node_modules" # Use the combo between node version, name, and SHA-256 hash of the lock file as cache key so that @@ -68,7 +68,7 @@ jobs: cache: "npm" - name: Cache node modules id: cache-node-modules - uses: actions/cache@v3 + uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6 with: path: "./examples/${{ matrix.example }}/node_modules" # Use the combo between example, name, and SHA-256 hash of all example lock files as cache key. @@ -98,7 +98,7 @@ jobs: cache: "npm" - name: Cache node modules id: cache-node-modules - uses: actions/cache@v3 + uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6 with: path: "./node_modules" # Use the combo between node version, name, and SHA-256 hash of the lock file as cache key so that @@ -133,7 +133,7 @@ jobs: run: npm i -g npm@next-8 - name: Cache node modules id: cache-node-modules - uses: actions/cache@v3 + uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6 with: path: "./node_modules" # Use the combo between node version, name, and SHA-256 hash of the lock file as cache key so that diff --git a/.github/workflows/reusable_deploy_layer_stack.yml b/.github/workflows/reusable_deploy_layer_stack.yml index 380c3b22ad..865e0a493f 100644 --- a/.github/workflows/reusable_deploy_layer_stack.yml +++ b/.github/workflows/reusable_deploy_layer_stack.yml @@ -62,7 +62,7 @@ jobs: - name: checkout uses: actions/checkout@v3 - name: aws credentials - uses: aws-actions/configure-aws-credentials@v1 + uses: aws-actions/configure-aws-credentials@186395a8644e48f35e7b453e8a7128d9a3948296 with: aws-region: ${{ matrix.region }} role-to-assume: ${{ secrets.target-account-role }} @@ -72,7 +72,7 @@ jobs: node-version: "18" - name: Cache node modules id: cache-node-modules - uses: actions/cache@v3 + uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6 with: path: "./node_modules" # Use the combo between node version, name, and SHA-256 hash of the lock file as cache key so that diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index ff56739b19..dffe8073c7 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -52,7 +52,7 @@ jobs: # See https://github.com/npm/cli/issues/4475 to see why --foreground-scripts run: npm ci --foreground-scripts - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@v1.6.1 + uses: aws-actions/configure-aws-credentials@186395a8644e48f35e7b453e8a7128d9a3948296 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} aws-region: eu-west-1 @@ -98,13 +98,13 @@ jobs: - name: Setup npm run: npm i -g npm@next-8 - name: "Configure AWS credentials" - uses: aws-actions/configure-aws-credentials@v1.6.1 + uses: aws-actions/configure-aws-credentials@186395a8644e48f35e7b453e8a7128d9a3948296 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} aws-region: eu-west-1 - name: Cache node modules for commons id: cache-node-modules - uses: actions/cache@v3 + uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6 with: path: "./node_modules" # Use the combo between node version, name, and SHA-256 hash of the lock file as cache key so that From 5af51d319dee68d7a7ba832721580d7a6e655249 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 27 Feb 2023 10:56:51 +0100 Subject: [PATCH 44/56] feat(logger): make loglevel types stricter (#1313) * feat: make loglevel types stricter * test: added unit test * refactor: logLevel types --- packages/logger/src/Logger.ts | 53 +++++++++++-------- packages/logger/src/types/Log.ts | 4 +- packages/logger/tests/unit/Logger.test.ts | 2 +- .../formatter/PowertoolLogFormatter.test.ts | 2 +- packages/logger/tests/unit/helpers.test.ts | 35 ++++++++++-- 5 files changed, 68 insertions(+), 28 deletions(-) diff --git a/packages/logger/src/Logger.ts b/packages/logger/src/Logger.ts index 2ce5df7059..663ce2bd37 100644 --- a/packages/logger/src/Logger.ts +++ b/packages/logger/src/Logger.ts @@ -117,7 +117,7 @@ class Logger extends Utility implements ClassThatLogs { private customConfigService?: ConfigServiceInterface; - private static readonly defaultLogLevel: LogLevel = 'INFO'; + private static readonly defaultLogLevel: Uppercase = 'INFO'; // envVarsService is always initialized in the constructor in setOptions() private envVarsService!: EnvironmentVariablesService; @@ -128,7 +128,7 @@ class Logger extends Utility implements ClassThatLogs { private logIndentation: number = LogJsonIndent.COMPACT; - private logLevel?: LogLevel; + private logLevel?: Uppercase; private readonly logLevelThresholds: LogLevelThresholds = { DEBUG: 8, @@ -554,12 +554,16 @@ class Logger extends Utility implements ClassThatLogs { /** * It returns the log level set for the Logger instance. + * + * Even though logLevel starts as undefined, it will always be set to a value + * during the Logger instance's initialization. So, we can safely use the non-null + * assertion operator here. * * @private * @returns {LogLevel} */ - private getLogLevel(): LogLevel { - return this.logLevel; + private getLogLevel(): Uppercase { + return this.logLevel!; } /** @@ -619,14 +623,14 @@ class Logger extends Utility implements ClassThatLogs { } /** - * It returns true if the provided log level is valid. + * It returns true and type guards the log level if a given log level is valid. * * @param {LogLevel} logLevel * @private * @returns {boolean} */ - private isValidLogLevel(logLevel?: LogLevel): boolean { - return typeof logLevel === 'string' && logLevel.toUpperCase() in this.logLevelThresholds; + private isValidLogLevel(logLevel?: LogLevel | string): logLevel is Uppercase { + return typeof logLevel === 'string' && logLevel in this.logLevelThresholds; } /** @@ -647,11 +651,12 @@ class Logger extends Utility implements ClassThatLogs { /** * It prints a given log with given log level. * - * @param {LogLevel} logLevel - * @param {LogItem} log + * @param {Uppercase} logLevel + * @param {LogItemMessage} input + * @param {LogItemExtraInput} extraInput * @private */ - private processLogItem(logLevel: LogLevel, input: LogItemMessage, extraInput: LogItemExtraInput): void { + private processLogItem(logLevel: Uppercase, input: LogItemMessage, extraInput: LogItemExtraInput): void { if (!this.shouldPrint(logLevel)) { return; } @@ -743,20 +748,25 @@ class Logger extends Utility implements ClassThatLogs { * @returns {void} */ private setLogLevel(logLevel?: LogLevel): void { - if (this.isValidLogLevel(logLevel)) { - this.logLevel = (logLevel).toUpperCase(); + const constructorLogLevel = logLevel?.toUpperCase(); + if (this.isValidLogLevel(constructorLogLevel)) { + this.logLevel = constructorLogLevel; return; } - const customConfigValue = this.getCustomConfigService()?.getLogLevel(); + const customConfigValue = this.getCustomConfigService() + ?.getLogLevel() + ?.toUpperCase(); if (this.isValidLogLevel(customConfigValue)) { - this.logLevel = (customConfigValue).toUpperCase(); + this.logLevel = customConfigValue; return; } - const envVarsValue = this.getEnvVarsService().getLogLevel(); + const envVarsValue = this.getEnvVarsService() + ?.getLogLevel() + ?.toUpperCase(); if (this.isValidLogLevel(envVarsValue)) { - this.logLevel = (envVarsValue).toUpperCase(); + this.logLevel = envVarsValue; return; } @@ -845,14 +855,15 @@ class Logger extends Utility implements ClassThatLogs { /** * It checks whether the current log item should/can be printed. * - * @param {string} serviceName - * @param {Environment} environment - * @param {LogAttributes} persistentLogAttributes + * @param {Uppercase} logLevel * @private * @returns {boolean} */ - private shouldPrint(logLevel: LogLevel): boolean { - if (this.logLevelThresholds[logLevel] >= this.logLevelThresholds[this.getLogLevel()]) { + private shouldPrint(logLevel: Uppercase): boolean { + if ( + this.logLevelThresholds[logLevel] >= + this.logLevelThresholds[this.getLogLevel()] + ) { return true; } diff --git a/packages/logger/src/types/Log.ts b/packages/logger/src/types/Log.ts index 0b7cd5d30f..020b9cf200 100644 --- a/packages/logger/src/types/Log.ts +++ b/packages/logger/src/types/Log.ts @@ -3,10 +3,10 @@ type LogLevelInfo = 'INFO'; type LogLevelWarn = 'WARN'; type LogLevelError = 'ERROR'; -type LogLevel = LogLevelDebug | LogLevelInfo | LogLevelWarn | LogLevelError | string; +type LogLevel = LogLevelDebug | Lowercase | LogLevelInfo | Lowercase | LogLevelWarn | Lowercase | LogLevelError | Lowercase; type LogLevelThresholds = { - [key in LogLevel]: number; + [key in Uppercase]: number; }; type LogAttributeValue = unknown; diff --git a/packages/logger/tests/unit/Logger.test.ts b/packages/logger/tests/unit/Logger.test.ts index 5c2b3fd95e..eedb348c81 100644 --- a/packages/logger/tests/unit/Logger.test.ts +++ b/packages/logger/tests/unit/Logger.test.ts @@ -1486,7 +1486,7 @@ describe('Class: Logger', () => { }; const childLoggerWithSampleRateEnabled = parentLogger.createChild(optionsWithSampleRateEnabled); - const optionsWithErrorLogLevel = { + const optionsWithErrorLogLevel: ConstructorOptions = { logLevel: 'ERROR', }; const childLoggerWithErrorLogLevel = parentLogger.createChild(optionsWithErrorLogLevel); diff --git a/packages/logger/tests/unit/formatter/PowertoolLogFormatter.test.ts b/packages/logger/tests/unit/formatter/PowertoolLogFormatter.test.ts index 4d8d4100f5..1aff6d111a 100644 --- a/packages/logger/tests/unit/formatter/PowertoolLogFormatter.test.ts +++ b/packages/logger/tests/unit/formatter/PowertoolLogFormatter.test.ts @@ -22,7 +22,7 @@ describe('Class: PowertoolLogFormatter', () => { // Prepare const formatter = new PowertoolLogFormatter(); - const unformattedAttributes = { + const unformattedAttributes: UnformattedAttributes = { sampleRateValue: undefined, awsRegion: 'eu-west-1', environment: '', diff --git a/packages/logger/tests/unit/helpers.test.ts b/packages/logger/tests/unit/helpers.test.ts index 7235dec4ab..31842614fb 100644 --- a/packages/logger/tests/unit/helpers.test.ts +++ b/packages/logger/tests/unit/helpers.test.ts @@ -55,7 +55,7 @@ describe('Helper: createLogger function', () => { test('when no parameters are set, returns a Logger instance with the correct properties', () => { // Prepare - const loggerOptions = { + const loggerOptions: ConstructorOptions = { logLevel: 'WARN', serviceName: 'my-lambda-service', sampleRateValue: 1, @@ -199,10 +199,10 @@ describe('Helper: createLogger function', () => { }); - test('when a custom logLevel is passed, returns a Logger instance with the correct properties', () => { + test('when a custom uppercase logLevel is passed, returns a Logger instance with the correct properties', () => { // Prepare - const loggerOptions:ConstructorOptions = { + const loggerOptions: ConstructorOptions = { logLevel: 'ERROR', }; @@ -227,6 +227,35 @@ describe('Helper: createLogger function', () => { })); }); + + test('when a custom lowercase logLevel is passed, returns a Logger instance with the correct properties', () => { + + // Prepare + const loggerOptions: ConstructorOptions = { + logLevel: 'warn', + }; + + // Act + const logger = createLogger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual(expect.objectContaining({ + logsSampled: false, + persistentLogAttributes: {}, + powertoolLogData: { + sampleRateValue: undefined, + awsRegion: 'eu-west-1', + environment: '', + serviceName: 'hello-world', + }, + envVarsService: expect.any(EnvironmentVariablesService), + customConfigService: undefined, + logLevel: 'WARN', + logFormatter: expect.any(PowertoolLogFormatter), + })); + + }); test('when no log level is set, returns a Logger instance with INFO level', () => { From 487298fb0bfb8221761eb1c6383157d4a40e8811 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 27 Feb 2023 12:40:44 +0100 Subject: [PATCH 45/56] chore(ci): revisit stale issues automation (#1341) * chore: updated stale-issues workflow * chore: removed stalebot config * chore: added action to allowlist * docs: added note to Maintainers doc * docs: fixed wording in comments * docs: fixed wording in comments --- .github/stale.yml | 16 ------ .github/workflows/on-workflows-push-pr.yml | 1 + .github/workflows/stale-issues.yml | 59 +++++++++------------- MAINTAINERS.md | 2 + 4 files changed, 27 insertions(+), 51 deletions(-) delete mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index d6bdd4c29c..0000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,16 +0,0 @@ -only: issues -daysUntilStale: 30 -daysUntilClose: 7 -exemptLabels: - - type/bug - - type/bug-upstream - - type/feature-request - - type/RFC -staleLabel: pending-close-response-required -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. -closeComment: > - This issue has been automatically closed because of inactivity. - Please open a new issue if you are still encountering problems. diff --git a/.github/workflows/on-workflows-push-pr.yml b/.github/workflows/on-workflows-push-pr.yml index 8664c52c81..5b272f9357 100644 --- a/.github/workflows/on-workflows-push-pr.yml +++ b/.github/workflows/on-workflows-push-pr.yml @@ -21,6 +21,7 @@ jobs: # Trusted GitHub Actions and/or organizations allowlist: | aws-actions/ + actions/stale actions/checkout actions/github-script actions/setup-node diff --git a/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml index 15b5dff177..f3ea84ee3c 100644 --- a/.github/workflows/stale-issues.yml +++ b/.github/workflows/stale-issues.yml @@ -1,46 +1,35 @@ name: "Close stale issues" -# Controls when the action will run. - on: schedule: - cron: "0 0 * * *" jobs: - cleanup: + check-issues: runs-on: ubuntu-latest - name: Stale issue job + permissions: + issues: write steps: - - uses: aws-actions/stale-issue-cleanup@e1cf8a5d54b4ff316c76f5356079d3a8df84137e + - uses: actions/stale@v7 with: - # Setting messages to an empty string will cause the automation to skip - # that category - ancient-issue-message: Greetings! We’re closing this issue because it has been open a long time and hasn’t been updated in a while and may not be getting the attention it deserves. We encourage you to check if this is still an issue in the latest release and if you find that this is still a problem, please feel free to comment or open a new issue. - stale-issue-message: This issue has not received a response in 2 weeks. If you still think there is a problem, please leave a comment to avoid the issue from automatically closing. - stale-pr-message: This PR has not received a response in 2 weeks. If you still think there is a problem, please leave a comment to avoid the PR from automatically closing. - # These labels are required - stale-issue-label: closing-soon - exempt-issue-label: no-autoclose - stale-pr-label: closing-soon - exempt-pr-label: pr/needs-review - response-requested-label: response-requested - - # Don't set closed-for-staleness label to skip closing very old issues - # regardless of label - closed-for-staleness-label: closed-for-staleness - - # Issue timing - days-before-stale: 14 - days-before-close: 7 - days-before-ancient: 365 - - # If you don't want to mark a issue as being ancient based on a - # threshold of "upvotes", you can set this here. An "upvote" is - # the total number of +1, heart, hooray, and rocket reactions - # on an issue. - minimum-upvotes-to-exempt: 5 - repo-token: ${{ secrets.GITHUB_TOKEN }} - loglevel: DEBUG - # Set dry-run to true to not perform label or close actions. - # dry-run: true + stale-issue-message: "This issue has not received a response in 2 weeks. If you still think there is a problem, please leave a comment to avoid the issue from automatically closing." + close-issue-message: "Greetings! We are closing this issue because it has been open a long time and hasn’t been updated in a while and may not be getting the attention it deserves. We encourage you to check if this is still an issue in the latest release and if you find that this is still a problem, please feel free to comment or reopen the issue." + # Label applied or removed when an issue becomes stale + stale-issue-label: status/pending-close-response-required + remove-stale-when-updated: true + # Label and close type when a stale issue is finally closed + close-issue-label: status/rejected + close-issue-reason: not_planned + # Exempt any issue that hasn't been triaged yet, or that is clearly labeled + exempt-issue-labels: triage,status/confirmed,status/blocked,status/on-hold,status/completed + # Include only issues where any of these labels are present (aka only issues that need more info from the customer) + # that are either under discussion, but no info/answer has been provided in 2 weeks, or that were already marked + # as stale + any-of-issue-labels: need-more-information,status/discussing,status/pending-close-response-required + # Settings specific to issues + days-before-issue-stale: 14 + days-before-issue-close: 7 + # Set to ignore PRs + days-before-pr-stale: -1 + days-before-pr-close: -1 \ No newline at end of file diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 6792654c1a..c7d31119bc 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -268,6 +268,8 @@ In other cases, you may have constrained capacity. Use `help=wanted` label when When in doubt, use `need-more-information` or `need-customer-feedback` labels to signal more context and feedback are necessary before proceeding. You can also use `status/on-hold` label when you expect it might take a while to gather enough information before you can decide. +Note that issues marked as `need-more-information` and `status/discussing` will be automatically closed after 3 weeks of inactivity. + ### Crediting contributions We credit all contributions as part of each [release note](https://github.com/awslabs/aws-lambda-powertools-typescript/releases) as an automated process. If you find contributors are missing from the release note you're producing, please add them manually. From c99714f31c48ebd169a6b6376b2a66b47e8f6c04 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 27 Feb 2023 18:26:19 +0100 Subject: [PATCH 46/56] tests(logger): revisited integration tests (#1342) * tests: basicFeatures * tests: logEvent * tests: childLogger * tests: sampleRate --- .../commons/tests/utils/InvocationLogs.ts | 26 +- .../basicFeatures.middy.test.FunctionCode.ts | 67 ++-- .../tests/e2e/basicFeatures.middy.test.ts | 327 +++++++++++------- .../childLogger.manual.test.FunctionCode.ts | 18 +- .../tests/e2e/childLogger.manual.test.ts | 115 +++--- packages/logger/tests/e2e/constants.ts | 3 +- ...ntEnvVarSetting.middy.test.FunctionCode.ts | 10 +- .../e2e/logEventEnvVarSetting.middy.test.ts | 47 ++- .../sampleRate.decorator.test.FunctionCode.ts | 5 +- .../tests/e2e/sampleRate.decorator.test.ts | 54 +-- packages/logger/tests/helpers/types.ts | 5 + 11 files changed, 403 insertions(+), 274 deletions(-) create mode 100644 packages/logger/tests/helpers/types.ts diff --git a/packages/commons/tests/utils/InvocationLogs.ts b/packages/commons/tests/utils/InvocationLogs.ts index 730e6f8e87..fd2b9f3e4c 100644 --- a/packages/commons/tests/utils/InvocationLogs.ts +++ b/packages/commons/tests/utils/InvocationLogs.ts @@ -69,28 +69,30 @@ export class InvocationLogs { * @returns {number} index of the log that contains END RequestId */ public static getEndLogIndex(logs: string[]): number { - return logs.findIndex((log) => log.includes('END RequestId')); + return logs.findIndex((log) => log.startsWith('END RequestId')); } /** - * Return only logs from function, exclude START, END, REPORT, and XRay log generated by Lambda service - * @param levelToFilter level to filter - * @returns Array of function logs + * Return only logs from function, exclude START, END, REPORT, + * and X-Ray log generated by the Lambda service. + * + * @param {LEVEL} [levelToFilter] - Level to filter the logs + * @returns Array of function logs, filtered by level if provided */ public getFunctionLogs(levelToFilter?: LEVEL): string[] { + const startLogIndex = InvocationLogs.getStartLogIndex(this.logs); const endLogIndex = InvocationLogs.getEndLogIndex(this.logs); - let filteredLogs = this.logs.slice(1, endLogIndex); + let filteredLogs = this.logs.slice(startLogIndex + 1, endLogIndex); if (levelToFilter) { filteredLogs = filteredLogs.filter((log) => { try { - const parsedLog = JSON.parse(log); - if (parsedLog.level == levelToFilter) return parsedLog; - else return; + const parsedLog = InvocationLogs.parseFunctionLog(log); + + return parsedLog.level == levelToFilter; } catch (error) { // If log is not from structured logging : such as metrics one. - if (log.split('\t')[2] == levelToFilter) return log; - else return; + return log.split('\t')[2] == levelToFilter; } }); } @@ -98,6 +100,10 @@ export class InvocationLogs { return filteredLogs; } + public static getStartLogIndex(logs: string[]): number { + return logs.findIndex((log) => log.startsWith('START RequestId')); + } + /** * Each of log message contains a JSON with the structured Log object (e.g. {\"cold_start\":true, ..}) * @param log diff --git a/packages/logger/tests/e2e/basicFeatures.middy.test.FunctionCode.ts b/packages/logger/tests/e2e/basicFeatures.middy.test.FunctionCode.ts index c434a7892a..116424e395 100644 --- a/packages/logger/tests/e2e/basicFeatures.middy.test.FunctionCode.ts +++ b/packages/logger/tests/e2e/basicFeatures.middy.test.FunctionCode.ts @@ -1,73 +1,66 @@ import { injectLambdaContext, Logger } from '../../src'; import { Context, APIGatewayAuthorizerResult } from 'aws-lambda'; +import { TestEvent, TestOutput } from '../helpers/types'; import middy from '@middy/core'; -const PERSISTENT_KEY = process.env.PERSISTENT_KEY; -const PERSISTENT_KEY_FIRST_INVOCATION_ONLY = process.env.PERSISTENT_KEY_FIRST_INVOCATION_ONLY; -const PERSISTENT_VALUE = process.env.PERSISTENT_VALUE; -const REMOVABLE_KEY = process.env.REMOVABLE_KEY; -const REMOVABLE_VALUE = process.env.REMOVABLE_VALUE; +const PERSISTENT_KEY = process.env.PERSISTENT_KEY || 'persistentKey'; +const PERSISTENT_VALUE = process.env.PERSISTENT_VALUE || 'persistentValue'; +const REMOVABLE_KEY = process.env.REMOVABLE_KEY || 'removableKey'; +const REMOVABLE_VALUE = process.env.REMOVABLE_VALUE || 'remvovableValue'; const ERROR_MSG = process.env.ERROR_MSG || 'error'; -const SINGLE_LOG_ITEM_KEY = process.env.SINGLE_LOG_ITEM_KEY; -const SINGLE_LOG_ITEM_VALUE = process.env.SINGLE_LOG_ITEM_VALUE; -const ARBITRARY_OBJECT_KEY = process.env.ARBITRARY_OBJECT_KEY; -const ARBITRARY_OBJECT_DATA = process.env.ARBITRARY_OBJECT_DATA; - -type LambdaEvent = { - invocation: number -}; +const RUNTIME_ADDED_KEY = process.env.RUNTIME_ADDED_KEY || 'runtimeAddedKey'; +const SINGLE_LOG_ITEM_KEY = process.env.SINGLE_LOG_ITEM_KEY || 'keyForSingleLogItem'; +const SINGLE_LOG_ITEM_VALUE = process.env.SINGLE_LOG_ITEM_VALUE || 'valueForSingleLogItem'; +const ARBITRARY_OBJECT_KEY = process.env.ARBITRARY_OBJECT_KEY || 'keyForArbitraryObject'; +const ARBITRARY_OBJECT_DATA = process.env.ARBITRARY_OBJECT_DATA || 'arbitrary object data'; const logger = new Logger({ persistentLogAttributes: { - [PERSISTENT_KEY]: PERSISTENT_VALUE, - [REMOVABLE_KEY]: REMOVABLE_VALUE, + [PERSISTENT_KEY]: PERSISTENT_VALUE, // This key-value pair will be added to every log + [REMOVABLE_KEY]: REMOVABLE_VALUE, // This other one will be removed at runtime and not displayed in any log }, }); -const testFunction = async (event: LambdaEvent, context: Context): Promise<{requestId: string}> => { - // Test feature 1: Log level filtering - // Test feature 2: Context data - // Test feature 3: Add and remove persistent additional log keys and value - // Test feature 4: X-Ray Trace ID injection - logger.removeKeys([REMOVABLE_KEY]); - - const specialValue = event.invocation; - if (specialValue === 0) { - logger.appendKeys({ - [PERSISTENT_KEY_FIRST_INVOCATION_ONLY]: specialValue - }); - } - +const testFunction = async (event: TestEvent, context: Context): TestOutput => { + // Test feature 1: Context data injection (all logs should have the same context data) + // Test feature 2: Event log (this log should have the event data) + // Test feature 3: Log level filtering (log level is set to INFO) logger.debug('##### This should not appear'); - logger.info('This is an INFO log with context and persistent key'); + + // Test feature 4: Add and remove persistent additional log keys and value + logger.removeKeys([REMOVABLE_KEY]); // This key should not appear in any log (except the event log) + logger.appendKeys({ // This key-value pair should appear in every log (except the event log) + [RUNTIME_ADDED_KEY]: event.invocation, + }); // Test feature 5: One-time additional log keys and values logger.info('This is an one-time log with an additional key-value', { [SINGLE_LOG_ITEM_KEY]: SINGLE_LOG_ITEM_VALUE, }); - // Test feature 6: Logging an error object + // Test feature 6: Error logging try { throw new Error(ERROR_MSG); } catch (e) { logger.error(ERROR_MSG, e as Error); } - // Test feature 7: Logging an arbitrary object + // Test feature 7: Arbitrary object logging const obj: APIGatewayAuthorizerResult = { principalId: ARBITRARY_OBJECT_DATA, policyDocument: { - Version: 'Version' + ARBITRARY_OBJECT_DATA, + Version: 'Version 1', Statement: [{ - Effect: 'Effect' + ARBITRARY_OBJECT_DATA, - Action: 'Action' + ARBITRARY_OBJECT_DATA, - Resource: 'Resource' + ARBITRARY_OBJECT_DATA + Effect: 'Allow', + Action: 'geo:*', + Resource: '*', }] } }; - logger.info('A log entry with an object', { [ARBITRARY_OBJECT_KEY]: obj }); + // Test feature 8: X-Ray Trace ID injection (all logs should have the same X-Ray Trace ID) + return { requestId: context.awsRequestId, }; diff --git a/packages/logger/tests/e2e/basicFeatures.middy.test.ts b/packages/logger/tests/e2e/basicFeatures.middy.test.ts index d12583b84a..04757def04 100644 --- a/packages/logger/tests/e2e/basicFeatures.middy.test.ts +++ b/packages/logger/tests/e2e/basicFeatures.middy.test.ts @@ -1,12 +1,8 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT-0 - /** * Test logger basic features * * @group e2e/logger/basicFeatures */ - import path from 'path'; import { App, Stack } from 'aws-cdk-lib'; import { APIGatewayAuthorizerResult } from 'aws-lambda'; @@ -24,7 +20,8 @@ import { STACK_OUTPUT_LOG_GROUP, SETUP_TIMEOUT, TEST_CASE_TIMEOUT, - TEARDOWN_TIMEOUT + TEARDOWN_TIMEOUT, + XRAY_TRACE_ID_REGEX, } from './constants'; const runtime: string = process.env.RUNTIME || 'nodejs18x'; @@ -40,17 +37,19 @@ const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'Basic const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'BasicFeatures-Middy'); const lambdaFunctionCodeFile = 'basicFeatures.middy.test.FunctionCode.ts'; +const invocationCount = 3; + // Text to be used by Logger in the Lambda function const PERSISTENT_KEY = 'persistentKey'; -const PERSISTENT_KEY_FIRST_INVOCATION_ONLY = 'specialKey'; -const PERSISTENT_VALUE = `a persistent value that will be put in every log ${uuid}`; +const RUNTIME_ADDED_KEY = 'invocation'; +const PERSISTENT_VALUE = uuid; const REMOVABLE_KEY = 'removableKey'; -const REMOVABLE_VALUE = `a persistent value that will be removed and not displayed in any log ${uuid}`; -const SINGLE_LOG_ITEM_KEY = `keyForSingleLogItem${uuid}`; -const SINGLE_LOG_ITEM_VALUE = `a value for a single log item${uuid}`; -const ERROR_MSG = `error-${uuid}`; -const ARBITRARY_OBJECT_KEY = `keyForArbitraryObject${uuid}`; -const ARBITRARY_OBJECT_DATA = `arbitrary object data ${uuid}`; +const REMOVABLE_VALUE = 'removedValue'; +const SINGLE_LOG_ITEM_KEY = 'singleKey'; +const SINGLE_LOG_ITEM_VALUE = 'singleValue'; +const ERROR_MSG = 'error'; +const ARBITRARY_OBJECT_KEY = 'arbitraryObjectKey'; +const ARBITRARY_OBJECT_DATA = 'arbitraryObjectData'; const integTestApp = new App(); let logGroupName: string; // We do not know it until deployment @@ -74,8 +73,8 @@ describe(`logger E2E tests basic functionalities (middy) for runtime: ${runtime} // Text to be used by Logger in the Lambda function PERSISTENT_KEY, - PERSISTENT_KEY_FIRST_INVOCATION_ONLY, PERSISTENT_VALUE, + RUNTIME_ADDED_KEY, REMOVABLE_KEY, REMOVABLE_VALUE, SINGLE_LOG_ITEM_KEY, @@ -91,171 +90,261 @@ describe(`logger E2E tests basic functionalities (middy) for runtime: ${runtime} const result = await deployStack(integTestApp, stack); logGroupName = result.outputs[STACK_OUTPUT_LOG_GROUP]; - // Invoke the function twice (one for cold start, another for warm start) - invocationLogs = await invokeFunction(functionName, 2, 'SEQUENTIAL'); + // Invoke the function three time (one for cold start, then two for warm start) + invocationLogs = await invokeFunction(functionName, invocationCount, 'SEQUENTIAL'); console.log('logGroupName', logGroupName); }, SETUP_TIMEOUT); describe('Log level filtering', () => { + it('should filter log based on LOG_LEVEL (INFO) environment variable in Lambda', async () => { - const debugLogs = invocationLogs[0].getFunctionLogs(LEVEL.DEBUG); - expect(debugLogs.length).toBe(0); + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation and filter by level + const debugLogs = invocationLogs[i].getFunctionLogs(LEVEL.DEBUG); + // Check that no log message below INFO level is logged + expect(debugLogs.length).toBe(0); + } + }, TEST_CASE_TIMEOUT); }); describe('Context data', () => { - it('should log context information of the function', async () => { - const logMessages = invocationLogs[0].getFunctionLogs(); - - for (const message of logMessages) { - expect(message).toContain('function_arn'); - expect(message).toContain('function_memory_size'); - expect(message).toContain('function_name'); - expect(message).toContain('function_request_id'); - expect(message).toContain('timestamp'); - } - }, TEST_CASE_TIMEOUT); - it('should include cold start equal to TRUE only on the first invocation', async () => { - const coldStartLogMessages = invocationLogs[0].getFunctionLogs(LEVEL.INFO); - for (const message of coldStartLogMessages) { - expect(message).toContain(`"cold_start":true`); + it('should inject context info in each log', async () => { + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + // Check that the context is logged on every log + for (const message of logMessages) { + const log = InvocationLogs.parseFunctionLog(message); + expect(log).toHaveProperty('function_arn'); + expect(log).toHaveProperty('function_memory_size'); + expect(log).toHaveProperty('function_name'); + expect(log).toHaveProperty('function_request_id'); + expect(log).toHaveProperty('timestamp'); + } } - const normalLogMessages = invocationLogs[1].getFunctionLogs(LEVEL.INFO); - for (const message of normalLogMessages) { - expect(message).not.toContain(`"cold_start":true`); - } }, TEST_CASE_TIMEOUT); - it('should log context information in every log', async () => { - const logMessages = invocationLogs[0].getFunctionLogs(); - - for (const message of logMessages) { - expect(message).toContain('function_arn'); - expect(message).toContain('function_memory_size'); - expect(message).toContain('function_name'); - expect(message).toContain('function_request_id'); - expect(message).toContain('timestamp'); + it('should include coldStart equal to TRUE only on the first invocation, FALSE otherwise', async () => { + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + // Check that cold start is logged correctly on every log + for (const message of logMessages) { + const log = InvocationLogs.parseFunctionLog(message); + if (i === 0) { + expect(log.cold_start).toBe(true); + } else { + expect(log.cold_start).toBe(false); + } + } } + }, TEST_CASE_TIMEOUT); + }); describe('Log event', () => { - it('should log the event on the first invocation', async () => { - const firstInvocationMessages = invocationLogs[0].getAllFunctionLogs(); - let eventLoggedInFirstInvocation = false; - for (const message of firstInvocationMessages) { - if (message.includes(`event`)) { - eventLoggedInFirstInvocation = true; - expect(message).toContain(`"event":{"invocation":0}`); + it('should log the event as the first log of each invocation only', async () => { + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + + for (const [ index, message ] of logMessages.entries()) { + const log = InvocationLogs.parseFunctionLog(message); + // Check that the event is logged on the first log + if (index === 0) { + expect(log).toHaveProperty('event'); + expect(log.event).toStrictEqual( + expect.objectContaining({ invocation: i }) + ); + // Check that the event is not logged again on the rest of the logs + } else { + expect(log).not.toHaveProperty('event'); + } } } - const secondInvocationMessages = invocationLogs[1].getAllFunctionLogs(); - let eventLoggedInSecondInvocation = false; - for (const message of secondInvocationMessages) { - if (message.includes(`event`)) { - eventLoggedInSecondInvocation = true; - expect(message).toContain(`"event":{"invocation":1}`); - } - } - - expect(eventLoggedInFirstInvocation).toBe(true); - expect(eventLoggedInSecondInvocation).toBe(true); - }, TEST_CASE_TIMEOUT); }); describe('Persistent additional log keys and values', () => { + it('should contain persistent value in every log', async () => { - const logMessages = invocationLogs[0].getFunctionLogs(); - for (const message of logMessages) { - expect(message).toContain(`"${PERSISTENT_KEY}":"${PERSISTENT_VALUE}"`); + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + + for (const message of logMessages) { + const log = InvocationLogs.parseFunctionLog(message); + // Check that the persistent key is present in every log + expect(log).toHaveProperty(PERSISTENT_KEY); + expect(log[PERSISTENT_KEY]).toBe(PERSISTENT_VALUE); + } } + }, TEST_CASE_TIMEOUT); it('should not contain persistent keys that were removed on runtime', async () => { - const logMessages = invocationLogs[0].getFunctionLogs(); - for (const message of logMessages) { - expect(message).not.toContain(`"${REMOVABLE_KEY}":"${REMOVABLE_VALUE}"`); + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + + for (const [ index, message ] of logMessages.entries()) { + const log = InvocationLogs.parseFunctionLog(message); + // Check that at the time of logging the event, which happens before the handler, + // the key was still present + if (index === 0) { + expect(log).toHaveProperty(REMOVABLE_KEY); + expect(log[REMOVABLE_KEY]).toBe(REMOVABLE_VALUE); + // Check that all other logs that happen at runtime do not contain the key + } else { + expect(log).not.toHaveProperty(REMOVABLE_KEY); + } + } } + }, TEST_CASE_TIMEOUT); - it('with clear state enabled, should not persist keys across invocations', async () => { - const firstInvocationMessages = invocationLogs[0].getFunctionLogs(); - for (const message of firstInvocationMessages) { - expect(message).toContain(`"${PERSISTENT_KEY_FIRST_INVOCATION_ONLY}":0`); + it('should not leak any persistent keys added runtime since clearState is enabled', async () => { + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + + for (const [ index, message ] of logMessages.entries()) { + const log = InvocationLogs.parseFunctionLog(message); + // Check that at the time of logging the event, which happens before the handler, + // the key is NOT present + if (index === 0) { + expect(log).not.toHaveProperty(RUNTIME_ADDED_KEY); + } else { + // Check that all other logs that happen at runtime do contain the key + expect(log).toHaveProperty(RUNTIME_ADDED_KEY); + // Check that the value is the same for all logs (it should be the index of the invocation) + expect(log[RUNTIME_ADDED_KEY]).toEqual(i); + } + } } - const secondInvocationMessages = invocationLogs[1].getFunctionLogs(); - for (const message of secondInvocationMessages) { - expect(message).not.toContain(`"${PERSISTENT_KEY_FIRST_INVOCATION_ONLY}":0`); - } }, TEST_CASE_TIMEOUT); }); - describe('X-Ray Trace ID injection', () => { - it('should inject & parse X-Ray Trace ID into every log', async () => { - const logMessages = invocationLogs[0].getFunctionLogs(); + describe('One-time additional log keys and values', () => { + + it('should log additional keys and value only once', async () => { - for (const message of logMessages) { - expect(message).toContain('xray_trace_id'); + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + // Check that the additional log is logged only once + const logMessagesWithAdditionalLog = logMessages.filter( + log => log.includes(SINGLE_LOG_ITEM_KEY) + ); + expect(logMessagesWithAdditionalLog).toHaveLength(1); + // Check that the additional log is logged correctly + const parsedLog = InvocationLogs + .parseFunctionLog(logMessagesWithAdditionalLog[0]); + expect(parsedLog[SINGLE_LOG_ITEM_KEY]).toBe(SINGLE_LOG_ITEM_VALUE); } + }, TEST_CASE_TIMEOUT); + }); - describe('One-time additional log keys and values', () => { - it('should log additional keys and value only once', async () => { - const logMessages = invocationLogs[0].getFunctionLogs() - .filter(message => message.includes(`"${SINGLE_LOG_ITEM_KEY}":"${SINGLE_LOG_ITEM_VALUE}"`)); + describe('Error logging', () => { + + it('should log error only once', async () => { + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation filtered by error level + const logMessages = invocationLogs[i].getFunctionLogs(LEVEL.ERROR); + + // Check that the error is logged only once + expect(logMessages).toHaveLength(1); + + // Check that the error is logged correctly + const errorLog = InvocationLogs.parseFunctionLog(logMessages[0]); + expect(errorLog).toHaveProperty('error'); + expect(errorLog.error).toStrictEqual( + expect.objectContaining({ + location: expect.any(String), + name: 'Error', + message: ERROR_MSG, + stack: expect.anything() + }) + ); + + } - expect(logMessages).toHaveLength(1); }, TEST_CASE_TIMEOUT); + }); + + describe('Arbitrary object logging', () => { + it('should log additional arbitrary object only once', async () => { - const logMessages = invocationLogs[0].getFunctionLogs() - .filter(message => message.includes(ARBITRARY_OBJECT_DATA)); - - expect(logMessages).toHaveLength(1); - - const logObject = InvocationLogs.parseFunctionLog(logMessages[0]); - expect(logObject).toHaveProperty(ARBITRARY_OBJECT_KEY); - const arbitrary = logObject[ARBITRARY_OBJECT_KEY] as APIGatewayAuthorizerResult; - expect(arbitrary.principalId).toBe(ARBITRARY_OBJECT_DATA); - expect(arbitrary.policyDocument).toEqual(expect.objectContaining( - { - Version: 'Version' + ARBITRARY_OBJECT_DATA, - Statement: [{ - Effect: 'Effect' + ARBITRARY_OBJECT_DATA, - Action: 'Action' + ARBITRARY_OBJECT_DATA, - Resource: 'Resource' + ARBITRARY_OBJECT_DATA - }] - } - )); + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + // Get the log messages that contains the arbitrary object + const filteredLogs = logMessages + .filter(log => log.includes(ARBITRARY_OBJECT_DATA)); + // Check that the arbitrary object is logged only once + expect(filteredLogs).toHaveLength(1); + const logObject = InvocationLogs.parseFunctionLog(filteredLogs[0]); + // Check that the arbitrary object is logged correctly + expect(logObject).toHaveProperty(ARBITRARY_OBJECT_KEY); + const arbitrary = logObject[ARBITRARY_OBJECT_KEY] as APIGatewayAuthorizerResult; + expect(arbitrary.principalId).toBe(ARBITRARY_OBJECT_DATA); + expect(arbitrary.policyDocument).toEqual(expect.objectContaining( + { + Version: 'Version 1', + Statement: [{ + Effect: 'Allow', + Action: 'geo:*', + Resource: '*' + }] + } + )); + } + }, TEST_CASE_TIMEOUT); }); - describe('Logging an error object', () => { - it('should log error only once', async () => { - const logMessages = invocationLogs[0].getFunctionLogs(LEVEL.ERROR) - .filter(message => message.includes(ERROR_MSG)); - - expect(logMessages).toHaveLength(1); + describe('X-Ray Trace ID injection', () => { - const logObject = InvocationLogs.parseFunctionLog(logMessages[0]); - const errorObj = logObject.error; + it('should inject & parse the X-Ray Trace ID of the current invocation into every log', async () => { + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + + // Check that the X-Ray Trace ID is logged on every log + const traceIds: string[] = []; + for (const message of logMessages) { + const log = InvocationLogs.parseFunctionLog(message); + expect(log).toHaveProperty('xray_trace_id'); + expect(log.xray_trace_id).toMatch(XRAY_TRACE_ID_REGEX); + traceIds.push(log.xray_trace_id as string); + } + } - expect(errorObj.name).toBe('Error'); - expect(errorObj.message).toBe(ERROR_MSG); - expect(errorObj.stack).toBeDefined(); }, TEST_CASE_TIMEOUT); + }); afterAll(async () => { diff --git a/packages/logger/tests/e2e/childLogger.manual.test.FunctionCode.ts b/packages/logger/tests/e2e/childLogger.manual.test.FunctionCode.ts index a738eb6888..c4e0fd8ec3 100644 --- a/packages/logger/tests/e2e/childLogger.manual.test.FunctionCode.ts +++ b/packages/logger/tests/e2e/childLogger.manual.test.FunctionCode.ts @@ -1,15 +1,17 @@ import { Logger } from '../../src'; -import { APIGatewayProxyEvent, Context } from 'aws-lambda'; +import { Context } from 'aws-lambda'; +import { LogLevel } from '../../src/types'; +import { TestEvent, TestOutput } from '../helpers/types'; -const PARENT_PERSISTENT_KEY = process.env.PARENT_PERSISTENT_KEY; -const PARENT_PERSISTENT_VALUE = process.env.PARENT_PERSISTENT_VALUE; -const PARENT_LOG_MSG = process.env.PARENT_LOG_MSG; -const CHILD_LOG_MSG = process.env.PARENT_LOG_MSG; -const CHILD_LOG_LEVEL = process.env.CHILD_LOG_LEVEL; +const PERSISTENT_KEY = process.env.PERSISTENT_KEY || 'persistentKey'; +const PERSISTENT_VALUE = process.env.ERSISTENT_VALUE || 'persistentValue'; +const PARENT_LOG_MSG = process.env.PARENT_LOG_MSG || 'parent-only-log-msg'; +const CHILD_LOG_MSG = process.env.CHILD_LOG_MSG || 'child-only-log-msg'; +const CHILD_LOG_LEVEL = (process.env.CHILD_LOG_LEVEL || 'warn') as LogLevel; const parentLogger = new Logger({ persistentLogAttributes: { - [PARENT_PERSISTENT_KEY]: PARENT_PERSISTENT_VALUE, + [PERSISTENT_KEY]: PERSISTENT_VALUE, } }); @@ -18,7 +20,7 @@ const childLogger = parentLogger.createChild({ logLevel: CHILD_LOG_LEVEL, }); -export const handler = async (event: APIGatewayProxyEvent, context: Context): Promise<{requestId: string}> => { +export const handler = async (_event: TestEvent, context: Context): TestOutput => { parentLogger.addContext(context); childLogger.info(CHILD_LOG_MSG); diff --git a/packages/logger/tests/e2e/childLogger.manual.test.ts b/packages/logger/tests/e2e/childLogger.manual.test.ts index 5bce0bb4f5..1e11d52b0b 100644 --- a/packages/logger/tests/e2e/childLogger.manual.test.ts +++ b/packages/logger/tests/e2e/childLogger.manual.test.ts @@ -1,12 +1,8 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT-0 - /** * Test logger child logger * * @group e2e/logger/childLogger */ - import path from 'path'; import { App, Stack } from 'aws-cdk-lib'; import { v4 } from 'uuid'; @@ -39,16 +35,19 @@ const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'Child const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'ChildLogger-Manual'); const lambdaFunctionCodeFile = 'childLogger.manual.test.FunctionCode.ts'; +const invocationCount = 3; + // Parameters to be used by Logger in the Lambda function -const PARENT_PERSISTENT_KEY = 'persistentKey'; -const PARENT_PERSISTENT_VALUE = `a persistent value that will be put in prent only ${uuid}`; -const PARENT_LOG_MSG = `only PARENT logger will log with this message ${uuid}`; -const CHILD_LOG_MSG = `only CHILD logger will log with this message ${uuid}`; -const CHILD_LOG_LEVEL = LEVEL.ERROR.toString(); +const PERSISTENT_KEY = 'persistentKey'; +const PERSISTENT_VALUE = 'persistentValue'; +const PARENT_LOG_MSG = 'parent-only-log-msg'; +const CHILD_LOG_MSG = 'child-only-log-msg'; +const CHILD_LOG_LEVEL = LEVEL.ERROR; const integTestApp = new App(); let logGroupName: string; // We do not know it until deployment let stack: Stack; + describe(`logger E2E tests child logger functionalities (manual) for runtime: ${runtime}`, () => { let invocationLogs: InvocationLogs[]; @@ -67,8 +66,8 @@ describe(`logger E2E tests child logger functionalities (manual) for runtime: ${ UUID: uuid, // Text to be used by Logger in the Lambda function - PARENT_PERSISTENT_KEY, - PARENT_PERSISTENT_VALUE, + PERSISTENT_KEY, + PERSISTENT_VALUE, PARENT_LOG_MSG, CHILD_LOG_MSG, CHILD_LOG_LEVEL, @@ -79,55 +78,89 @@ describe(`logger E2E tests child logger functionalities (manual) for runtime: ${ const result = await deployStack(integTestApp, stack); logGroupName = result.outputs[STACK_OUTPUT_LOG_GROUP]; - // Invoke the function once - invocationLogs = await invokeFunction(functionName, 1); + // Invoke the function three time (one for cold start, then two for warm start) + invocationLogs = await invokeFunction(functionName, invocationCount); console.log('logGroupName', logGroupName); }, SETUP_TIMEOUT); - const getAllChildLogs = (): string[] => invocationLogs[0].getFunctionLogs() - .filter(message => message.includes(CHILD_LOG_MSG)); - describe('Child logger', () => { - it('should not log at parent log level', async () => { - // Only parents log will appear at info level - const allInfoLogs = invocationLogs[0].getFunctionLogs(LEVEL.INFO); - const parentInfoLogs = allInfoLogs.filter(message => message.includes(PARENT_LOG_MSG)); - const childInfoLogs = allInfoLogs.filter(message => message.includes(CHILD_LOG_MSG)); - - expect(parentInfoLogs).toHaveLength(allInfoLogs.length); - expect(childInfoLogs).toHaveLength(0); + + it('should not log at same level of parent because of its own logLevel', async () => { + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation and filter by level + const infoLogs = invocationLogs[i].getFunctionLogs(LEVEL.INFO); + + const parentInfoLogs = infoLogs + .filter(message => message.includes(PARENT_LOG_MSG)); + const childInfoLogs = infoLogs + .filter(message => message.includes(CHILD_LOG_MSG)); + + expect(parentInfoLogs).toHaveLength(infoLogs.length); + expect(childInfoLogs).toHaveLength(0); + } + }, TEST_CASE_TIMEOUT); it('should log only level passed to a child', async () => { - const allChildLogs = getAllChildLogs(); - const errorChildLogs = allChildLogs.filter(message => message.includes(LEVEL.ERROR.toString())); - expect(errorChildLogs).toHaveLength(allChildLogs.length); + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + + // Filter child logs by level + const errorChildLogs = logMessages + .filter( + message => message.includes(LEVEL.ERROR.toString()) && + message.includes(CHILD_LOG_MSG) + ); + + // Check that the child logger only logged once (the other) + // log was filtered out by the child logger because of its logLevel + expect(errorChildLogs).toHaveLength(1); + } + }, TEST_CASE_TIMEOUT); it('should NOT inject context into the child logger', async () => { - // Only parent has injected context. - const allChildLogs = getAllChildLogs(); - - for (const log of allChildLogs) { - expect(log).not.toContain('function_arn'); - expect(log).not.toContain('function_memory_size'); - expect(log).not.toContain('function_name'); - expect(log).not.toContain('function_request_id'); - expect(log).not.toContain('timestamp'); + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + + // Filter child logs by level + const childLogMessages = logMessages + .filter(message => message.includes(CHILD_LOG_MSG)); + + // Check that the context is not present in any of the child logs + for (const message of childLogMessages) { + const log = InvocationLogs.parseFunctionLog(message); + expect(log).not.toHaveProperty('function_arn'); + expect(log).not.toHaveProperty('function_memory_size'); + expect(log).not.toHaveProperty('function_name'); + expect(log).not.toHaveProperty('function_request_id'); + } } + }, TEST_CASE_TIMEOUT); - it('should NOT have parent logger persistent key/value', async () => { - // Only parent has injected context. - const allChildLogs = getAllChildLogs(); + it('both logger instances should have the same persistent key/value', async () => { + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); - for (const log of allChildLogs) { - expect(log).not.toContain(`"${PARENT_PERSISTENT_KEY}":"${PARENT_PERSISTENT_VALUE}"`); + // Check that all logs have the persistent key/value + for (const message of logMessages) { + const log = InvocationLogs.parseFunctionLog(message); + expect(log).toHaveProperty(PERSISTENT_KEY); + } } + }, TEST_CASE_TIMEOUT); + }); afterAll(async () => { diff --git a/packages/logger/tests/e2e/constants.ts b/packages/logger/tests/e2e/constants.ts index 79eb64e5aa..25c928e0c1 100644 --- a/packages/logger/tests/e2e/constants.ts +++ b/packages/logger/tests/e2e/constants.ts @@ -3,4 +3,5 @@ export const ONE_MINUTE = 60 * 1000; export const TEST_CASE_TIMEOUT = ONE_MINUTE; export const SETUP_TIMEOUT = 5 * ONE_MINUTE; export const TEARDOWN_TIMEOUT = 5 * ONE_MINUTE; -export const STACK_OUTPUT_LOG_GROUP = 'LogGroupName'; \ No newline at end of file +export const STACK_OUTPUT_LOG_GROUP = 'LogGroupName'; +export const XRAY_TRACE_ID_REGEX = /^1-[0-9a-f]{8}-[0-9a-f]{24}$/; \ No newline at end of file diff --git a/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.FunctionCode.ts b/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.FunctionCode.ts index 6d412daf06..6fdcd1ec62 100644 --- a/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.FunctionCode.ts +++ b/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.FunctionCode.ts @@ -1,16 +1,14 @@ import { injectLambdaContext, Logger } from '../../src'; +import { TestEvent, TestOutput } from '../helpers/types'; import { Context } from 'aws-lambda'; import middy from '@middy/core'; -type LambdaEvent = { - invocation: number -}; - const logger = new Logger(); -const testFunction = async (event: LambdaEvent, context: Context): Promise<{requestId: string}> => ({ +const testFunction = async (_event: TestEvent, context: Context): TestOutput => ({ requestId: context.awsRequestId, }); export const handler = middy(testFunction) - .use(injectLambdaContext(logger)); + // The event should be logged because POWERTOOLS_LOGGER_LOG_EVENT is set to true + .use(injectLambdaContext(logger)); diff --git a/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.ts b/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.ts index 147d838c95..fc49c31a8c 100644 --- a/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.ts +++ b/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.ts @@ -1,12 +1,8 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT-0 - /** * Test logger basic features * * @group e2e/logger/logEventEnvVarSetting */ - import path from 'path'; import { App, Stack } from 'aws-cdk-lib'; import { v4 } from 'uuid'; @@ -37,6 +33,8 @@ const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'LogEv const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'LogEventEnvVarSetting-Middy'); const lambdaFunctionCodeFile = 'logEventEnvVarSetting.middy.test.FunctionCode.ts'; +const invocationCount = 3; + const integTestApp = new App(); let logGroupName: string; // We do not know it until deployment let stack: Stack; @@ -67,8 +65,8 @@ describe(`logger E2E tests log event via env var setting (middy) for runtime: ${ const result = await deployStack(integTestApp, stack); logGroupName = result.outputs[STACK_OUTPUT_LOG_GROUP]; - // Invoke the function twice (one for cold start, another for warm start) - invocationLogs = await invokeFunction(functionName, 2, 'SEQUENTIAL'); + // Invoke the function three time (one for cold start, then two for warm start) + invocationLogs = await invokeFunction(functionName, invocationCount, 'SEQUENTIAL'); console.log('logGroupName', logGroupName); @@ -76,28 +74,27 @@ describe(`logger E2E tests log event via env var setting (middy) for runtime: ${ describe('Log event', () => { - it('should log the event at both invocations', async () => { - const firstInvocationMessages = invocationLogs[0].getAllFunctionLogs(); - let eventLoggedInFirstInvocation = false; - for (const message of firstInvocationMessages) { - if (message.includes(`event`)) { - eventLoggedInFirstInvocation = true; - expect(message).toContain(`"event":{"invocation":0}`); + it('should log the event as the first log of each invocation only', async () => { + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + + for (const [ index, message ] of logMessages.entries()) { + const log = InvocationLogs.parseFunctionLog(message); + // Check that the event is logged on the first log + if (index === 0) { + expect(log).toHaveProperty('event'); + expect(log.event).toStrictEqual( + expect.objectContaining({ invocation: i }) + ); + // Check that the event is not logged again on the rest of the logs + } else { + expect(log).not.toHaveProperty('event'); + } } } - const secondInvocationMessages = invocationLogs[1].getAllFunctionLogs(); - let eventLoggedInSecondInvocation = false; - for (const message of secondInvocationMessages) { - if (message.includes(`event`)) { - eventLoggedInSecondInvocation = true; - expect(message).toContain(`"event":{"invocation":1}`); - } - } - - expect(eventLoggedInFirstInvocation).toBe(true); - expect(eventLoggedInSecondInvocation).toBe(true); - }, TEST_CASE_TIMEOUT); }); diff --git a/packages/logger/tests/e2e/sampleRate.decorator.test.FunctionCode.ts b/packages/logger/tests/e2e/sampleRate.decorator.test.FunctionCode.ts index 72c13f07b4..ba4b7f8895 100644 --- a/packages/logger/tests/e2e/sampleRate.decorator.test.FunctionCode.ts +++ b/packages/logger/tests/e2e/sampleRate.decorator.test.FunctionCode.ts @@ -1,5 +1,6 @@ import { Logger } from '../../src'; -import { APIGatewayProxyEvent, Context } from 'aws-lambda'; +import { TestEvent, TestOutput } from '../helpers/types'; +import { Context } from 'aws-lambda'; import { LambdaInterface } from '@aws-lambda-powertools/commons'; const SAMPLE_RATE = parseFloat(process.env.SAMPLE_RATE || '0.1'); @@ -20,7 +21,7 @@ class Lambda implements LambdaInterface { @logger.injectLambdaContext() // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - public async handler(event: APIGatewayProxyEvent, context: Context): Promise<{requestId: string}> { + public async handler(event: TestEvent, context: Context): TestOutput { this.printLogInAllLevels(); return { diff --git a/packages/logger/tests/e2e/sampleRate.decorator.test.ts b/packages/logger/tests/e2e/sampleRate.decorator.test.ts index f803deae79..1955b20f46 100644 --- a/packages/logger/tests/e2e/sampleRate.decorator.test.ts +++ b/packages/logger/tests/e2e/sampleRate.decorator.test.ts @@ -1,12 +1,8 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT-0 - /** * Test logger sample rate feature * * @group e2e/logger/sampleRate */ - import path from 'path'; import { App, Stack } from 'aws-cdk-lib'; import { v4 } from 'uuid'; @@ -39,16 +35,18 @@ const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'Sampl const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'SampleRate-Decorator'); const lambdaFunctionCodeFile = 'sampleRate.decorator.test.FunctionCode.ts'; +const invocationCount = 20; + // Parameters to be used by Logger in the Lambda function const LOG_MSG = `Log message ${uuid}`; const SAMPLE_RATE = '0.5'; -const LOG_LEVEL = LEVEL.ERROR.toString(); +const LOG_LEVEL = LEVEL.ERROR; const integTestApp = new App(); let stack: Stack; let logGroupName: string; // We do not know the exact name until deployment -describe(`logger E2E tests sample rate and injectLambdaContext() for runtime: ${runtime}`, () => { +describe(`logger E2E tests sample rate and injectLambdaContext() for runtime: nodejs18x`, () => { let invocationLogs: InvocationLogs[]; @@ -75,7 +73,7 @@ describe(`logger E2E tests sample rate and injectLambdaContext() for runtime: ${ const result = await deployStack(integTestApp, stack); logGroupName = result.outputs[STACK_OUTPUT_LOG_GROUP]; - invocationLogs = await invokeFunction(functionName, 20); + invocationLogs = await invokeFunction(functionName, invocationCount); console.log('logGroupName', logGroupName); @@ -86,23 +84,23 @@ describe(`logger E2E tests sample rate and injectLambdaContext() for runtime: ${ // Fetch log streams from all invocations let countSampled = 0; let countNotSampled = 0; - for (const invocationLog of invocationLogs) { - const logs = invocationLog.getFunctionLogs(); - if (logs.length === 1 && logs[0].includes(LEVEL.ERROR.toString())) { + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(); + + if (logMessages.length === 1 && logMessages[0].includes(LEVEL.ERROR)) { countNotSampled++; - } else if (logs.length === 4) { + } else if (logMessages.length === 4) { countSampled++; } else { - // TODO: To be investigate if this is Lambda service's issue. This happens once every 10-20 e2e tests runs. The symptoms I found are: - // 1. Warning and error logs disappear (in sampled case) - // 2. Error log disappears (in non-sampled case) - // I reviewed Logger code but it is not possible that the first case could happen because we use the same "logsSampled" flag. console.error(`Log group ${logGroupName} contains missing log`); throw new Error('Sampled log should have either 1 error log or 4 logs of all levels'); } } - // Given that we set rate to 0.5. The chance that we get all invocations sampled (or not sampled) is less than 0.5^20 + // Given that we set rate to 0.5. The chance that we get all invocations sampled + // (or not sampled) is less than 0.5^20 expect(countSampled).toBeGreaterThan(0); expect(countNotSampled).toBeGreaterThan(0); @@ -110,15 +108,21 @@ describe(`logger E2E tests sample rate and injectLambdaContext() for runtime: ${ }); describe('Decorator injectLambdaContext()', () => { - it('should inject Lambda context into the log', async () => { - const logMessages = invocationLogs[0].getFunctionLogs(LEVEL.ERROR); - - for (const log of logMessages) { - expect(log).toContain('function_arn'); - expect(log).toContain('function_memory_size'); - expect(log).toContain('function_name'); - expect(log).toContain('function_request_id'); - expect(log).toContain('timestamp'); + it('should inject Lambda context into every log emitted', async () => { + + for (let i = 0; i < invocationCount; i++) { + // Get log messages of the invocation + const logMessages = invocationLogs[i].getFunctionLogs(LEVEL.ERROR); + + // Check that the context is logged on every log + for (const message of logMessages) { + const log = InvocationLogs.parseFunctionLog(message); + expect(log).toHaveProperty('function_arn'); + expect(log).toHaveProperty('function_memory_size'); + expect(log).toHaveProperty('function_name'); + expect(log).toHaveProperty('function_request_id'); + expect(log).toHaveProperty('timestamp'); + } } }, TEST_CASE_TIMEOUT); diff --git a/packages/logger/tests/helpers/types.ts b/packages/logger/tests/helpers/types.ts new file mode 100644 index 0000000000..8c7b5dcbdf --- /dev/null +++ b/packages/logger/tests/helpers/types.ts @@ -0,0 +1,5 @@ +export type TestEvent = { + invocation: number +}; + +export type TestOutput = Promise<{requestId: string}>; \ No newline at end of file From b0a0fe5d0c7732ad7268fb54732fddefd88e52eb Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 28 Feb 2023 10:14:32 +0100 Subject: [PATCH 47/56] chore(ci): narrow down stale issue filtering (#1346) * chore: fix issue filtering * docs: updated maintainer guidance * docs: updated label meaning --- .github/workflows/stale-issues.yml | 10 +++++----- MAINTAINERS.md | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml index f3ea84ee3c..e080faba7f 100644 --- a/.github/workflows/stale-issues.yml +++ b/.github/workflows/stale-issues.yml @@ -23,13 +23,13 @@ jobs: close-issue-reason: not_planned # Exempt any issue that hasn't been triaged yet, or that is clearly labeled exempt-issue-labels: triage,status/confirmed,status/blocked,status/on-hold,status/completed - # Include only issues where any of these labels are present (aka only issues that need more info from the customer) - # that are either under discussion, but no info/answer has been provided in 2 weeks, or that were already marked - # as stale - any-of-issue-labels: need-more-information,status/discussing,status/pending-close-response-required + # Include only issues that were labeled as `need-more-information` (aka only issues that need more info from the customer) + only-issue-labels: need-more-information # Settings specific to issues days-before-issue-stale: 14 days-before-issue-close: 7 # Set to ignore PRs days-before-pr-stale: -1 - days-before-pr-close: -1 \ No newline at end of file + days-before-pr-close: -1 + # Operations + operations-per-run: 60 diff --git a/MAINTAINERS.md b/MAINTAINERS.md index c7d31119bc..2d13d63424 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -102,7 +102,7 @@ These are the most common labels used by maintainers to triage issues, pull requ | good-first-issue | Something that is suitable for those who want to start contributing | | | help-wanted | Tasks you want help from anyone to move forward | Bandwidth, complex topics, etc. | | need-customer-feedback | Tasks that need more feedback before proceeding | 80/20% rule, uncertain, etc. | -| need-more-information | Missing information before making any calls | | +| need-more-information | Missing information before making any calls | Triggers stale automation after 2 weeks | | need-issue | PR is missing a related issue for tracking change | | ## Maintainer Responsibilities @@ -268,7 +268,7 @@ In other cases, you may have constrained capacity. Use `help=wanted` label when When in doubt, use `need-more-information` or `need-customer-feedback` labels to signal more context and feedback are necessary before proceeding. You can also use `status/on-hold` label when you expect it might take a while to gather enough information before you can decide. -Note that issues marked as `need-more-information` and `status/discussing` will be automatically closed after 3 weeks of inactivity. +Note that issues marked as `need-more-information` will be automatically closed after 3 weeks of inactivity. ### Crediting contributions @@ -298,4 +298,4 @@ In the rare cases where both parties don't have the bandwidth or expertise to co ## Automation -🚧 WIP 🚧 \ No newline at end of file +🚧 WIP 🚧 From 87b94e552e973fbed5c6193e4632a562e06740ab Mon Sep 17 00:00:00 2001 From: Muthu Venkatachalam <33663725+Muthuveerappanv@users.noreply.github.com> Date: Tue, 28 Feb 2023 13:13:33 -0500 Subject: [PATCH 48/56] chore: add workspace flag to `package` script (#1344) * fix worspace package script fixes - https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1343 * fix review comments, skip package for docs snippets --------- Co-authored-by: Muthu Venkatachalam --- docs/snippets/package.json | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/snippets/package.json b/docs/snippets/package.json index 48cb3fa3da..8779dea179 100644 --- a/docs/snippets/package.json +++ b/docs/snippets/package.json @@ -9,6 +9,7 @@ "scripts": { "test": "echo 'Not Applicable'", "test:e2e": "echo 'Not Applicable'", + "package": "echo 'Not applicable'", "build": "echo 'Not Applicable'", "lint": "eslint --ext .ts --no-error-on-unmatched-pattern logger tracer metrics parameters", "lint-fix": "eslint --fix --ext .ts --no-error-on-unmatched-pattern logger tracer metrics parameters" diff --git a/package.json b/package.json index dafe17ab5d..d9db61efa2 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "init-environment": "husky install", "test": "npm t -ws", "commit": "commit", - "package": "npm run package", + "package": "npm run package -ws", "setup-local": "export PROJECT_ROOT=$(pwd) && npm ci --foreground-scripts && cd $PROJECT_ROOT/examples/cdk && npm ci && cd $PROJECT_ROOT/examples/sam && npm ci && cd $PROJECT_ROOT/ && npm run init-environment", "build": "npm run build -ws", "postversion": "git push && git push --tags", From ca0bdb03542f091b251431188665998059196a33 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 28 Feb 2023 19:45:11 +0100 Subject: [PATCH 49/56] chore(ci): fix typo in doc publishing workflow (#1349) * chore: fix typo in variable name * chore: remove extra spacing * chore: remove extra path from triggers --- .github/workflows/on_doc_merge.yml | 1 - .github/workflows/reusable-publish-docs.yml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/on_doc_merge.yml b/.github/workflows/on_doc_merge.yml index 9e454a834d..ff1e5ec487 100644 --- a/.github/workflows/on_doc_merge.yml +++ b/.github/workflows/on_doc_merge.yml @@ -7,7 +7,6 @@ on: paths: - "docs/**" - "mkdocs.yml" - - "examples/**" jobs: release-docs: diff --git a/.github/workflows/reusable-publish-docs.yml b/.github/workflows/reusable-publish-docs.yml index e64d6e92f3..24c39ae6cb 100644 --- a/.github/workflows/reusable-publish-docs.yml +++ b/.github/workflows/reusable-publish-docs.yml @@ -4,7 +4,6 @@ env: BRANCH: main ORIGIN: awslabs/aws-lambda-powertools-typescript - on: workflow_call: inputs: @@ -111,7 +110,7 @@ jobs: keep_files: true destination_dir: ${{ env.VERSION }}/api - name: Release API docs to latest - if: ${{ input.alias == 'latest' }} + if: ${{ inputs.alias == 'latest' }} uses: peaceiris/actions-gh-pages@bd8c6b06eba6b3d25d72b7a1767993c0aeee42e7 with: github_token: ${{ secrets.GITHUB_TOKEN }} From d0fab1a47a911c7137f1faa26252ebfe381983e8 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 28 Feb 2023 22:42:59 +0100 Subject: [PATCH 50/56] chore: add disclaimer to pr template (#1351) --- .github/PULL_REQUEST_TEMPLATE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 70037b5ca3..a199f2ea66 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -49,3 +49,5 @@ --- By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. + +**Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. \ No newline at end of file From 8c3275495cf3a500f10ad83c75e0a4ea67e10594 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 2 Mar 2023 15:09:36 +0100 Subject: [PATCH 51/56] chore(ci): fix workflow definition --- .github/workflows/publish_layer.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish_layer.yml b/.github/workflows/publish_layer.yml index ae5c4bc2c0..20ffed7b8e 100644 --- a/.github/workflows/publish_layer.yml +++ b/.github/workflows/publish_layer.yml @@ -113,11 +113,11 @@ jobs: echo DOCS_ALIAS="$DOCS_ALIAS" >> "$GITHUB_OUTPUT" release-docs: - needs: [ prod, prepare_docs_alias ] + needs: [ deploy-prod, prepare_docs_alias ] permissions: contents: write pages: write - uses: ./.github/workflows/reusable_publish_docs.yml + uses: ./.github/workflows/reusable-publish-docs.yml with: version: ${{ inputs.latest_published_version }} alias: ${{ needs.prepare_docs_alias.outputs.DOCS_ALIAS }} From 208d23a49d9cbf91525dec7148b969124c57a446 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 2 Mar 2023 15:18:40 +0100 Subject: [PATCH 52/56] chore(ci): fix publish_layer permissions --- .github/workflows/publish_layer.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish_layer.yml b/.github/workflows/publish_layer.yml index 20ffed7b8e..a677557f49 100644 --- a/.github/workflows/publish_layer.yml +++ b/.github/workflows/publish_layer.yml @@ -2,7 +2,8 @@ name: Deploy layer to all regions permissions: id-token: write - contents: read + contents: write + pages: write on: # Manual trigger From f317bcd0e10be8b9fe2ed74099b39c2c46fac57c Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 2 Mar 2023 15:27:42 +0100 Subject: [PATCH 53/56] chore(ci): remove permission block from workflow --- .github/workflows/publish_layer.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/publish_layer.yml b/.github/workflows/publish_layer.yml index a677557f49..0ba75b0b63 100644 --- a/.github/workflows/publish_layer.yml +++ b/.github/workflows/publish_layer.yml @@ -115,9 +115,6 @@ jobs: release-docs: needs: [ deploy-prod, prepare_docs_alias ] - permissions: - contents: write - pages: write uses: ./.github/workflows/reusable-publish-docs.yml with: version: ${{ inputs.latest_published_version }} From 4848d4c7752af6bbf8db4d8f518f0526f24a602a Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 2 Mar 2023 15:35:35 +0100 Subject: [PATCH 54/56] chore(ci): update permission passed --- .github/workflows/reusable_deploy_layer_stack.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable_deploy_layer_stack.yml b/.github/workflows/reusable_deploy_layer_stack.yml index 865e0a493f..8222e7811e 100644 --- a/.github/workflows/reusable_deploy_layer_stack.yml +++ b/.github/workflows/reusable_deploy_layer_stack.yml @@ -106,8 +106,10 @@ jobs: path: ./layer/cdk-layer-stack/* # NOTE: upload-artifact does not inherit working-directory setting. if-no-files-found: error retention-days: 1 - update_v2_layer_arn_docs: + update_layer_arn_docs: needs: deploy-cdk-stack + permissions: + contents: write if: ${{ inputs.stage == 'PROD' }} uses: ./.github/workflows/reusable_update_layer_arn_docs.yml with: From 6d002900c290e91baa0e190ece517913fddc7c83 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 2 Mar 2023 15:45:18 +0100 Subject: [PATCH 55/56] chore(ci): removed secret usage --- .github/workflows/reusable-publish-docs.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/reusable-publish-docs.yml b/.github/workflows/reusable-publish-docs.yml index 24c39ae6cb..9a7f114ebb 100644 --- a/.github/workflows/reusable-publish-docs.yml +++ b/.github/workflows/reusable-publish-docs.yml @@ -20,24 +20,14 @@ on: required: false default: false type: boolean - workflow_origin: # see https://github.com/awslabs/aws-lambda-powertools-python/issues/1349 - required: true - type: string - secrets: - token: - required: true jobs: publish-docs: - # see https://github.com/awslabs/aws-lambda-powertools-python/issues/1349 - if: ${{ inputs.workflow_origin == 'awslabs/aws-lambda-powertools-typescript' }} runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 with: - # Here `token` is needed to avoid incurring in error GH006 Protected Branch Update Failed, - token: ${{ secrets.token }} # While `fetch-depth` is used to allow the workflow to later commit & push the changes. fetch-depth: 0 - name: Setup NodeJS From 196ded7099174d8817489846a128f3facf7c2b0f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 2 Mar 2023 15:03:50 +0000 Subject: [PATCH 56/56] chore(release): v1.6.0 [skip ci] --- CHANGELOG.md | 24 +++++++++ examples/cdk/CHANGELOG.md | 8 +++ examples/cdk/package-lock.json | 92 ++++++++++++++-------------------- examples/cdk/package.json | 8 +-- examples/sam/CHANGELOG.md | 8 +++ examples/sam/package-lock.json | 90 ++++++++++++++------------------- examples/sam/package.json | 8 +-- lerna.json | 2 +- packages/commons/CHANGELOG.md | 8 +++ packages/commons/package.json | 2 +- packages/logger/CHANGELOG.md | 17 +++++++ packages/logger/package.json | 4 +- packages/metrics/CHANGELOG.md | 8 +++ packages/metrics/package.json | 4 +- packages/tracer/CHANGELOG.md | 8 +++ packages/tracer/package.json | 4 +- 16 files changed, 175 insertions(+), 120 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64952bcef9..a8e61293d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.6.0](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.1...v1.6.0) (2023-03-02) + + +### Bug Fixes + +* **docs:** logger bringYourOwnFormatter snippet [#1253](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1253) ([#1254](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1254)) ([fdbba32](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/fdbba32d8b3545730d242ac4fd1ef2d83cdbccce)) +* hardcoded cdk version in `publish_layer.yaml` ([#1232](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1232)) ([63a3909](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/63a3909063637ca2306a718a10e35e54881f570e)) +* **logger:** createChild not passing all parent's attributes ([#1267](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1267)) ([84ab4b9](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/84ab4b911d17d687bdbe60ded31f1e2b6860feb3)) +* **logger:** middleware stores initial persistent attributes correctly ([#1329](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1329)) ([6b32304](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/6b3230489895dc1abdfc6ad56daeeb555fda529f)) +* **parameters:** handle base64/binaries in transformer ([#1326](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1326)) ([bb50c04](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/bb50c04f5b2e6a144295b453577a7ea1a15ac011)) +* **parameters:** Tokenize attribute names in `DynamoDBProvider` ([#1239](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1239)) ([f3e5ed7](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/f3e5ed70c7e5baa3f3aa15428e8d6cb56b096f26)) + + +### Features + +* **idempotency:** Add function wrapper and decorator ([#1262](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1262)) ([eacb1d9](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/eacb1d9f59a82ad34234f51198ed215c41a64b41)) +* **layers:** add new regions ([#1322](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1322)) ([618613b](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/618613b9a69166553dd9ef8d5b92f89e1cdf79d0)) +* **logger:** make loglevel types stricter ([#1313](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1313)) ([5af51d3](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/5af51d319dee68d7a7ba832721580d7a6e655249)) +* **parameters:** add support for custom AWS SDK v3 clients for providers ([#1260](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1260)) ([3a8cfa0](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/3a8cfa0d6e5aaa5c2c36d97d7835dbf5287b7110)) + + + + + ## [1.5.1](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.0...v1.5.1) (2023-01-13) diff --git a/examples/cdk/CHANGELOG.md b/examples/cdk/CHANGELOG.md index 3fd2ea7b7d..c740e87e5c 100644 --- a/examples/cdk/CHANGELOG.md +++ b/examples/cdk/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.6.0](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.1...v1.6.0) (2023-03-02) + +**Note:** Version bump only for package cdk-sample + + + + + ## [1.5.1](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.0...v1.5.1) (2023-01-13) **Note:** Version bump only for package cdk-sample diff --git a/examples/cdk/package-lock.json b/examples/cdk/package-lock.json index 14aafa8ecb..ad83490a26 100644 --- a/examples/cdk/package-lock.json +++ b/examples/cdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "cdk-sample", - "version": "1.5.1", + "version": "1.6.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "cdk-sample", - "version": "1.5.1", + "version": "1.6.0", "license": "MIT-0", "dependencies": { "@middy/core": "^3.6.2", @@ -20,9 +20,9 @@ "cdk-app": "bin/cdk-app.js" }, "devDependencies": { - "@aws-lambda-powertools/logger": "^1.5.0", - "@aws-lambda-powertools/metrics": "^1.5.0", - "@aws-lambda-powertools/tracer": "^1.5.0", + "@aws-lambda-powertools/logger": "^1.5.1", + "@aws-lambda-powertools/metrics": "^1.5.1", + "@aws-lambda-powertools/tracer": "^1.5.1", "@aws-sdk/lib-dynamodb": "^3.231.0", "@types/aws-lambda": "^8.10.109", "@types/jest": "^29.2.4", @@ -165,39 +165,38 @@ "peer": true }, "node_modules/@aws-lambda-powertools/commons": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/commons/-/commons-1.5.0.tgz", - "integrity": "sha512-UDG1EzINigcBCxoDcwtiqtWR0ZDkZu3w4dZ/mCenUL3v1AUoiUfmpiZI54r0NyXXeyQOIAmvKft7VnQP2BHfJA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/commons/-/commons-1.5.1.tgz", + "integrity": "sha512-qp13/WksB6M/liEHgV3301qmI/aeQKmrYk1iODB+rrkVWeKW4AJqBMuWQ26gIlbD8t82EtuUNPUFle1a2qwjIg==", "dev": true }, "node_modules/@aws-lambda-powertools/logger": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/logger/-/logger-1.5.0.tgz", - "integrity": "sha512-BlajpUrI4fFMNfv1QBvPG9m7cCdu+5YCo8AsF+/2nM6OwEJDIIqpYvCQEr9TeQZQMMbirp43De2Z7MisV0HDyw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/logger/-/logger-1.5.1.tgz", + "integrity": "sha512-dXvw7c4Z0DUEY62zEWsQ4DQZ3yf+JsEAqojQH+kw+rXVv0cjnIEbM4ZKONPG2jcPS5IMzO6gmCbu/PKB1PYgyA==", "dev": true, "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.0", - "lodash.merge": "^4.6.2", - "lodash.pickby": "^4.6.0" + "@aws-lambda-powertools/commons": "^1.5.1", + "lodash.merge": "^4.6.2" } }, "node_modules/@aws-lambda-powertools/metrics": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/metrics/-/metrics-1.5.0.tgz", - "integrity": "sha512-4x04YpKWz4OI4NSxbi2EhFQYGNjvO3kHUO9gSN/pOpa/bhrwXaTL1gIJsPKcDlKjm1VeQtLbFDutespz4kttpA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/metrics/-/metrics-1.5.1.tgz", + "integrity": "sha512-kBvJR9phGdH5UqtcWyxVMQoZU57liQeVxZCFbDMSAhdlxBbW1pkPBASaKv/FLlX++Tb4TeO1Tj28dox1yL1t+w==", "dev": true, "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.0" + "@aws-lambda-powertools/commons": "^1.5.1" } }, "node_modules/@aws-lambda-powertools/tracer": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/tracer/-/tracer-1.5.0.tgz", - "integrity": "sha512-WXpMqzOsgi8PN+q+cQAWw5Y/Kf/wQ0BlAiIHcn2MSP5lurdOj9pslqUZOYywtjL1FraGOd+2ZEdo+iw6cbAz+w==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/tracer/-/tracer-1.5.1.tgz", + "integrity": "sha512-T/BHOzg+LbY5PF6drKuVhyk4tcAakyn8gI/lGrx8NhZ72vc0YZOCGaMhvUGbImoZ0UFyn5ApP0n70qvk1i1A/Q==", "dev": true, "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.0", - "aws-xray-sdk-core": "^3.3.6" + "@aws-lambda-powertools/commons": "^1.5.1", + "aws-xray-sdk-core": "^3.4.0" } }, "node_modules/@aws-sdk/abort-controller": { @@ -6465,12 +6464,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/lodash.pickby": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", - "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==", - "dev": true - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -8194,39 +8187,38 @@ } }, "@aws-lambda-powertools/commons": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/commons/-/commons-1.5.0.tgz", - "integrity": "sha512-UDG1EzINigcBCxoDcwtiqtWR0ZDkZu3w4dZ/mCenUL3v1AUoiUfmpiZI54r0NyXXeyQOIAmvKft7VnQP2BHfJA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/commons/-/commons-1.5.1.tgz", + "integrity": "sha512-qp13/WksB6M/liEHgV3301qmI/aeQKmrYk1iODB+rrkVWeKW4AJqBMuWQ26gIlbD8t82EtuUNPUFle1a2qwjIg==", "dev": true }, "@aws-lambda-powertools/logger": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/logger/-/logger-1.5.0.tgz", - "integrity": "sha512-BlajpUrI4fFMNfv1QBvPG9m7cCdu+5YCo8AsF+/2nM6OwEJDIIqpYvCQEr9TeQZQMMbirp43De2Z7MisV0HDyw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/logger/-/logger-1.5.1.tgz", + "integrity": "sha512-dXvw7c4Z0DUEY62zEWsQ4DQZ3yf+JsEAqojQH+kw+rXVv0cjnIEbM4ZKONPG2jcPS5IMzO6gmCbu/PKB1PYgyA==", "dev": true, "requires": { - "@aws-lambda-powertools/commons": "^1.5.0", - "lodash.merge": "^4.6.2", - "lodash.pickby": "^4.6.0" + "@aws-lambda-powertools/commons": "^1.5.1", + "lodash.merge": "^4.6.2" } }, "@aws-lambda-powertools/metrics": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/metrics/-/metrics-1.5.0.tgz", - "integrity": "sha512-4x04YpKWz4OI4NSxbi2EhFQYGNjvO3kHUO9gSN/pOpa/bhrwXaTL1gIJsPKcDlKjm1VeQtLbFDutespz4kttpA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/metrics/-/metrics-1.5.1.tgz", + "integrity": "sha512-kBvJR9phGdH5UqtcWyxVMQoZU57liQeVxZCFbDMSAhdlxBbW1pkPBASaKv/FLlX++Tb4TeO1Tj28dox1yL1t+w==", "dev": true, "requires": { - "@aws-lambda-powertools/commons": "^1.5.0" + "@aws-lambda-powertools/commons": "^1.5.1" } }, "@aws-lambda-powertools/tracer": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/tracer/-/tracer-1.5.0.tgz", - "integrity": "sha512-WXpMqzOsgi8PN+q+cQAWw5Y/Kf/wQ0BlAiIHcn2MSP5lurdOj9pslqUZOYywtjL1FraGOd+2ZEdo+iw6cbAz+w==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/tracer/-/tracer-1.5.1.tgz", + "integrity": "sha512-T/BHOzg+LbY5PF6drKuVhyk4tcAakyn8gI/lGrx8NhZ72vc0YZOCGaMhvUGbImoZ0UFyn5ApP0n70qvk1i1A/Q==", "dev": true, "requires": { - "@aws-lambda-powertools/commons": "^1.5.0", - "aws-xray-sdk-core": "^3.3.6" + "@aws-lambda-powertools/commons": "^1.5.1", + "aws-xray-sdk-core": "^3.4.0" } }, "@aws-sdk/abort-controller": { @@ -12885,12 +12877,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "lodash.pickby": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", - "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==", - "dev": true - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", diff --git a/examples/cdk/package.json b/examples/cdk/package.json index 8d08eba7a9..e0c57ab26b 100644 --- a/examples/cdk/package.json +++ b/examples/cdk/package.json @@ -1,6 +1,6 @@ { "name": "cdk-sample", - "version": "1.5.1", + "version": "1.6.0", "author": { "name": "Amazon Web Services", "url": "https://aws.amazon.com" @@ -24,9 +24,9 @@ "cdk": "cdk" }, "devDependencies": { - "@aws-lambda-powertools/logger": "^1.5.0", - "@aws-lambda-powertools/metrics": "^1.5.0", - "@aws-lambda-powertools/tracer": "^1.5.0", + "@aws-lambda-powertools/logger": "^1.5.1", + "@aws-lambda-powertools/metrics": "^1.5.1", + "@aws-lambda-powertools/tracer": "^1.5.1", "@aws-sdk/lib-dynamodb": "^3.231.0", "@types/aws-lambda": "^8.10.109", "@types/jest": "^29.2.4", diff --git a/examples/sam/CHANGELOG.md b/examples/sam/CHANGELOG.md index 4842af911c..880873e218 100644 --- a/examples/sam/CHANGELOG.md +++ b/examples/sam/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.6.0](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.1...v1.6.0) (2023-03-02) + +**Note:** Version bump only for package sam-example + + + + + ## [1.5.1](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.0...v1.5.1) (2023-01-13) **Note:** Version bump only for package sam-example diff --git a/examples/sam/package-lock.json b/examples/sam/package-lock.json index 5e8c80a513..87988d76c1 100644 --- a/examples/sam/package-lock.json +++ b/examples/sam/package-lock.json @@ -1,17 +1,17 @@ { "name": "sam-example", - "version": "1.5.1", + "version": "1.6.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "sam-example", - "version": "1.5.1", + "version": "1.6.0", "license": "MIT-0", "dependencies": { - "@aws-lambda-powertools/logger": "^1.5.0", - "@aws-lambda-powertools/metrics": "^1.5.0", - "@aws-lambda-powertools/tracer": "^1.5.0", + "@aws-lambda-powertools/logger": "^1.5.1", + "@aws-lambda-powertools/metrics": "^1.5.1", + "@aws-lambda-powertools/tracer": "^1.5.1", "@aws-sdk/client-dynamodb": "^3.231.0", "@aws-sdk/lib-dynamodb": "^3.231.0", "@middy/core": "^3.6.2", @@ -124,35 +124,34 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-lambda-powertools/commons": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/commons/-/commons-1.5.0.tgz", - "integrity": "sha512-UDG1EzINigcBCxoDcwtiqtWR0ZDkZu3w4dZ/mCenUL3v1AUoiUfmpiZI54r0NyXXeyQOIAmvKft7VnQP2BHfJA==" + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/commons/-/commons-1.5.1.tgz", + "integrity": "sha512-qp13/WksB6M/liEHgV3301qmI/aeQKmrYk1iODB+rrkVWeKW4AJqBMuWQ26gIlbD8t82EtuUNPUFle1a2qwjIg==" }, "node_modules/@aws-lambda-powertools/logger": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/logger/-/logger-1.5.0.tgz", - "integrity": "sha512-BlajpUrI4fFMNfv1QBvPG9m7cCdu+5YCo8AsF+/2nM6OwEJDIIqpYvCQEr9TeQZQMMbirp43De2Z7MisV0HDyw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/logger/-/logger-1.5.1.tgz", + "integrity": "sha512-dXvw7c4Z0DUEY62zEWsQ4DQZ3yf+JsEAqojQH+kw+rXVv0cjnIEbM4ZKONPG2jcPS5IMzO6gmCbu/PKB1PYgyA==", "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.0", - "lodash.merge": "^4.6.2", - "lodash.pickby": "^4.6.0" + "@aws-lambda-powertools/commons": "^1.5.1", + "lodash.merge": "^4.6.2" } }, "node_modules/@aws-lambda-powertools/metrics": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/metrics/-/metrics-1.5.0.tgz", - "integrity": "sha512-4x04YpKWz4OI4NSxbi2EhFQYGNjvO3kHUO9gSN/pOpa/bhrwXaTL1gIJsPKcDlKjm1VeQtLbFDutespz4kttpA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/metrics/-/metrics-1.5.1.tgz", + "integrity": "sha512-kBvJR9phGdH5UqtcWyxVMQoZU57liQeVxZCFbDMSAhdlxBbW1pkPBASaKv/FLlX++Tb4TeO1Tj28dox1yL1t+w==", "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.0" + "@aws-lambda-powertools/commons": "^1.5.1" } }, "node_modules/@aws-lambda-powertools/tracer": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/tracer/-/tracer-1.5.0.tgz", - "integrity": "sha512-WXpMqzOsgi8PN+q+cQAWw5Y/Kf/wQ0BlAiIHcn2MSP5lurdOj9pslqUZOYywtjL1FraGOd+2ZEdo+iw6cbAz+w==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/tracer/-/tracer-1.5.1.tgz", + "integrity": "sha512-T/BHOzg+LbY5PF6drKuVhyk4tcAakyn8gI/lGrx8NhZ72vc0YZOCGaMhvUGbImoZ0UFyn5ApP0n70qvk1i1A/Q==", "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.0", - "aws-xray-sdk-core": "^3.3.6" + "@aws-lambda-powertools/commons": "^1.5.1", + "aws-xray-sdk-core": "^3.4.0" } }, "node_modules/@aws-sdk/abort-controller": { @@ -5880,11 +5879,6 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "node_modules/lodash.pickby": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", - "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==" - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -7478,35 +7472,34 @@ } }, "@aws-lambda-powertools/commons": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/commons/-/commons-1.5.0.tgz", - "integrity": "sha512-UDG1EzINigcBCxoDcwtiqtWR0ZDkZu3w4dZ/mCenUL3v1AUoiUfmpiZI54r0NyXXeyQOIAmvKft7VnQP2BHfJA==" + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/commons/-/commons-1.5.1.tgz", + "integrity": "sha512-qp13/WksB6M/liEHgV3301qmI/aeQKmrYk1iODB+rrkVWeKW4AJqBMuWQ26gIlbD8t82EtuUNPUFle1a2qwjIg==" }, "@aws-lambda-powertools/logger": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/logger/-/logger-1.5.0.tgz", - "integrity": "sha512-BlajpUrI4fFMNfv1QBvPG9m7cCdu+5YCo8AsF+/2nM6OwEJDIIqpYvCQEr9TeQZQMMbirp43De2Z7MisV0HDyw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/logger/-/logger-1.5.1.tgz", + "integrity": "sha512-dXvw7c4Z0DUEY62zEWsQ4DQZ3yf+JsEAqojQH+kw+rXVv0cjnIEbM4ZKONPG2jcPS5IMzO6gmCbu/PKB1PYgyA==", "requires": { - "@aws-lambda-powertools/commons": "^1.5.0", - "lodash.merge": "^4.6.2", - "lodash.pickby": "^4.6.0" + "@aws-lambda-powertools/commons": "^1.5.1", + "lodash.merge": "^4.6.2" } }, "@aws-lambda-powertools/metrics": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/metrics/-/metrics-1.5.0.tgz", - "integrity": "sha512-4x04YpKWz4OI4NSxbi2EhFQYGNjvO3kHUO9gSN/pOpa/bhrwXaTL1gIJsPKcDlKjm1VeQtLbFDutespz4kttpA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/metrics/-/metrics-1.5.1.tgz", + "integrity": "sha512-kBvJR9phGdH5UqtcWyxVMQoZU57liQeVxZCFbDMSAhdlxBbW1pkPBASaKv/FLlX++Tb4TeO1Tj28dox1yL1t+w==", "requires": { - "@aws-lambda-powertools/commons": "^1.5.0" + "@aws-lambda-powertools/commons": "^1.5.1" } }, "@aws-lambda-powertools/tracer": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/tracer/-/tracer-1.5.0.tgz", - "integrity": "sha512-WXpMqzOsgi8PN+q+cQAWw5Y/Kf/wQ0BlAiIHcn2MSP5lurdOj9pslqUZOYywtjL1FraGOd+2ZEdo+iw6cbAz+w==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/tracer/-/tracer-1.5.1.tgz", + "integrity": "sha512-T/BHOzg+LbY5PF6drKuVhyk4tcAakyn8gI/lGrx8NhZ72vc0YZOCGaMhvUGbImoZ0UFyn5ApP0n70qvk1i1A/Q==", "requires": { - "@aws-lambda-powertools/commons": "^1.5.0", - "aws-xray-sdk-core": "^3.3.6" + "@aws-lambda-powertools/commons": "^1.5.1", + "aws-xray-sdk-core": "^3.4.0" } }, "@aws-sdk/abort-controller": { @@ -11752,11 +11745,6 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "lodash.pickby": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", - "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==" - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", diff --git a/examples/sam/package.json b/examples/sam/package.json index cfe0386814..dcc566393d 100644 --- a/examples/sam/package.json +++ b/examples/sam/package.json @@ -1,6 +1,6 @@ { "name": "sam-example", - "version": "1.5.1", + "version": "1.6.0", "author": { "name": "Amazon Web Services", "url": "https://aws.amazon.com" @@ -35,9 +35,9 @@ "typescript": "^4.9.4" }, "dependencies": { - "@aws-lambda-powertools/logger": "^1.5.0", - "@aws-lambda-powertools/metrics": "^1.5.0", - "@aws-lambda-powertools/tracer": "^1.5.0", + "@aws-lambda-powertools/logger": "^1.5.1", + "@aws-lambda-powertools/metrics": "^1.5.1", + "@aws-lambda-powertools/tracer": "^1.5.1", "@aws-sdk/client-dynamodb": "^3.231.0", "@aws-sdk/lib-dynamodb": "^3.231.0", "@middy/core": "^3.6.2", diff --git a/lerna.json b/lerna.json index f22b860fc8..7b3656a4c6 100644 --- a/lerna.json +++ b/lerna.json @@ -8,7 +8,7 @@ "examples/sam", "layer-publisher" ], - "version": "1.5.1", + "version": "1.6.0", "npmClient": "npm", "message": "chore(release): %s [skip ci]" } \ No newline at end of file diff --git a/packages/commons/CHANGELOG.md b/packages/commons/CHANGELOG.md index d1f406818d..961dc25a13 100644 --- a/packages/commons/CHANGELOG.md +++ b/packages/commons/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.6.0](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.1...v1.6.0) (2023-03-02) + +**Note:** Version bump only for package @aws-lambda-powertools/commons + + + + + ## [1.5.1](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.0...v1.5.1) (2023-01-13) **Note:** Version bump only for package @aws-lambda-powertools/commons diff --git a/packages/commons/package.json b/packages/commons/package.json index 2bb3651b09..912313b58b 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -1,6 +1,6 @@ { "name": "@aws-lambda-powertools/commons", - "version": "1.5.1", + "version": "1.6.0", "description": "A shared utility package for AWS Lambda Powertools for TypeScript libraries", "author": { "name": "Amazon Web Services", diff --git a/packages/logger/CHANGELOG.md b/packages/logger/CHANGELOG.md index eeb6fd8122..c20096bb3c 100644 --- a/packages/logger/CHANGELOG.md +++ b/packages/logger/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.6.0](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.1...v1.6.0) (2023-03-02) + + +### Bug Fixes + +* **logger:** createChild not passing all parent's attributes ([#1267](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1267)) ([84ab4b9](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/84ab4b911d17d687bdbe60ded31f1e2b6860feb3)) +* **logger:** middleware stores initial persistent attributes correctly ([#1329](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1329)) ([6b32304](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/6b3230489895dc1abdfc6ad56daeeb555fda529f)) + + +### Features + +* **logger:** make loglevel types stricter ([#1313](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/1313)) ([5af51d3](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/5af51d319dee68d7a7ba832721580d7a6e655249)) + + + + + ## [1.5.1](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.0...v1.5.1) (2023-01-13) diff --git a/packages/logger/package.json b/packages/logger/package.json index 19c60437b1..fd35e423d3 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -1,6 +1,6 @@ { "name": "@aws-lambda-powertools/logger", - "version": "1.5.1", + "version": "1.6.0", "description": "The logging package for the AWS Lambda Powertools for TypeScript library", "author": { "name": "Amazon Web Services", @@ -45,7 +45,7 @@ "url": "https://github.com/awslabs/aws-lambda-powertools-typescript/issues" }, "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.1", + "@aws-lambda-powertools/commons": "^1.6.0", "lodash.merge": "^4.6.2" }, "keywords": [ diff --git a/packages/metrics/CHANGELOG.md b/packages/metrics/CHANGELOG.md index 737e65944a..e0f9ef3e8a 100644 --- a/packages/metrics/CHANGELOG.md +++ b/packages/metrics/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.6.0](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.1...v1.6.0) (2023-03-02) + +**Note:** Version bump only for package @aws-lambda-powertools/metrics + + + + + ## [1.5.1](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.0...v1.5.1) (2023-01-13) **Note:** Version bump only for package @aws-lambda-powertools/metrics diff --git a/packages/metrics/package.json b/packages/metrics/package.json index b1d3a367bf..058df1ee42 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -1,6 +1,6 @@ { "name": "@aws-lambda-powertools/metrics", - "version": "1.5.1", + "version": "1.6.0", "description": "The metrics package for the AWS Lambda Powertools for TypeScript library", "author": { "name": "Amazon Web Services", @@ -46,7 +46,7 @@ "url": "https://github.com/awslabs/aws-lambda-powertools-typescript/issues" }, "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.1" + "@aws-lambda-powertools/commons": "^1.6.0" }, "keywords": [ "aws", diff --git a/packages/tracer/CHANGELOG.md b/packages/tracer/CHANGELOG.md index a25bef0254..e4b7505622 100644 --- a/packages/tracer/CHANGELOG.md +++ b/packages/tracer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.6.0](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.1...v1.6.0) (2023-03-02) + +**Note:** Version bump only for package @aws-lambda-powertools/tracer + + + + + ## [1.5.1](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v1.5.0...v1.5.1) (2023-01-13) **Note:** Version bump only for package @aws-lambda-powertools/tracer diff --git a/packages/tracer/package.json b/packages/tracer/package.json index 2226877822..21d891bff8 100644 --- a/packages/tracer/package.json +++ b/packages/tracer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-lambda-powertools/tracer", - "version": "1.5.1", + "version": "1.6.0", "description": "The tracer package for the AWS Lambda Powertools for TypeScript library", "author": { "name": "Amazon Web Services", @@ -48,7 +48,7 @@ "url": "https://github.com/awslabs/aws-lambda-powertools-typescript/issues" }, "dependencies": { - "@aws-lambda-powertools/commons": "^1.5.1", + "@aws-lambda-powertools/commons": "^1.6.0", "aws-xray-sdk-core": "^3.4.0" }, "keywords": [